@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.
- package/CHANGELOG.md +72 -0
- package/README.en.md +181 -14
- package/README.md +201 -16
- package/docs/channels/wecom.md +137 -1
- package/openclaw.plugin.json +688 -6
- package/package.json +204 -4
- package/scripts/wecom-agent-selfcheck.mjs +775 -0
- package/scripts/wecom-bot-longconn-probe.mjs +582 -0
- package/scripts/wecom-bot-selfcheck.mjs +952 -0
- package/scripts/wecom-callback-matrix.mjs +224 -0
- package/scripts/wecom-doctor.mjs +1407 -0
- package/scripts/wecom-e2e-scenario.mjs +333 -0
- package/scripts/wecom-migrate.mjs +261 -0
- package/scripts/wecom-quickstart.mjs +1824 -0
- package/scripts/wecom-release-check.mjs +232 -0
- package/scripts/wecom-remote-e2e.mjs +310 -0
- package/scripts/wecom-selfcheck.mjs +1255 -0
- package/scripts/wecom-smoke.sh +74 -0
- package/src/core/delivery-router.js +21 -0
- package/src/core.js +619 -30
- package/src/wecom/account-config-core.js +27 -1
- package/src/wecom/account-config.js +19 -2
- package/src/wecom/agent-dispatch-executor.js +11 -0
- package/src/wecom/agent-dispatch-handlers.js +61 -8
- package/src/wecom/agent-inbound-guards.js +24 -0
- package/src/wecom/agent-inbound-processor.js +34 -2
- package/src/wecom/agent-late-reply-runtime.js +30 -2
- package/src/wecom/agent-text-sender.js +2 -0
- package/src/wecom/api-client-core.js +27 -19
- package/src/wecom/api-client-media.js +16 -7
- package/src/wecom/api-client-send-text.js +4 -0
- package/src/wecom/api-client-send-typed.js +4 -1
- package/src/wecom/api-client-senders.js +41 -3
- package/src/wecom/api-client.js +1 -0
- package/src/wecom/bot-dispatch-fallback.js +18 -3
- package/src/wecom/bot-dispatch-handlers.js +47 -10
- package/src/wecom/bot-inbound-dispatch-runtime.js +3 -0
- package/src/wecom/bot-inbound-executor-helpers.js +11 -1
- package/src/wecom/bot-inbound-executor.js +24 -0
- package/src/wecom/bot-inbound-guards.js +31 -1
- package/src/wecom/channel-config-schema.js +132 -0
- package/src/wecom/channel-plugin.js +348 -7
- package/src/wecom/command-handlers.js +102 -11
- package/src/wecom/command-status-text.js +206 -0
- package/src/wecom/doc-client.js +7 -1
- package/src/wecom/inbound-content-handler-file-video-link.js +4 -0
- package/src/wecom/inbound-content-handler-image-voice.js +6 -0
- package/src/wecom/inbound-content.js +5 -0
- package/src/wecom/installer-api.js +910 -0
- package/src/wecom/media-download.js +2 -2
- package/src/wecom/migration-diagnostics.js +816 -0
- package/src/wecom/network-config.js +91 -0
- package/src/wecom/observability-metrics.js +9 -3
- package/src/wecom/outbound-agent-delivery.js +313 -0
- package/src/wecom/outbound-agent-media-sender.js +37 -7
- package/src/wecom/outbound-agent-push.js +1 -0
- package/src/wecom/outbound-delivery.js +129 -12
- package/src/wecom/outbound-stream-msg-item.js +25 -2
- package/src/wecom/outbound-webhook-delivery.js +19 -0
- package/src/wecom/outbound-webhook-media.js +30 -6
- package/src/wecom/pending-reply-manager.js +143 -0
- package/src/wecom/plugin-account-policy-services.js +26 -0
- package/src/wecom/plugin-base-services.js +58 -0
- package/src/wecom/plugin-constants.js +1 -1
- package/src/wecom/plugin-delivery-inbound-services.js +25 -0
- package/src/wecom/plugin-processing-deps.js +7 -0
- package/src/wecom/plugin-route-runtime-deps.js +1 -0
- package/src/wecom/plugin-services.js +87 -0
- package/src/wecom/policy-resolvers.js +93 -20
- package/src/wecom/quickstart-metadata.js +1247 -0
- package/src/wecom/reasoning-visibility.js +104 -0
- package/src/wecom/register-runtime.js +10 -0
- package/src/wecom/reliable-delivery-persistence.js +138 -0
- package/src/wecom/reliable-delivery.js +642 -0
- package/src/wecom/reply-output-policy.js +171 -0
- package/src/wecom/text-inbound-scheduler.js +6 -1
- package/src/wecom/workspace-auto-sender.js +2 -0
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { buildWecomApiUrl } from "./network-config.js";
|
|
2
|
+
|
|
1
3
|
export function createWecomApiMediaClient({
|
|
2
4
|
fetchWithRetry,
|
|
3
5
|
getWecomAccessToken,
|
|
@@ -9,9 +11,12 @@ export function createWecomApiMediaClient({
|
|
|
9
11
|
throw new Error("createWecomApiMediaClient: getWecomAccessToken is required");
|
|
10
12
|
}
|
|
11
13
|
|
|
12
|
-
async function uploadWecomMedia({ corpId, corpSecret, type, buffer, filename, logger, proxyUrl }) {
|
|
13
|
-
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger });
|
|
14
|
-
const uploadUrl =
|
|
14
|
+
async function uploadWecomMedia({ corpId, corpSecret, type, buffer, filename, logger, proxyUrl, apiBaseUrl }) {
|
|
15
|
+
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger, apiBaseUrl });
|
|
16
|
+
const uploadUrl = buildWecomApiUrl(
|
|
17
|
+
`/cgi-bin/media/upload?access_token=${encodeURIComponent(accessToken)}&type=${encodeURIComponent(type)}`,
|
|
18
|
+
{ apiBaseUrl },
|
|
19
|
+
);
|
|
15
20
|
|
|
16
21
|
const boundary = `----WecomMediaUpload${Date.now()}`;
|
|
17
22
|
const header = Buffer.from(
|
|
@@ -30,6 +35,7 @@ export function createWecomApiMediaClient({
|
|
|
30
35
|
"Content-Type": `multipart/form-data; boundary=${boundary}`,
|
|
31
36
|
},
|
|
32
37
|
body,
|
|
38
|
+
apiBaseUrl,
|
|
33
39
|
},
|
|
34
40
|
3,
|
|
35
41
|
1000,
|
|
@@ -43,11 +49,14 @@ export function createWecomApiMediaClient({
|
|
|
43
49
|
return json.media_id;
|
|
44
50
|
}
|
|
45
51
|
|
|
46
|
-
async function downloadWecomMedia({ corpId, corpSecret, mediaId, proxyUrl, logger }) {
|
|
47
|
-
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger });
|
|
48
|
-
const mediaUrl =
|
|
52
|
+
async function downloadWecomMedia({ corpId, corpSecret, mediaId, proxyUrl, logger, apiBaseUrl }) {
|
|
53
|
+
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger, apiBaseUrl });
|
|
54
|
+
const mediaUrl = buildWecomApiUrl(
|
|
55
|
+
`/cgi-bin/media/get?access_token=${encodeURIComponent(accessToken)}&media_id=${encodeURIComponent(mediaId)}`,
|
|
56
|
+
{ apiBaseUrl },
|
|
57
|
+
);
|
|
49
58
|
|
|
50
|
-
const res = await fetchWithRetry(mediaUrl, {}, 3, 1000, { proxyUrl, logger });
|
|
59
|
+
const res = await fetchWithRetry(mediaUrl, { apiBaseUrl }, 3, 1000, { proxyUrl, logger });
|
|
51
60
|
if (!res.ok) {
|
|
52
61
|
throw new Error(`Failed to download media: ${res.status}`);
|
|
53
62
|
}
|
|
@@ -42,6 +42,7 @@ export function createWecomTextSender({
|
|
|
42
42
|
text,
|
|
43
43
|
logger,
|
|
44
44
|
proxyUrl,
|
|
45
|
+
apiBaseUrl,
|
|
45
46
|
}) {
|
|
46
47
|
const sendJson = await sendWecomTypedMessage({
|
|
47
48
|
corpId,
|
|
@@ -57,6 +58,7 @@ export function createWecomTextSender({
|
|
|
57
58
|
},
|
|
58
59
|
logger,
|
|
59
60
|
proxyUrl,
|
|
61
|
+
apiBaseUrl,
|
|
60
62
|
errorPrefix: "",
|
|
61
63
|
});
|
|
62
64
|
const isAppChat = Boolean(chatId);
|
|
@@ -76,6 +78,7 @@ export function createWecomTextSender({
|
|
|
76
78
|
text,
|
|
77
79
|
logger,
|
|
78
80
|
proxyUrl,
|
|
81
|
+
apiBaseUrl,
|
|
79
82
|
}) {
|
|
80
83
|
const targetKey = buildTargetKey({ corpId, agentId, toUser, toParty, toTag, chatId });
|
|
81
84
|
return enqueueTargetSend(targetKey, async () => {
|
|
@@ -96,6 +99,7 @@ export function createWecomTextSender({
|
|
|
96
99
|
text: chunks[i],
|
|
97
100
|
logger,
|
|
98
101
|
proxyUrl,
|
|
102
|
+
apiBaseUrl,
|
|
99
103
|
});
|
|
100
104
|
if (i < chunks.length - 1) {
|
|
101
105
|
// eslint-disable-next-line no-await-in-loop
|
|
@@ -29,10 +29,11 @@ export function createWecomTypedMessageSender({
|
|
|
29
29
|
payload,
|
|
30
30
|
logger,
|
|
31
31
|
proxyUrl,
|
|
32
|
+
apiBaseUrl,
|
|
32
33
|
errorPrefix,
|
|
33
34
|
}) {
|
|
34
35
|
return apiLimiter.execute(async () => {
|
|
35
|
-
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger });
|
|
36
|
+
const accessToken = await getWecomAccessToken({ corpId, corpSecret, proxyUrl, logger, apiBaseUrl });
|
|
36
37
|
const { sendUrl, body, isAppChat } = buildWecomMessageSendRequest({
|
|
37
38
|
accessToken,
|
|
38
39
|
agentId,
|
|
@@ -42,6 +43,7 @@ export function createWecomTypedMessageSender({
|
|
|
42
43
|
chatId,
|
|
43
44
|
msgType,
|
|
44
45
|
payload,
|
|
46
|
+
apiBaseUrl,
|
|
45
47
|
});
|
|
46
48
|
const sendRes = await fetchWithRetry(
|
|
47
49
|
sendUrl,
|
|
@@ -49,6 +51,7 @@ export function createWecomTypedMessageSender({
|
|
|
49
51
|
method: "POST",
|
|
50
52
|
headers: { "Content-Type": "application/json" },
|
|
51
53
|
body: JSON.stringify(body),
|
|
54
|
+
apiBaseUrl,
|
|
52
55
|
},
|
|
53
56
|
3,
|
|
54
57
|
1000,
|
|
@@ -24,7 +24,39 @@ export function createWecomApiSenders({
|
|
|
24
24
|
sendWecomTypedMessage,
|
|
25
25
|
});
|
|
26
26
|
|
|
27
|
-
async function
|
|
27
|
+
async function sendWecomMarkdown({
|
|
28
|
+
corpId,
|
|
29
|
+
corpSecret,
|
|
30
|
+
agentId,
|
|
31
|
+
toUser,
|
|
32
|
+
toParty,
|
|
33
|
+
toTag,
|
|
34
|
+
chatId,
|
|
35
|
+
content,
|
|
36
|
+
logger,
|
|
37
|
+
proxyUrl,
|
|
38
|
+
apiBaseUrl,
|
|
39
|
+
}) {
|
|
40
|
+
return sendWecomTypedMessage({
|
|
41
|
+
corpId,
|
|
42
|
+
corpSecret,
|
|
43
|
+
agentId,
|
|
44
|
+
toUser,
|
|
45
|
+
toParty,
|
|
46
|
+
toTag,
|
|
47
|
+
chatId,
|
|
48
|
+
msgType: "markdown",
|
|
49
|
+
payload: {
|
|
50
|
+
markdown: { content: String(content ?? "") },
|
|
51
|
+
},
|
|
52
|
+
logger,
|
|
53
|
+
proxyUrl,
|
|
54
|
+
apiBaseUrl,
|
|
55
|
+
errorPrefix: "WeCom markdown send failed",
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async function sendWecomImage({ corpId, corpSecret, agentId, toUser, toParty, toTag, chatId, mediaId, logger, proxyUrl, apiBaseUrl }) {
|
|
28
60
|
return sendWecomTypedMessage({
|
|
29
61
|
corpId,
|
|
30
62
|
corpSecret,
|
|
@@ -39,6 +71,7 @@ export function createWecomApiSenders({
|
|
|
39
71
|
},
|
|
40
72
|
logger,
|
|
41
73
|
proxyUrl,
|
|
74
|
+
apiBaseUrl,
|
|
42
75
|
errorPrefix: "WeCom image send failed",
|
|
43
76
|
});
|
|
44
77
|
}
|
|
@@ -56,6 +89,7 @@ export function createWecomApiSenders({
|
|
|
56
89
|
description,
|
|
57
90
|
logger,
|
|
58
91
|
proxyUrl,
|
|
92
|
+
apiBaseUrl,
|
|
59
93
|
}) {
|
|
60
94
|
const videoPayload = {
|
|
61
95
|
media_id: mediaId,
|
|
@@ -76,11 +110,12 @@ export function createWecomApiSenders({
|
|
|
76
110
|
},
|
|
77
111
|
logger,
|
|
78
112
|
proxyUrl,
|
|
113
|
+
apiBaseUrl,
|
|
79
114
|
errorPrefix: "WeCom video send failed",
|
|
80
115
|
});
|
|
81
116
|
}
|
|
82
117
|
|
|
83
|
-
async function sendWecomFile({ corpId, corpSecret, agentId, toUser, toParty, toTag, chatId, mediaId, logger, proxyUrl }) {
|
|
118
|
+
async function sendWecomFile({ corpId, corpSecret, agentId, toUser, toParty, toTag, chatId, mediaId, logger, proxyUrl, apiBaseUrl }) {
|
|
84
119
|
return sendWecomTypedMessage({
|
|
85
120
|
corpId,
|
|
86
121
|
corpSecret,
|
|
@@ -95,11 +130,12 @@ export function createWecomApiSenders({
|
|
|
95
130
|
},
|
|
96
131
|
logger,
|
|
97
132
|
proxyUrl,
|
|
133
|
+
apiBaseUrl,
|
|
98
134
|
errorPrefix: "WeCom file send failed",
|
|
99
135
|
});
|
|
100
136
|
}
|
|
101
137
|
|
|
102
|
-
async function sendWecomVoice({ corpId, corpSecret, agentId, toUser, toParty, toTag, chatId, mediaId, logger, proxyUrl }) {
|
|
138
|
+
async function sendWecomVoice({ corpId, corpSecret, agentId, toUser, toParty, toTag, chatId, mediaId, logger, proxyUrl, apiBaseUrl }) {
|
|
103
139
|
return sendWecomTypedMessage({
|
|
104
140
|
corpId,
|
|
105
141
|
corpSecret,
|
|
@@ -114,12 +150,14 @@ export function createWecomApiSenders({
|
|
|
114
150
|
},
|
|
115
151
|
logger,
|
|
116
152
|
proxyUrl,
|
|
153
|
+
apiBaseUrl,
|
|
117
154
|
errorPrefix: "WeCom voice send failed",
|
|
118
155
|
});
|
|
119
156
|
}
|
|
120
157
|
|
|
121
158
|
return {
|
|
122
159
|
sendWecomText,
|
|
160
|
+
sendWecomMarkdown,
|
|
123
161
|
sendWecomImage,
|
|
124
162
|
sendWecomVideo,
|
|
125
163
|
sendWecomFile,
|
package/src/wecom/api-client.js
CHANGED
|
@@ -46,6 +46,7 @@ export function createWecomApiClient({
|
|
|
46
46
|
getWecomAccessToken: core.getWecomAccessToken,
|
|
47
47
|
buildWecomMessageSendRequest: core.buildWecomMessageSendRequest,
|
|
48
48
|
sendWecomText: senders.sendWecomText,
|
|
49
|
+
sendWecomMarkdown: senders.sendWecomMarkdown,
|
|
49
50
|
uploadWecomMedia: media.uploadWecomMedia,
|
|
50
51
|
sendWecomImage: senders.sendWecomImage,
|
|
51
52
|
sendWecomVideo: senders.sendWecomVideo,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { parseThinkingContent } from "./thinking-parser.js";
|
|
2
|
+
import { applyWecomReasoningPolicy } from "./reasoning-visibility.js";
|
|
2
3
|
|
|
3
4
|
function assertFunction(name, value) {
|
|
4
5
|
if (typeof value !== "function") {
|
|
@@ -6,13 +7,24 @@ function assertFunction(name, value) {
|
|
|
6
7
|
}
|
|
7
8
|
}
|
|
8
9
|
|
|
9
|
-
export function buildWecomBotVisibleFallbackPayload(
|
|
10
|
+
export function buildWecomBotVisibleFallbackPayload(
|
|
11
|
+
rawText = "",
|
|
12
|
+
markdownToWecomText = (text) => String(text ?? ""),
|
|
13
|
+
reasoningPolicy = {},
|
|
14
|
+
) {
|
|
10
15
|
const parsed = parseThinkingContent(rawText);
|
|
11
16
|
const visibleContent = markdownToWecomText(parsed.visibleContent).trim();
|
|
12
17
|
const thinkingContent = markdownToWecomText(parsed.thinkingContent).trim();
|
|
13
|
-
|
|
18
|
+
const resolved = applyWecomReasoningPolicy({
|
|
14
19
|
text: visibleContent,
|
|
15
20
|
thinkingContent,
|
|
21
|
+
policy: reasoningPolicy,
|
|
22
|
+
transport: "bot",
|
|
23
|
+
phase: "final",
|
|
24
|
+
});
|
|
25
|
+
return {
|
|
26
|
+
text: resolved.text,
|
|
27
|
+
thinkingContent: resolved.thinkingContent,
|
|
16
28
|
};
|
|
17
29
|
}
|
|
18
30
|
|
|
@@ -25,6 +37,7 @@ export async function handleWecomBotPostDispatchFallback({
|
|
|
25
37
|
markdownToWecomText,
|
|
26
38
|
safeDeliverReply,
|
|
27
39
|
startLateReplyWatcher,
|
|
40
|
+
reasoningPolicy = {},
|
|
28
41
|
} = {}) {
|
|
29
42
|
if (!dispatchState || typeof dispatchState !== "object") {
|
|
30
43
|
throw new Error("handleWecomBotPostDispatchFallback: dispatchState is required");
|
|
@@ -41,6 +54,7 @@ export async function handleWecomBotPostDispatchFallback({
|
|
|
41
54
|
const { text: fallback, thinkingContent } = buildWecomBotVisibleFallbackPayload(
|
|
42
55
|
dispatchState.blockText,
|
|
43
56
|
markdownToWecomText,
|
|
57
|
+
reasoningPolicy,
|
|
44
58
|
);
|
|
45
59
|
if (fallback || thinkingContent) {
|
|
46
60
|
await safeDeliverReply(
|
|
@@ -81,6 +95,7 @@ export async function handleWecomBotDispatchError({
|
|
|
81
95
|
readTranscriptFallbackResult,
|
|
82
96
|
safeDeliverReply,
|
|
83
97
|
markTranscriptReplyDelivered,
|
|
98
|
+
reasoningPolicy = {},
|
|
84
99
|
} = {}) {
|
|
85
100
|
assertFunction("isDispatchTimeoutError", isDispatchTimeoutError);
|
|
86
101
|
assertFunction("startLateReplyWatcher", startLateReplyWatcher);
|
|
@@ -92,7 +107,7 @@ export async function handleWecomBotDispatchError({
|
|
|
92
107
|
|
|
93
108
|
api?.logger?.warn?.(`wecom(bot): processing failed: ${String(err?.message || err)}`);
|
|
94
109
|
if (dispatchState && typeof dispatchState === "object" && dispatchState.streamFinished !== true) {
|
|
95
|
-
const partialPayload = buildWecomBotVisibleFallbackPayload(dispatchState.blockText, markdownToWecomText);
|
|
110
|
+
const partialPayload = buildWecomBotVisibleFallbackPayload(dispatchState.blockText, markdownToWecomText, reasoningPolicy);
|
|
96
111
|
if (partialPayload.text || partialPayload.thinkingContent) {
|
|
97
112
|
const delivered = await safeDeliverReply(partialPayload, "timeout-partial-fallback");
|
|
98
113
|
if (delivered) return true;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { parseThinkingContent } from "./thinking-parser.js";
|
|
2
|
+
import { applyWecomReasoningPolicy } from "./reasoning-visibility.js";
|
|
3
|
+
import { extractWecomReplyDirectives } from "./reply-output-policy.js";
|
|
2
4
|
|
|
3
5
|
function assertFunction(name, value) {
|
|
4
6
|
if (typeof value !== "function") {
|
|
@@ -30,6 +32,7 @@ export function createWecomBotDispatchHandlers({
|
|
|
30
32
|
markdownToWecomText,
|
|
31
33
|
isAgentFailureText,
|
|
32
34
|
safeDeliverReply,
|
|
35
|
+
reasoningPolicy = {},
|
|
33
36
|
} = {}) {
|
|
34
37
|
if (!state || typeof state !== "object") {
|
|
35
38
|
throw new Error("createWecomBotDispatchHandlers: state is required");
|
|
@@ -46,14 +49,46 @@ export function createWecomBotDispatchHandlers({
|
|
|
46
49
|
|
|
47
50
|
const logger = api?.logger;
|
|
48
51
|
|
|
49
|
-
const buildThinkingState = (rawText) => {
|
|
52
|
+
const buildThinkingState = (rawText, rawThinkingOverride = "") => {
|
|
50
53
|
const parsed = parseThinkingContent(rawText);
|
|
51
54
|
const visibleRaw = String(parsed.visibleContent ?? "").trim();
|
|
52
55
|
const thinkingRaw = String(parsed.thinkingContent ?? "").trim();
|
|
56
|
+
const visibleText = visibleRaw ? markdownToWecomText(visibleRaw).trim() : "";
|
|
57
|
+
const thinkingContent = (thinkingRaw ? markdownToWecomText(thinkingRaw).trim() : "") || String(rawThinkingOverride ?? "").trim();
|
|
58
|
+
const rawThinkingContent = String(rawThinkingOverride ?? "").trim() || thinkingRaw;
|
|
59
|
+
const cleanedStreamPayload = applyWecomReasoningPolicy({
|
|
60
|
+
text: visibleText,
|
|
61
|
+
thinkingContent,
|
|
62
|
+
policy: reasoningPolicy,
|
|
63
|
+
transport: "bot",
|
|
64
|
+
phase: "stream",
|
|
65
|
+
});
|
|
66
|
+
const cleanedFinalPayload = applyWecomReasoningPolicy({
|
|
67
|
+
text: visibleText,
|
|
68
|
+
thinkingContent,
|
|
69
|
+
policy: reasoningPolicy,
|
|
70
|
+
transport: "bot",
|
|
71
|
+
phase: "final",
|
|
72
|
+
});
|
|
73
|
+
const rawFinalPayload = applyWecomReasoningPolicy({
|
|
74
|
+
text: visibleRaw,
|
|
75
|
+
thinkingContent: rawThinkingContent,
|
|
76
|
+
policy: reasoningPolicy,
|
|
77
|
+
transport: "bot",
|
|
78
|
+
phase: "final",
|
|
79
|
+
});
|
|
53
80
|
return {
|
|
54
81
|
rawText: String(rawText ?? ""),
|
|
55
|
-
|
|
56
|
-
|
|
82
|
+
streamPayload: {
|
|
83
|
+
...cleanedStreamPayload,
|
|
84
|
+
text: extractWecomReplyDirectives(cleanedStreamPayload.text).text,
|
|
85
|
+
},
|
|
86
|
+
finalPayload: {
|
|
87
|
+
...cleanedFinalPayload,
|
|
88
|
+
text: extractWecomReplyDirectives(cleanedFinalPayload.text).text,
|
|
89
|
+
rawText: String(rawFinalPayload.text ?? "").trim(),
|
|
90
|
+
rawThinkingContent: String(rawFinalPayload.thinkingContent ?? "").trim(),
|
|
91
|
+
},
|
|
57
92
|
};
|
|
58
93
|
};
|
|
59
94
|
|
|
@@ -74,10 +109,10 @@ export function createWecomBotDispatchHandlers({
|
|
|
74
109
|
if (!payload?.text) return;
|
|
75
110
|
state.blockText = normalizeWecomBotBlockText(state.blockText, payload.text);
|
|
76
111
|
const blockState = buildThinkingState(state.blockText);
|
|
77
|
-
updateBotStream(streamId, blockState.
|
|
112
|
+
updateBotStream(streamId, blockState.streamPayload.text, {
|
|
78
113
|
append: false,
|
|
79
114
|
finished: false,
|
|
80
|
-
thinkingContent: blockState.thinkingContent,
|
|
115
|
+
thinkingContent: blockState.streamPayload.thinkingContent,
|
|
81
116
|
});
|
|
82
117
|
if (typeof pushWecomBotLongConnectionStreamUpdate === "function") {
|
|
83
118
|
try {
|
|
@@ -85,9 +120,9 @@ export function createWecomBotDispatchHandlers({
|
|
|
85
120
|
accountId,
|
|
86
121
|
sessionId,
|
|
87
122
|
streamId,
|
|
88
|
-
content: blockState.
|
|
123
|
+
content: blockState.streamPayload.text,
|
|
89
124
|
finish: false,
|
|
90
|
-
thinkingContent: blockState.thinkingContent,
|
|
125
|
+
thinkingContent: blockState.streamPayload.thinkingContent,
|
|
91
126
|
});
|
|
92
127
|
} catch (err) {
|
|
93
128
|
logger?.warn?.(
|
|
@@ -104,11 +139,13 @@ export function createWecomBotDispatchHandlers({
|
|
|
104
139
|
return;
|
|
105
140
|
}
|
|
106
141
|
const finalState = buildThinkingState(payload.text);
|
|
107
|
-
if (finalState.
|
|
142
|
+
if (finalState.finalPayload.text || finalState.finalPayload.thinkingContent) {
|
|
108
143
|
state.streamFinished = await safeDeliverReply(
|
|
109
144
|
{
|
|
110
|
-
text: finalState.
|
|
111
|
-
thinkingContent: finalState.thinkingContent,
|
|
145
|
+
text: finalState.finalPayload.text,
|
|
146
|
+
thinkingContent: finalState.finalPayload.thinkingContent,
|
|
147
|
+
rawText: finalState.finalPayload.rawText,
|
|
148
|
+
rawThinkingContent: finalState.finalPayload.rawThinkingContent,
|
|
112
149
|
},
|
|
113
150
|
"final",
|
|
114
151
|
);
|
|
@@ -35,6 +35,7 @@ export async function executeWecomBotDispatchRuntime({
|
|
|
35
35
|
markdownToWecomText,
|
|
36
36
|
isAgentFailureText,
|
|
37
37
|
safeDeliverReply,
|
|
38
|
+
resolveWecomReasoningPolicy,
|
|
38
39
|
markTranscriptReplyDelivered,
|
|
39
40
|
ACTIVE_LATE_REPLY_WATCHERS,
|
|
40
41
|
ensureTranscriptFallbackReader,
|
|
@@ -94,6 +95,7 @@ export async function executeWecomBotDispatchRuntime({
|
|
|
94
95
|
markdownToWecomText,
|
|
95
96
|
isAgentFailureText,
|
|
96
97
|
safeDeliverReply,
|
|
98
|
+
reasoningPolicy: resolveWecomReasoningPolicy?.(api) ?? {},
|
|
97
99
|
});
|
|
98
100
|
|
|
99
101
|
await withTimeout(
|
|
@@ -129,6 +131,7 @@ export async function executeWecomBotDispatchRuntime({
|
|
|
129
131
|
markdownToWecomText,
|
|
130
132
|
safeDeliverReply,
|
|
131
133
|
startLateReplyWatcher,
|
|
134
|
+
reasoningPolicy: resolveWecomReasoningPolicy?.(api) ?? {},
|
|
132
135
|
});
|
|
133
136
|
|
|
134
137
|
return {
|
|
@@ -90,6 +90,7 @@ export function assertWecomBotInboundFlowDeps({ api, ...deps } = {}) {
|
|
|
90
90
|
"resolveWecomCommandPolicy",
|
|
91
91
|
"resolveWecomAllowFromPolicy",
|
|
92
92
|
"resolveWecomDmPolicy",
|
|
93
|
+
"resolveWecomReasoningPolicy",
|
|
93
94
|
"isWecomSenderAllowed",
|
|
94
95
|
"extractLeadingSlashCommand",
|
|
95
96
|
"buildWecomBotHelpText",
|
|
@@ -117,6 +118,7 @@ export function createWecomBotInboundFlowState({
|
|
|
117
118
|
api,
|
|
118
119
|
accountId = "default",
|
|
119
120
|
fromUser,
|
|
121
|
+
chatId = "",
|
|
120
122
|
content,
|
|
121
123
|
imageEntries,
|
|
122
124
|
imageUrls,
|
|
@@ -172,7 +174,10 @@ export function createWecomBotInboundFlowState({
|
|
|
172
174
|
content: String(quote.content ?? "").trim(),
|
|
173
175
|
}
|
|
174
176
|
: null,
|
|
175
|
-
groupChatPolicy: normalizeWecomBotGroupChatPolicy(
|
|
177
|
+
groupChatPolicy: normalizeWecomBotGroupChatPolicy(
|
|
178
|
+
resolveWecomGroupChatPolicy(api, normalizedAccountId, {}, chatId),
|
|
179
|
+
api?.logger,
|
|
180
|
+
),
|
|
176
181
|
dynamicAgentPolicy: resolveWecomDynamicAgentPolicy(api),
|
|
177
182
|
isAdminUser: false,
|
|
178
183
|
};
|
|
@@ -203,7 +208,9 @@ export function createWecomBotSafeReplyHelpers({
|
|
|
203
208
|
? reply
|
|
204
209
|
: { text: "" };
|
|
205
210
|
const contentText = String(normalizedReply.text ?? "").trim();
|
|
211
|
+
const rawContentText = String(normalizedReply.rawText ?? contentText).trim();
|
|
206
212
|
const thinkingContent = String(normalizedReply.thinkingContent ?? "").trim();
|
|
213
|
+
const rawThinkingContent = String(normalizedReply.rawThinkingContent ?? thinkingContent).trim();
|
|
207
214
|
const replyMediaUrls = normalizeWecomBotOutboundMediaUrls(normalizedReply);
|
|
208
215
|
if (!contentText && replyMediaUrls.length === 0 && !thinkingContent) return false;
|
|
209
216
|
const result = await deliverBotReplyText({
|
|
@@ -214,7 +221,10 @@ export function createWecomBotSafeReplyHelpers({
|
|
|
214
221
|
streamId,
|
|
215
222
|
responseUrl,
|
|
216
223
|
text: contentText,
|
|
224
|
+
rawText: rawContentText,
|
|
217
225
|
thinkingContent,
|
|
226
|
+
rawThinkingContent,
|
|
227
|
+
routeAgentId: state.routedAgentId,
|
|
218
228
|
mediaUrls: replyMediaUrls,
|
|
219
229
|
mediaType: String(normalizedReply.mediaType ?? "").trim().toLowerCase() || undefined,
|
|
220
230
|
reason,
|
|
@@ -45,6 +45,7 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
45
45
|
resolveWecomCommandPolicy,
|
|
46
46
|
resolveWecomAllowFromPolicy,
|
|
47
47
|
resolveWecomDmPolicy,
|
|
48
|
+
resolveWecomReasoningPolicy,
|
|
48
49
|
isWecomSenderAllowed,
|
|
49
50
|
extractLeadingSlashCommand,
|
|
50
51
|
buildWecomBotHelpText,
|
|
@@ -66,6 +67,8 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
66
67
|
ensureTranscriptFallbackReader,
|
|
67
68
|
resetWecomConversationSession,
|
|
68
69
|
clearSessionStoreEntry,
|
|
70
|
+
markWecomReliableInboundActivity = () => null,
|
|
71
|
+
flushWecomSessionPendingReplies = async () => {},
|
|
69
72
|
} = payload;
|
|
70
73
|
|
|
71
74
|
assertWecomBotInboundFlowDeps({
|
|
@@ -77,6 +80,7 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
77
80
|
api,
|
|
78
81
|
accountId,
|
|
79
82
|
fromUser,
|
|
83
|
+
chatId,
|
|
80
84
|
content,
|
|
81
85
|
imageEntries,
|
|
82
86
|
imageUrls,
|
|
@@ -93,6 +97,12 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
93
97
|
resolveWecomGroupChatPolicy,
|
|
94
98
|
resolveWecomDynamicAgentPolicy,
|
|
95
99
|
});
|
|
100
|
+
markWecomReliableInboundActivity({
|
|
101
|
+
mode: "bot",
|
|
102
|
+
accountId: state.accountId,
|
|
103
|
+
sessionId: state.baseSessionId,
|
|
104
|
+
fromUser,
|
|
105
|
+
});
|
|
96
106
|
const { runtime, cfg } = state;
|
|
97
107
|
const { safeFinishStream, safeDeliverReply } = createWecomBotSafeReplyHelpers({
|
|
98
108
|
api,
|
|
@@ -129,10 +139,12 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
129
139
|
api,
|
|
130
140
|
accountId: state.accountId,
|
|
131
141
|
fromUser,
|
|
142
|
+
chatId,
|
|
132
143
|
isGroupChat,
|
|
133
144
|
msgType,
|
|
134
145
|
commandBody: state.commandBody,
|
|
135
146
|
normalizedFromUser: state.normalizedFromUser,
|
|
147
|
+
groupChatPolicy: state.groupChatPolicy,
|
|
136
148
|
resolveWecomCommandPolicy,
|
|
137
149
|
resolveWecomAllowFromPolicy,
|
|
138
150
|
resolveWecomDmPolicy,
|
|
@@ -231,6 +243,17 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
231
243
|
const storePath = runtimeContext.storePath;
|
|
232
244
|
const ctxPayload = runtimeContext.ctxPayload;
|
|
233
245
|
const sessionRuntimeId = runtimeContext.sessionRuntimeId;
|
|
246
|
+
markWecomReliableInboundActivity({
|
|
247
|
+
mode: "bot",
|
|
248
|
+
accountId: state.accountId,
|
|
249
|
+
sessionId: state.sessionId,
|
|
250
|
+
fromUser,
|
|
251
|
+
});
|
|
252
|
+
await flushWecomSessionPendingReplies({
|
|
253
|
+
mode: "bot",
|
|
254
|
+
accountId: state.accountId,
|
|
255
|
+
sessionId: state.sessionId,
|
|
256
|
+
});
|
|
234
257
|
|
|
235
258
|
const dispatchResult = await executeWecomBotDispatchRuntime({
|
|
236
259
|
api,
|
|
@@ -255,6 +278,7 @@ export async function executeWecomBotInboundFlow(payload = {}) {
|
|
|
255
278
|
markdownToWecomText,
|
|
256
279
|
isAgentFailureText,
|
|
257
280
|
safeDeliverReply,
|
|
281
|
+
resolveWecomReasoningPolicy,
|
|
258
282
|
markTranscriptReplyDelivered,
|
|
259
283
|
ACTIVE_LATE_REPLY_WATCHERS,
|
|
260
284
|
ensureTranscriptFallbackReader,
|
|
@@ -59,10 +59,12 @@ export async function applyWecomBotCommandAndSenderGuard({
|
|
|
59
59
|
api,
|
|
60
60
|
accountId = "default",
|
|
61
61
|
fromUser,
|
|
62
|
+
chatId = "",
|
|
62
63
|
isGroupChat = false,
|
|
63
64
|
msgType = "text",
|
|
64
65
|
commandBody = "",
|
|
65
66
|
normalizedFromUser = "",
|
|
67
|
+
groupChatPolicy = {},
|
|
66
68
|
resolveWecomCommandPolicy,
|
|
67
69
|
resolveWecomAllowFromPolicy,
|
|
68
70
|
resolveWecomDmPolicy,
|
|
@@ -84,6 +86,30 @@ export async function applyWecomBotCommandAndSenderGuard({
|
|
|
84
86
|
const dmPolicy = resolveWecomDmPolicy(api, accountId, {});
|
|
85
87
|
const allowFromPolicy = resolveWecomAllowFromPolicy(api, accountId, {});
|
|
86
88
|
|
|
89
|
+
if (isGroupChat) {
|
|
90
|
+
const groupPolicyMode = String(groupChatPolicy?.policyMode ?? (groupChatPolicy?.enabled === false ? "deny" : "open"))
|
|
91
|
+
.trim()
|
|
92
|
+
.toLowerCase();
|
|
93
|
+
const groupSenderAllowed =
|
|
94
|
+
isAdminUser ||
|
|
95
|
+
groupPolicyMode === "open" ||
|
|
96
|
+
(groupPolicyMode !== "deny" &&
|
|
97
|
+
(groupChatPolicy.allowFrom.includes("*") ||
|
|
98
|
+
isWecomSenderAllowed({
|
|
99
|
+
senderId: normalizedFromUser,
|
|
100
|
+
allowFrom: groupChatPolicy.allowFrom,
|
|
101
|
+
})));
|
|
102
|
+
if (!groupSenderAllowed) {
|
|
103
|
+
return {
|
|
104
|
+
ok: false,
|
|
105
|
+
finishText: groupChatPolicy?.rejectMessage || "当前群聊发送者未授权,请联系管理员。",
|
|
106
|
+
commandBody: String(commandBody ?? ""),
|
|
107
|
+
isAdminUser,
|
|
108
|
+
commandPolicy,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
87
113
|
if (!isGroupChat) {
|
|
88
114
|
const dmAccess = await resolveWecomDirectMessageAccess({
|
|
89
115
|
api,
|
|
@@ -176,7 +202,11 @@ export async function applyWecomBotCommandAndSenderGuard({
|
|
|
176
202
|
if (commandKey === "/status") {
|
|
177
203
|
return {
|
|
178
204
|
ok: false,
|
|
179
|
-
finishText: buildWecomBotStatusText(api, fromUser
|
|
205
|
+
finishText: buildWecomBotStatusText(api, fromUser, {
|
|
206
|
+
accountId,
|
|
207
|
+
chatId,
|
|
208
|
+
isGroupChat,
|
|
209
|
+
}),
|
|
180
210
|
commandBody: nextCommandBody,
|
|
181
211
|
isAdminUser,
|
|
182
212
|
commandPolicy,
|