@dingtalk-real-ai/dingtalk-connector 0.8.20 → 0.8.21
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 +45 -0
- package/README.en.md +18 -2
- package/README.md +18 -2
- package/dist/{connection-BZd5NXuh.mjs → connection-D4uO_J9G.mjs} +33 -7
- package/dist/entry-bundled.mjs +1 -1
- package/dist/gateway-methods-B0_tBGPn.mjs +2 -0
- package/dist/{gateway-methods-DI8lkjSd.mjs → gateway-methods-BNuB2wXl.mjs} +2 -2
- package/dist/index.mjs +2 -2
- package/dist/{media-DUMfXnwJ.mjs → media-BRqGsKUB.mjs} +8 -8
- package/dist/{media-DEuF7r3G.mjs → media-DD7Rlljd.mjs} +1 -1
- package/dist/{message-handler-_vk6QsWo.mjs → message-handler-CPGT1bgU.mjs} +74 -13
- package/dist/{messaging-CyIJY4h2.mjs → messaging-DQwrrd68.mjs} +90 -10
- package/dist/{runtime-b4xvqwW6.mjs → runtime-BphH7_vR.mjs} +5 -5
- package/dist/{utils-DY1gFCdU.mjs → utils-BqUoUOwd.mjs} +1 -1
- package/dist/utils-QEvgZ2uM.mjs +119 -0
- package/docs/RELEASE_NOTES_V0.8.21-beta.0.md +163 -0
- package/docs/RELEASE_NOTES_V0.8.21.md +154 -0
- package/docs/TROUBLESHOOTING.md +28 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/src/core/connection.ts +75 -13
- package/src/core/message-handler.ts +15 -1
- package/src/reply-dispatcher.ts +101 -5
- package/src/services/messaging/card.ts +117 -5
- package/src/utils/empty-reply.ts +72 -0
- package/src/utils/index.ts +1 -0
- package/dist/gateway-methods-DtdiDpYK.mjs +0 -2
- package/dist/utils-CIfI_3Jh.mjs +0 -63
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { u as uploadMediaToDingTalk } from "./media-
|
|
1
|
+
import { u as uploadMediaToDingTalk } from "./media-BRqGsKUB.mjs";
|
|
2
2
|
import { n as createLoggerFromConfig } from "./logger-BDWwViGT.mjs";
|
|
3
3
|
import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
|
|
4
|
-
import { i as
|
|
4
|
+
import { i as DINGTALK_API, o as getAccessToken, s as getOapiAccessToken } from "./utils-QEvgZ2uM.mjs";
|
|
5
5
|
import { r as MEDIA_MSG_TYPES } from "./session-DJ4jYqPv.mjs";
|
|
6
6
|
//#region src/services/messaging/card.ts
|
|
7
7
|
const AI_CARD_TEMPLATE_ID = "02fcf2f4-5e02-4a85-b672-46d1f715543e.schema";
|
|
@@ -99,10 +99,16 @@ const AICardStatus = {
|
|
|
99
99
|
FAILED: "5"
|
|
100
100
|
};
|
|
101
101
|
/**
|
|
102
|
+
* 统一换行符为 \n,避免 CRLF 干扰 Markdown 解析
|
|
103
|
+
*/
|
|
104
|
+
function normalizeLineEndings(text) {
|
|
105
|
+
return text.replace(/\r\n?/g, "\n");
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
102
108
|
* 确保 Markdown 表格前有空行,否则钉钉无法正确渲染表格
|
|
103
109
|
*/
|
|
104
110
|
function ensureTableBlankLines(text) {
|
|
105
|
-
const lines = text.split("\n");
|
|
111
|
+
const lines = normalizeLineEndings(text).split("\n");
|
|
106
112
|
const result = [];
|
|
107
113
|
const tableDividerRegex = /^\s*\|?\s*:?-+:?\s*(\|?\s*:?-+:?\s*)+\|?\s*$/;
|
|
108
114
|
const tableRowRegex = /^\s*\|?.*\|.*\|?\s*$/;
|
|
@@ -116,6 +122,79 @@ function ensureTableBlankLines(text) {
|
|
|
116
122
|
return result.join("\n");
|
|
117
123
|
}
|
|
118
124
|
/**
|
|
125
|
+
* 将单个 \n 转换为 <br>,保留 \n\n 段落分隔。
|
|
126
|
+
*
|
|
127
|
+
* 钉钉 AI Card 渲染器的换行约定:
|
|
128
|
+
* - 普通文本:用 `<br>` 做换行,`\n` 不创建视觉换行
|
|
129
|
+
* - 代码块(```):用 `\n` 做换行,`<br>` 会原样显示为文本
|
|
130
|
+
* - 列表(- / 1.)、表格(|)、标题(#):用 `\n` 做语法行分隔
|
|
131
|
+
* - 引用块(>):用 `<br>` + lazy continuation,续行不需要 `>`
|
|
132
|
+
* - 段落间距:`\n\n`
|
|
133
|
+
*
|
|
134
|
+
* 本函数按上述约定转换:
|
|
135
|
+
* - 代码块内:完全保留原始 `\n`
|
|
136
|
+
* - 连续引用行:合并为一行,`<br>` 连接,去掉续行 `>` 前缀
|
|
137
|
+
* - 其余:Markdown 块语法行前保留 `\n`,单 `\n` → `<br>`
|
|
138
|
+
*/
|
|
139
|
+
function fixNewlines(text) {
|
|
140
|
+
const normalized = normalizeLineEndings(text);
|
|
141
|
+
const markdownBlockStartPattern = /^(\s{0,3}(?:[-*+]|\d+[.)])[ ])|(\s{0,3}\|)|(\s{0,3}#{1,6}\s)|(\s{0,3}(?:[-*_])\s*(?:[-*_])\s*(?:[-*_]))/;
|
|
142
|
+
const fencePattern = /^\s{0,3}```/;
|
|
143
|
+
const quotePattern = /^\s{0,3}>\s?/;
|
|
144
|
+
const mergedLines = [];
|
|
145
|
+
let pendingQuoteLines = [];
|
|
146
|
+
let inCodeBlock = false;
|
|
147
|
+
const flushPendingQuoteLines = () => {
|
|
148
|
+
if (pendingQuoteLines.length > 0) {
|
|
149
|
+
mergedLines.push(pendingQuoteLines.join("<br>"));
|
|
150
|
+
pendingQuoteLines = [];
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
for (const line of normalized.split("\n")) {
|
|
154
|
+
const isFence = fencePattern.test(line);
|
|
155
|
+
if (inCodeBlock) {
|
|
156
|
+
flushPendingQuoteLines();
|
|
157
|
+
mergedLines.push(line);
|
|
158
|
+
if (isFence) inCodeBlock = false;
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
if (isFence) {
|
|
162
|
+
flushPendingQuoteLines();
|
|
163
|
+
mergedLines.push(line);
|
|
164
|
+
inCodeBlock = true;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (quotePattern.test(line)) if (pendingQuoteLines.length === 0) pendingQuoteLines.push(line);
|
|
168
|
+
else pendingQuoteLines.push(line.replace(quotePattern, ""));
|
|
169
|
+
else {
|
|
170
|
+
flushPendingQuoteLines();
|
|
171
|
+
mergedLines.push(line);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
flushPendingQuoteLines();
|
|
175
|
+
const lines = mergedLines;
|
|
176
|
+
inCodeBlock = false;
|
|
177
|
+
const parts = [];
|
|
178
|
+
for (let i = 0; i < lines.length; i++) {
|
|
179
|
+
const currentLine = lines[i];
|
|
180
|
+
const nextInCodeBlock = fencePattern.test(currentLine) ? !inCodeBlock : inCodeBlock;
|
|
181
|
+
if (i < lines.length - 1) {
|
|
182
|
+
const nextLine = lines[i + 1];
|
|
183
|
+
const keepNewline = nextInCodeBlock || currentLine === "" || nextLine === "" || fencePattern.test(nextLine) || markdownBlockStartPattern.test(nextLine);
|
|
184
|
+
parts.push(currentLine + (keepNewline ? "\n" : "<br>"));
|
|
185
|
+
} else parts.push(currentLine);
|
|
186
|
+
inCodeBlock = nextInCodeBlock;
|
|
187
|
+
}
|
|
188
|
+
return parts.join("");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* 标准化 AI Card 消息内容:先修复表格空行,再处理换行符。
|
|
192
|
+
* 用于 streamAICard 和 finishAICard 的所有路径,确保行为一致。
|
|
193
|
+
*/
|
|
194
|
+
function normalizeForCard(content) {
|
|
195
|
+
return fixNewlines(ensureTableBlankLines(content));
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
119
198
|
* 构建卡片投放请求体
|
|
120
199
|
*/
|
|
121
200
|
function buildDeliverBody(cardInstanceId, target, robotCode) {
|
|
@@ -205,7 +284,7 @@ async function streamAICard(card, content, finished = false, config, log) {
|
|
|
205
284
|
outTrackId: card.cardInstanceId,
|
|
206
285
|
cardData: { cardParamMap: {
|
|
207
286
|
flowStatus: AICardStatus.INPUTING,
|
|
208
|
-
msgContent: content,
|
|
287
|
+
msgContent: normalizeForCard(content),
|
|
209
288
|
staticMsgContent: "",
|
|
210
289
|
sys_full_json_obj: JSON.stringify({ order: ["msgContent"] }),
|
|
211
290
|
config: JSON.stringify({ autoLayout: true })
|
|
@@ -237,12 +316,13 @@ async function streamAICard(card, content, finished = false, config, log) {
|
|
|
237
316
|
}
|
|
238
317
|
card.inputingStarted = true;
|
|
239
318
|
}
|
|
240
|
-
const fixedContent =
|
|
319
|
+
const fixedContent = normalizeForCard(content);
|
|
320
|
+
const streamContent = finished ? fixedContent : fixedContent.replace(/\n+$/, "");
|
|
241
321
|
const body = {
|
|
242
322
|
outTrackId: card.cardInstanceId,
|
|
243
323
|
guid: `${Date.now()}_${Math.random().toString(36).slice(2, 8)}`,
|
|
244
324
|
key: "msgContent",
|
|
245
|
-
content:
|
|
325
|
+
content: streamContent,
|
|
246
326
|
isFull: true,
|
|
247
327
|
isFinalize: finished,
|
|
248
328
|
isError: false
|
|
@@ -282,7 +362,7 @@ async function streamAICard(card, content, finished = false, config, log) {
|
|
|
282
362
|
*/
|
|
283
363
|
async function finishAICard(card, content, config, log) {
|
|
284
364
|
if (config) await ensureValidToken(card, config);
|
|
285
|
-
const fixedContent =
|
|
365
|
+
const fixedContent = normalizeForCard(content);
|
|
286
366
|
log?.info?.(`[DingTalk][AICard] 开始 finish,最终内容长度=${fixedContent.length}`);
|
|
287
367
|
await streamAICard(card, fixedContent, true, config, log);
|
|
288
368
|
const body = {
|
|
@@ -771,7 +851,7 @@ async function sendMediaToDingTalk(params) {
|
|
|
771
851
|
});
|
|
772
852
|
}
|
|
773
853
|
let resolvedMediaUrl = mediaUrl;
|
|
774
|
-
const { toLocalPath } = await import("./media-
|
|
854
|
+
const { toLocalPath } = await import("./media-DD7Rlljd.mjs");
|
|
775
855
|
const _fs = await import("fs");
|
|
776
856
|
const _path = await import("path");
|
|
777
857
|
const directPath = toLocalPath(mediaUrl);
|
|
@@ -806,7 +886,7 @@ async function sendMediaToDingTalk(params) {
|
|
|
806
886
|
}
|
|
807
887
|
if (mediaType === "video") {
|
|
808
888
|
const videoMarker = `[DINGTALK_VIDEO]{"path":"${mediaUrl}"}[/DINGTALK_VIDEO]`;
|
|
809
|
-
const { processVideoMarkers } = await import("./media-
|
|
889
|
+
const { processVideoMarkers } = await import("./media-DD7Rlljd.mjs");
|
|
810
890
|
await processVideoMarkers(videoMarker, "", config, oapiToken, console, true, targetParam);
|
|
811
891
|
if (text?.trim()) {
|
|
812
892
|
const result = await sendProactive(config, targetParam, text, {
|
|
@@ -830,7 +910,7 @@ async function sendMediaToDingTalk(params) {
|
|
|
830
910
|
fileName,
|
|
831
911
|
fileType: ext || "file"
|
|
832
912
|
};
|
|
833
|
-
const { sendFileProactive } = await import("./media-
|
|
913
|
+
const { sendFileProactive } = await import("./media-DD7Rlljd.mjs");
|
|
834
914
|
await sendFileProactive(config, targetParam, fileInfo, uploadResult.mediaId, log);
|
|
835
915
|
return {
|
|
836
916
|
ok: true,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { d as __exportAll } from "./media-
|
|
1
|
+
import { d as __exportAll } from "./media-BRqGsKUB.mjs";
|
|
2
2
|
import { a as resolveDingtalkAccount, c as addWildcardAllowFrom, d as hasConfiguredSecretInput, f as normalizeAccountId, l as createDefaultChannelRuntimeState, m as resolveDefaultGroupPolicy, o as resolveDingtalkCredentials, p as resolveAllowlistProviderRuntimeGroupPolicy, r as resolveDefaultDingtalkAccountId, s as DEFAULT_ACCOUNT_ID, t as listDingtalkAccountIds, u as formatDocsLink } from "./accounts-CF4oK_HZ.mjs";
|
|
3
3
|
import { t as createLogger } from "./logger-BDWwViGT.mjs";
|
|
4
4
|
import { t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
|
|
5
|
-
import "./utils-
|
|
6
|
-
import { n as sendMediaToDingTalk, o as sendTextToDingTalk } from "./messaging-
|
|
5
|
+
import "./utils-QEvgZ2uM.mjs";
|
|
6
|
+
import { n as sendMediaToDingTalk, o as sendTextToDingTalk } from "./messaging-DQwrrd68.mjs";
|
|
7
7
|
import { createRequire } from "node:module";
|
|
8
8
|
import { z, z as z$1 } from "zod";
|
|
9
9
|
//#region src/secret-input.ts
|
|
@@ -905,8 +905,8 @@ async function monitorDingtalkProvider(opts = {}) {
|
|
|
905
905
|
const log = createLogger(cfg.channels?.["dingtalk-connector"]?.debug ?? false);
|
|
906
906
|
const [accountsModule, monitorAccountModule, monitorSingleModule] = await Promise.all([
|
|
907
907
|
import("./accounts-BSIiLyZa.mjs"),
|
|
908
|
-
import("./message-handler-
|
|
909
|
-
import("./connection-
|
|
908
|
+
import("./message-handler-CPGT1bgU.mjs"),
|
|
909
|
+
import("./connection-D4uO_J9G.mjs")
|
|
910
910
|
]);
|
|
911
911
|
const { resolveDingtalkAccount, listEnabledDingtalkAccounts } = accountsModule;
|
|
912
912
|
const { handleDingTalkMessage } = monitorAccountModule;
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { n as dingtalkOapiHttp, t as dingtalkHttp } from "./http-client-DFWZgO1n.mjs";
|
|
2
|
+
//#region src/utils/token.ts
|
|
3
|
+
const DINGTALK_API = "https://api.dingtalk.com";
|
|
4
|
+
const DINGTALK_OAPI = "https://oapi.dingtalk.com";
|
|
5
|
+
/**
|
|
6
|
+
* 按 clientId 分桶缓存,避免多账号串 token。
|
|
7
|
+
*/
|
|
8
|
+
const apiTokenCache = /* @__PURE__ */ new Map();
|
|
9
|
+
const oapiTokenCache = /* @__PURE__ */ new Map();
|
|
10
|
+
function cacheKey(config) {
|
|
11
|
+
const clientId = String(config?.clientId ?? "").trim();
|
|
12
|
+
if (!clientId) throw new Error("Invalid DingtalkConfig: clientId is required for token caching. Please ensure your configuration includes a valid clientId.");
|
|
13
|
+
return clientId;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 获取钉钉 Access Token(新版 API)
|
|
17
|
+
*/
|
|
18
|
+
async function getAccessToken(config) {
|
|
19
|
+
const now = Date.now();
|
|
20
|
+
const key = cacheKey(config);
|
|
21
|
+
const cached = apiTokenCache.get(key);
|
|
22
|
+
if (cached && cached.expiryMs > now + 6e4) return cached.token;
|
|
23
|
+
const response = await dingtalkHttp.post(`${DINGTALK_API}/v1.0/oauth2/accessToken`, {
|
|
24
|
+
appKey: config.clientId,
|
|
25
|
+
appSecret: config.clientSecret
|
|
26
|
+
});
|
|
27
|
+
const token = response.data.accessToken;
|
|
28
|
+
const expireInSec = Number(response.data.expireIn ?? 0);
|
|
29
|
+
apiTokenCache.set(key, {
|
|
30
|
+
token,
|
|
31
|
+
expiryMs: now + expireInSec * 1e3
|
|
32
|
+
});
|
|
33
|
+
return token;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 获取钉钉 OAPI Access Token(旧版 API,用于媒体上传等)
|
|
37
|
+
*/
|
|
38
|
+
async function getOapiAccessToken(config) {
|
|
39
|
+
try {
|
|
40
|
+
const now = Date.now();
|
|
41
|
+
const key = cacheKey(config);
|
|
42
|
+
const cached = oapiTokenCache.get(key);
|
|
43
|
+
if (cached && cached.expiryMs > now + 6e4) return cached.token;
|
|
44
|
+
const resp = await dingtalkOapiHttp.get(`${DINGTALK_OAPI}/gettoken`, { params: {
|
|
45
|
+
appkey: config.clientId,
|
|
46
|
+
appsecret: config.clientSecret
|
|
47
|
+
} });
|
|
48
|
+
if (resp.data?.errcode === 0 && resp.data?.access_token) {
|
|
49
|
+
const token = String(resp.data.access_token);
|
|
50
|
+
const expiresInSec = Number(resp.data.expires_in ?? 7200);
|
|
51
|
+
oapiTokenCache.set(key, {
|
|
52
|
+
token,
|
|
53
|
+
expiryMs: now + expiresInSec * 1e3
|
|
54
|
+
});
|
|
55
|
+
return token;
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
} catch {
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
//#endregion
|
|
63
|
+
//#region src/utils/empty-reply.ts
|
|
64
|
+
/**
|
|
65
|
+
* 空回复(final 文本为空)兜底文案集中处。
|
|
66
|
+
*
|
|
67
|
+
* 背景
|
|
68
|
+
* ----
|
|
69
|
+
* 群聊场景下用户 @ 机器人后看到「任务执行完成(无文本输出)」,常见根因不是 connector,
|
|
70
|
+
* 而是上游 OpenClaw 的 reply delivery mode(`source-reply-delivery-mode.ts`):群聊
|
|
71
|
+
* 默认走 `message_tool_only`,会跳过 `onPartialReply` 与 `accumulatedText`,
|
|
72
|
+
* 导致本插件累积的文本始终为空,最后落到 connector 的空回复兜底。
|
|
73
|
+
*
|
|
74
|
+
* 修复路径在 OpenClaw 的 `openclaw.json`:
|
|
75
|
+
* {
|
|
76
|
+
* "messages": {
|
|
77
|
+
* "groupChat": { "visibleReplies": "automatic" }
|
|
78
|
+
* }
|
|
79
|
+
* }
|
|
80
|
+
*
|
|
81
|
+
* 本模块的优化目标:让群聊场景看到的兜底文案变成一句可操作的提示,
|
|
82
|
+
* 并在日志里给运维一份完整指引,而不是一句无信息量的「任务执行完成」。
|
|
83
|
+
* 单聊场景的兜底文案保持原样(单聊空 final 通常是模型自身输出空)。
|
|
84
|
+
*/
|
|
85
|
+
const DIRECT_FALLBACK_TEXT = "✅ 任务执行完成(无文本输出)";
|
|
86
|
+
const GROUP_FALLBACK_TEXT = [
|
|
87
|
+
"ℹ️ 暂未收到模型回复内容。",
|
|
88
|
+
"若群聊频繁出现该提示,请联系机器人管理员检查 OpenClaw 配置:",
|
|
89
|
+
"`messages.groupChat.visibleReplies` 需设为 `\"automatic\"`",
|
|
90
|
+
"(详见 README / TROUBLESHOOTING.md)。"
|
|
91
|
+
].join("\n");
|
|
92
|
+
const GROUP_FALLBACK_LOG_HINT = "群聊 final 文本为空:常见根因是 OpenClaw `messages.groupChat.visibleReplies` 未设为 \"automatic\"(上游 source-reply-delivery-mode.ts 默认 message_tool_only, 会跳过 partial/accumulated 文本)。请在 openclaw.json 中追加:{ \"messages\": { \"groupChat\": { \"visibleReplies\": \"automatic\" } } },然后 `openclaw gateway restart`。详见 docs/TROUBLESHOOTING.md。";
|
|
93
|
+
/**
|
|
94
|
+
* 选取空回复的兜底文案。
|
|
95
|
+
*
|
|
96
|
+
* - 群聊:附带修复指引(指向 OpenClaw `messages.groupChat.visibleReplies` 配置)。
|
|
97
|
+
* - 单聊:维持原文案,避免对模型本身就输出空的常规场景产生噪音。
|
|
98
|
+
*/
|
|
99
|
+
function pickEmptyReplyFallbackText(isGroup) {
|
|
100
|
+
return isGroup ? GROUP_FALLBACK_TEXT : DIRECT_FALLBACK_TEXT;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* 群聊空回复时给运维的 warn 级别日志指引(含 openclaw.json 修复片段)。
|
|
104
|
+
* 单聊不需要这条 hint,因为单聊空回复多半与配置无关。
|
|
105
|
+
*/
|
|
106
|
+
function emptyGroupReplyLogHint() {
|
|
107
|
+
return GROUP_FALLBACK_LOG_HINT;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* 群聊是否未显式将 OpenClaw `messages.groupChat.visibleReplies` 设为 `"automatic"`。
|
|
111
|
+
*
|
|
112
|
+
* `undefined` / 缺失 / `messages: {}` 均视为未开启(与上游默认 `message_tool_only` 行为一致),
|
|
113
|
+
* 需要 connector 在「本轮无任何用户可见回复」时用 idle 兜底提示配置。
|
|
114
|
+
*/
|
|
115
|
+
function groupChatLacksVisibleRepliesAutomatic(cfg) {
|
|
116
|
+
return cfg?.messages?.groupChat?.visibleReplies !== "automatic";
|
|
117
|
+
}
|
|
118
|
+
//#endregion
|
|
119
|
+
export { DINGTALK_OAPI as a, DINGTALK_API as i, groupChatLacksVisibleRepliesAutomatic as n, getAccessToken as o, pickEmptyReplyFallbackText as r, getOapiAccessToken as s, emptyGroupReplyLogHint as t };
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Release Notes - v0.8.21-beta.0
|
|
2
|
+
|
|
3
|
+
> **社区验证版本** — 计划经过 ~3 天社区验证后晋升为正式版 `v0.8.21`。
|
|
4
|
+
> **Community validation release** — planned to promote to GA `v0.8.21` after ~3 days of community validation.
|
|
5
|
+
|
|
6
|
+
## 🎉 本次重点 / Highlights
|
|
7
|
+
|
|
8
|
+
本版本聚焦 WebSocket 连接稳定性 + 群聊体验:
|
|
9
|
+
|
|
10
|
+
1. 修掉本仓 listener 注册时机错乱导致的 ~30s 一次幽灵重连(社区贡献 #566)
|
|
11
|
+
2. 修掉消息处理保活 interval 与 `TIMEOUT_THRESHOLD` 不匹配的兜底失效(#594)
|
|
12
|
+
3. 过滤上游 `dingtalk-stream@2.1.4` SDK 直接走 `console.info` 的噪音(#571 / #536 / #573)
|
|
13
|
+
4. 群聊 @ 机器人空回复时,把无信息的「任务执行完成」改为带 `openclaw.json` 修复指引的可操作文案(#589)
|
|
14
|
+
|
|
15
|
+
This release focuses on WebSocket stability + group-chat UX:
|
|
16
|
+
|
|
17
|
+
1. Fix root cause of ~30s phantom reconnect cycle caused by listener-registration timing (community contribution, #566)
|
|
18
|
+
2. Fix message-processing keepalive interval mismatch with `TIMEOUT_THRESHOLD` (#594)
|
|
19
|
+
3. Filter noisy `console.info` calls from upstream `dingtalk-stream@2.1.4` (#571 / #536 / #573)
|
|
20
|
+
4. Group `@` mention's empty-reply fallback now embeds an actionable `openclaw.json` fix hint (#589)
|
|
21
|
+
|
|
22
|
+
## 🐛 修复 / Fixes
|
|
23
|
+
|
|
24
|
+
### WebSocket phantom reconnect 根因修复 (#566)
|
|
25
|
+
|
|
26
|
+
**现象**:约每 30 秒一次的 WebSocket 重连,`Disconnecting.` / `connect success` 成对出现,看起来像连接频繁掉线。
|
|
27
|
+
|
|
28
|
+
**根因**:`setupPongListener` / `setupMessageListener` / `setupCloseListener` 在 `client.connect()` 之前被调用,此时 `client.socket === undefined`,函数体里 `client.socket?.on(...)` 的可选链让调用静默成 no-op——三个 listener 实际从未挂上。
|
|
29
|
+
|
|
30
|
+
- pong listener 缺失 → `lastSocketAvailableTime` 不刷新 → 命中 `TIMEOUT_THRESHOLD = 20s` → `doReconnect()` 触发
|
|
31
|
+
- message listener 缺失 → 服务端真实下发的 disconnect topic 也无法响应
|
|
32
|
+
- close listener 缺失 → socket close 不能立即触发重连
|
|
33
|
+
|
|
34
|
+
**修复**:
|
|
35
|
+
|
|
36
|
+
- 删除模块初始化时的 no-op setup 调用
|
|
37
|
+
- 初次 `client.connect()` 成功后立即注册三个 listener
|
|
38
|
+
- `doReconnect()` 里把 setup 挪到 `client.connect()` 之后、`await for OPEN` 之前——避免 keepAlive 期间 ping 的 pong 回来时 listener 还没挂被丢的 race window
|
|
39
|
+
|
|
40
|
+
**致谢**:感谢 [@Majorshi](https://github.com/Majorshi) 提交 [PR #566](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/566),并感谢 [@lizhiyao](https://github.com/lizhiyao) / [@edokeh](https://github.com/edokeh) / [@jeikl](https://github.com/jeikl) 提前验证修复有效。
|
|
41
|
+
|
|
42
|
+
### 消息处理保活 interval 兜底 bug (#594)
|
|
43
|
+
|
|
44
|
+
**根因**:`markMessageProcessingStart` 启动的兜底定时器原本 30s 间隔,但 `TIMEOUT_THRESHOLD` 已在 `d90916b` 中从 90s 降到 20s——30s 间隔无法在 AI 长任务期间防住超时(AI 任务跑到约 21s 就可能触发 keepAlive 幽灵重连,下次刷新还要等 9 秒)。
|
|
45
|
+
|
|
46
|
+
**修复**:interval 调整为 15s(< `TIMEOUT_THRESHOLD = 20s`),让保活真正生效。同时清理文件头 docstring 与相关注释里的 stale `90 秒超时` 文案。
|
|
47
|
+
|
|
48
|
+
### 上游 SDK `console.info` 噪音过滤 (#571 / #536 / #573)
|
|
49
|
+
|
|
50
|
+
**现象**:日志反复出现
|
|
51
|
+
```
|
|
52
|
+
[2026-05-09T07:04:42.420Z] connect success
|
|
53
|
+
Disconnecting.
|
|
54
|
+
[2026-05-09T07:05:12.483Z] connect success
|
|
55
|
+
Disconnecting.
|
|
56
|
+
...
|
|
57
|
+
```
|
|
58
|
+
看起来像连接频繁掉线。
|
|
59
|
+
|
|
60
|
+
**根因**:
|
|
61
|
+
1. 上游 `dingtalk-stream@2.1.4` SDK 在 `client.cjs:138 / :185` 每次 `disconnect()` / `connect()` 时直接 `console.info(...)`,绕过 logger,在频繁重连场景下会刷屏
|
|
62
|
+
2. 钉钉服务端在 LB / 实例切换等场景下可能下发 `disconnect` topic,客户端需断开重连——这是协议机制
|
|
63
|
+
3. connector 在 `src/core/connection.ts:setupMessageListener` / `setupCloseListener` 中正确处理了重连,消息收发无感
|
|
64
|
+
|
|
65
|
+
**修复**:
|
|
66
|
+
- 在 `src/core/connection.ts` 新增 `silenceDingtalkStreamConsoleNoise()`:模块级一次性 patch `console.info`,**只过滤 `Disconnecting.` 和 `[time] connect success` 两条精确字符串**,其他 `console.info` 不受影响
|
|
67
|
+
- 在首个账号连上时通过 `printConnectionNoticeOnce()` 打印一次连接生命周期说明,解释过滤动机以及「高频重连不正常」的预期,多 bot 启动时不重复
|
|
68
|
+
- `setupMessageListener` 收到 `disconnect` topic 时加一行 `logger.debug`,便于排查时查看完整生命周期
|
|
69
|
+
|
|
70
|
+
## 🩹 改进 / Improvements
|
|
71
|
+
|
|
72
|
+
### 群聊空回复兜底文案带修复指引 (#589)
|
|
73
|
+
|
|
74
|
+
**现象**:群聊 @ 机器人时只看到「✅ 任务执行完成(无文本输出)」,没有任何模型回复内容。
|
|
75
|
+
|
|
76
|
+
**根因**:当 OpenClaw `messages.groupChat.visibleReplies` 未设为 `"automatic"` 时,上游 `source-reply-delivery-mode.ts` 对群聊默认走 `message_tool_only`:
|
|
77
|
+
- 不调 `onPartialReply` → connector `accumulatedText` 始终为空
|
|
78
|
+
- 大多数情况下也不调 `deliver(final)` → 三处空 final 兜底全部打到原文案
|
|
79
|
+
|
|
80
|
+
用户看到的只是一句无信息的「任务执行完成」,完全没线索去查 `openclaw.json`。
|
|
81
|
+
|
|
82
|
+
**修复**:
|
|
83
|
+
- 新增 `src/utils/empty-reply.ts` 集中兜底文案
|
|
84
|
+
- `reply-dispatcher.ts` 的 `closeStreaming` / `deliver` 空 final 分支:群聊改用带 `openclaw.json` 修复指引的可操作文案
|
|
85
|
+
- 新增 `maybeSendGroupVisibleRepliesIdleNudge`:在 `onIdle` / `onError` 时若本轮无任何用户可见输出(覆盖上游 `message_tool_only` 根本不调 `deliver()` 的盲区),主动 nudge 一条配置指引
|
|
86
|
+
- `core/message-handler.ts` 异步模式同点位分流
|
|
87
|
+
- warn 日志同步打印完整 `openclaw.json` 修复片段,便于运维直接复制
|
|
88
|
+
- **单聊文案保持不变**(`✅ 任务执行完成(无文本输出)`):单聊空 final 通常是模型本身输出空,不需要指引噪音
|
|
89
|
+
|
|
90
|
+
### 新群聊兜底文案
|
|
91
|
+
|
|
92
|
+
```
|
|
93
|
+
ℹ️ 暂未收到模型回复内容。
|
|
94
|
+
若群聊频繁出现该提示,请联系机器人管理员检查 OpenClaw 配置:
|
|
95
|
+
`messages.groupChat.visibleReplies` 需设为 `"automatic"`
|
|
96
|
+
(详见 README / TROUBLESHOOTING.md)。
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 用户/运维侧修复
|
|
100
|
+
|
|
101
|
+
`~/.openclaw/openclaw.json` 加上:
|
|
102
|
+
|
|
103
|
+
```json
|
|
104
|
+
{
|
|
105
|
+
"messages": {
|
|
106
|
+
"groupChat": { "visibleReplies": "automatic" }
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
然后 `openclaw gateway restart`。
|
|
112
|
+
|
|
113
|
+
> 跟进调研:插件侧直接覆盖 `sourceReplyDeliveryMode: "automatic"` 让用户不需要改 `openclaw.json` 的方案在 [Issue #591](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/591) 跟踪。
|
|
114
|
+
|
|
115
|
+
## 📚 文档 / Docs
|
|
116
|
+
|
|
117
|
+
- **TROUBLESHOOTING.md**:新增「群聊 @ 机器人只返回『任务执行完成(无文本输出)』」条目,给出修复步骤
|
|
118
|
+
- `silenceDingtalkStreamConsoleNoise` 周边的注释 / 函数命名 / banner 文案统一收紧(#592)
|
|
119
|
+
- 清理 `src/core/connection.ts` 中的 stale `90 秒超时` 文档残留(#594)
|
|
120
|
+
|
|
121
|
+
## 🔒 兼容性 / Compatibility
|
|
122
|
+
|
|
123
|
+
- **API 无变化**、配置 schema 无变化、导出符号无变化
|
|
124
|
+
- 不影响 #437 的心跳超时检测修复语义
|
|
125
|
+
- 单聊行为完全不变(兜底文案、流式行为都保持原样)
|
|
126
|
+
- 已配置 `messages.groupChat.visibleReplies = "automatic"` 的群聊行为完全不变
|
|
127
|
+
|
|
128
|
+
## 🧪 验证 / Verification
|
|
129
|
+
|
|
130
|
+
- 单测:`tests/empty-reply.unit.test.ts` 7/7 + `tests/reply-dispatcher` 7/7 通过
|
|
131
|
+
- 多位社区用户已对 #566 修复 +1 验证有效(@lizhiyao / @edokeh / @jeikl)
|
|
132
|
+
- 本机回归(macOS / OpenClaw 2026.5.7):
|
|
133
|
+
- 群聊 @ + `messages: {}` → 看到新的可操作指引
|
|
134
|
+
- 群聊 @ + `visibleReplies = "automatic"` → 恢复正常流式
|
|
135
|
+
- 单聊空 final → 文案不变
|
|
136
|
+
|
|
137
|
+
## ⏭ 下一步 / Next steps
|
|
138
|
+
|
|
139
|
+
- 社区验证 ~3 天稳定后晋升 GA `v0.8.21`
|
|
140
|
+
- [Issue #591](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/591):插件侧 `sourceReplyDeliveryMode` 调研——下个迭代评估
|
|
141
|
+
|
|
142
|
+
## 📥 安装升级 / Installation & Upgrade
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
npx openclaw@latest add @dingtalk-real-ai/dingtalk-connector@0.8.21-beta.0
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
或:
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
npm install @dingtalk-real-ai/dingtalk-connector@beta
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## 🔗 相关链接 / Related Links
|
|
155
|
+
|
|
156
|
+
- [完整变更日志](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/CHANGELOG.md)
|
|
157
|
+
- 关联 PRs / issues:#566 / #589 / #592 / #594 / #571 / #536 / #573 / #545
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
**发布日期 / Release Date**:2026-05-17
|
|
162
|
+
**版本号 / Version**:v0.8.21-beta.0
|
|
163
|
+
**兼容性 / Compatibility**:OpenClaw Gateway 2026.4.9+
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
# Release Notes - v0.8.21
|
|
2
|
+
|
|
3
|
+
> **GA 正式版** — 晋升自 `0.8.21-beta.0`,经社区验证稳定后发布。
|
|
4
|
+
> **General Availability** — Promoted from `0.8.21-beta.0` after community validation.
|
|
5
|
+
|
|
6
|
+
## 🎉 本次重点 / Highlights
|
|
7
|
+
|
|
8
|
+
本版本聚焦 WebSocket 连接稳定性 + 群聊体验,与 `0.8.21-beta.0` 功能完全一致:
|
|
9
|
+
|
|
10
|
+
1. 修掉本仓 listener 注册时机错乱导致的 ~30s 一次幽灵重连(社区贡献 [#566](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/566))
|
|
11
|
+
2. 修掉消息处理保活 interval 与 `TIMEOUT_THRESHOLD` 不匹配的兜底失效([#594](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/594))
|
|
12
|
+
3. 过滤上游 `dingtalk-stream@2.1.4` SDK 直接走 `console.info` 的噪音([#571](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/571) / [#536](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/536) / [#573](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/573))
|
|
13
|
+
4. 群聊 @ 机器人空回复时,把无信息的「任务执行完成」改为带 `openclaw.json` 修复指引的可操作文案([#589](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/589))
|
|
14
|
+
|
|
15
|
+
This release focuses on WebSocket stability + group-chat UX. Functionally identical to `0.8.21-beta.0`:
|
|
16
|
+
|
|
17
|
+
1. Fix root cause of ~30s phantom reconnect cycle caused by listener-registration timing (community contribution, #566)
|
|
18
|
+
2. Fix message-processing keepalive interval mismatch with `TIMEOUT_THRESHOLD` (#594)
|
|
19
|
+
3. Filter noisy `console.info` calls from upstream `dingtalk-stream@2.1.4` (#571 / #536 / #573)
|
|
20
|
+
4. Group `@` mention's empty-reply fallback now embeds an actionable `openclaw.json` fix hint (#589)
|
|
21
|
+
|
|
22
|
+
## 🐛 修复 / Fixes
|
|
23
|
+
|
|
24
|
+
### WebSocket phantom reconnect 根因修复 (#566)
|
|
25
|
+
|
|
26
|
+
**现象**:约每 30 秒一次的 WebSocket 重连,`Disconnecting.` / `connect success` 成对出现,看起来像连接频繁掉线。
|
|
27
|
+
|
|
28
|
+
**根因**:`setupPongListener` / `setupMessageListener` / `setupCloseListener` 在 `client.connect()` 之前被调用,此时 `client.socket === undefined`,函数体里 `client.socket?.on(...)` 的可选链让调用静默成 no-op——三个 listener 实际从未挂上。
|
|
29
|
+
|
|
30
|
+
- pong listener 缺失 → `lastSocketAvailableTime` 不刷新 → 命中 `TIMEOUT_THRESHOLD = 20s` → `doReconnect()` 触发
|
|
31
|
+
- message listener 缺失 → 服务端真实下发的 disconnect topic 也无法响应
|
|
32
|
+
- close listener 缺失 → socket close 不能立即触发重连
|
|
33
|
+
|
|
34
|
+
**修复**:
|
|
35
|
+
- 删除模块初始化时的 no-op setup 调用
|
|
36
|
+
- 初次 `client.connect()` 成功后立即注册三个 listener
|
|
37
|
+
- `doReconnect()` 里把 setup 挪到 `client.connect()` 之后、`await for OPEN` 之前——避免 keepAlive 期间 ping 的 pong 回来时 listener 还没挂被丢的 race window
|
|
38
|
+
|
|
39
|
+
**致谢**:感谢 [@Majorshi](https://github.com/Majorshi) 提交 [PR #566](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/pull/566),并感谢 [@lizhiyao](https://github.com/lizhiyao) / [@edokeh](https://github.com/edokeh) / [@jeikl](https://github.com/jeikl) 验证修复有效。
|
|
40
|
+
|
|
41
|
+
### 消息处理保活 interval 兜底 bug (#594)
|
|
42
|
+
|
|
43
|
+
**根因**:`markMessageProcessingStart` 启动的兜底定时器原本 30s 间隔,但 `TIMEOUT_THRESHOLD` 已在 `d90916b` 中从 90s 降到 20s——30s 间隔无法在 AI 长任务期间防住超时(AI 任务跑到约 21s 就可能触发 keepAlive 幽灵重连,下次刷新还要等 9 秒)。
|
|
44
|
+
|
|
45
|
+
**修复**:interval 调整为 15s(< `TIMEOUT_THRESHOLD = 20s`),让保活真正生效。同时清理文件头 docstring 与相关注释里的 stale `90 秒超时` 文案。
|
|
46
|
+
|
|
47
|
+
### 上游 SDK `console.info` 噪音过滤 (#571 / #536 / #573)
|
|
48
|
+
|
|
49
|
+
**现象**:日志反复出现 `Disconnecting.` / `[time] connect success` 成对刷屏。
|
|
50
|
+
|
|
51
|
+
**修复**:
|
|
52
|
+
- 在 `src/core/connection.ts` 新增 `silenceDingtalkStreamConsoleNoise()`:模块级一次性 patch `console.info`,**只过滤这两条精确字符串**,其他 `console.info` 不受影响
|
|
53
|
+
- 在首个账号连上时通过 `printConnectionNoticeOnce()` 打印一次连接生命周期说明,解释过滤动机以及「高频重连不正常」的预期,多 bot 启动时不重复
|
|
54
|
+
- `setupMessageListener` 收到 `disconnect` topic 时加一行 `logger.debug`,便于排查时查看完整生命周期
|
|
55
|
+
|
|
56
|
+
## 🩹 改进 / Improvements
|
|
57
|
+
|
|
58
|
+
### 群聊空回复兜底文案带修复指引 (#589)
|
|
59
|
+
|
|
60
|
+
**现象**:群聊 @ 机器人时只看到「✅ 任务执行完成(无文本输出)」,没有任何模型回复内容。
|
|
61
|
+
|
|
62
|
+
**根因**:当 OpenClaw `messages.groupChat.visibleReplies` 未设为 `"automatic"` 时,上游 `source-reply-delivery-mode.ts` 对群聊默认走 `message_tool_only`:
|
|
63
|
+
- 不调 `onPartialReply` → connector `accumulatedText` 始终为空
|
|
64
|
+
- 大多数情况下也不调 `deliver(final)` → 三处空 final 兜底全部打到原文案
|
|
65
|
+
|
|
66
|
+
**修复**:
|
|
67
|
+
- 新增 `src/utils/empty-reply.ts` 集中兜底文案
|
|
68
|
+
- `reply-dispatcher.ts` 的 `closeStreaming` / `deliver` 空 final 分支:群聊改用带 `openclaw.json` 修复指引的可操作文案
|
|
69
|
+
- 新增 `maybeSendGroupVisibleRepliesIdleNudge`:在 `onIdle` / `onError` 时若本轮无任何用户可见输出(覆盖上游 `message_tool_only` 根本不调 `deliver()` 的盲区),主动 nudge 一条配置指引
|
|
70
|
+
- `core/message-handler.ts` 异步模式同点位分流
|
|
71
|
+
- warn 日志同步打印完整 `openclaw.json` 修复片段
|
|
72
|
+
- **单聊文案保持不变**
|
|
73
|
+
|
|
74
|
+
### 新群聊兜底文案
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
ℹ️ 暂未收到模型回复内容。
|
|
78
|
+
若群聊频繁出现该提示,请联系机器人管理员检查 OpenClaw 配置:
|
|
79
|
+
`messages.groupChat.visibleReplies` 需设为 `"automatic"`
|
|
80
|
+
(详见 README / TROUBLESHOOTING.md)。
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### 用户/运维侧修复
|
|
84
|
+
|
|
85
|
+
`~/.openclaw/openclaw.json` 加上:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"messages": {
|
|
90
|
+
"groupChat": { "visibleReplies": "automatic" }
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
然后 `openclaw gateway restart`。
|
|
96
|
+
|
|
97
|
+
> 跟进调研:插件侧直接覆盖 `sourceReplyDeliveryMode: "automatic"` 让用户不需要改 `openclaw.json` 的方案在 [Issue #591](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/591) 跟踪。
|
|
98
|
+
|
|
99
|
+
## 📚 文档 / Docs
|
|
100
|
+
|
|
101
|
+
- **TROUBLESHOOTING.md**:新增「群聊 @ 机器人只返回『任务执行完成(无文本输出)』」条目
|
|
102
|
+
- `silenceDingtalkStreamConsoleNoise` 周边的注释 / 函数命名 / banner 文案统一收紧(#592)
|
|
103
|
+
- 清理 `src/core/connection.ts` 中的 stale `90 秒超时` 文档残留(#594)
|
|
104
|
+
|
|
105
|
+
## 🔒 兼容性 / Compatibility
|
|
106
|
+
|
|
107
|
+
- **API 无变化**、配置 schema 无变化、导出符号无变化
|
|
108
|
+
- 不影响 #437 的心跳超时检测修复语义
|
|
109
|
+
- 单聊行为完全不变(兜底文案、流式行为都保持原样)
|
|
110
|
+
- 已配置 `messages.groupChat.visibleReplies = "automatic"` 的群聊行为完全不变
|
|
111
|
+
|
|
112
|
+
## 🧪 验证 / Verification
|
|
113
|
+
|
|
114
|
+
**已验证组合 / Verified combo**:
|
|
115
|
+
- OpenClaw Gateway `2026.5.7`
|
|
116
|
+
- Connector `0.8.21-beta.0` (已晋升为 `0.8.21`)
|
|
117
|
+
- 平台 macOS(darwin 23.2.0)
|
|
118
|
+
|
|
119
|
+
**验证场景**:
|
|
120
|
+
- 单 bot 30s 周期重连已消失
|
|
121
|
+
- 群聊 @ + `messages: {}` → 看到新的可操作指引
|
|
122
|
+
- 群聊 @ + `visibleReplies = "automatic"` → 恢复正常流式
|
|
123
|
+
- 单聊空 final → 文案不变
|
|
124
|
+
|
|
125
|
+
**社区验证**:beta.0(2026-05-17 发布)经过约 2 天社区使用,相关 issue(#571 / #536 / #573 / #545)均确认修复有效。
|
|
126
|
+
|
|
127
|
+
## 📥 安装升级 / Installation & Upgrade
|
|
128
|
+
|
|
129
|
+
```bash
|
|
130
|
+
npx openclaw@latest add @dingtalk-real-ai/dingtalk-connector@0.8.21
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
或:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
npm install @dingtalk-real-ai/dingtalk-connector@latest
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## ⏭ 下一步 / Next steps
|
|
140
|
+
|
|
141
|
+
- [Issue #591](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/issues/591):插件侧 `sourceReplyDeliveryMode` 调研(让用户不再需要改 `openclaw.json`)
|
|
142
|
+
- PR #566 review 中 P2 跟进:抽 `attachSocketListeners()` 单一入口 + listener 注册时机回归测试
|
|
143
|
+
|
|
144
|
+
## 🔗 相关链接 / Related Links
|
|
145
|
+
|
|
146
|
+
- [完整变更日志 / Full Changelog](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/CHANGELOG.md)
|
|
147
|
+
- [Beta release notes (`v0.8.21-beta.0`)](https://github.com/DingTalk-Real-AI/dingtalk-openclaw-connector/blob/main/docs/RELEASE_NOTES_V0.8.21-beta.0.md)
|
|
148
|
+
- 关联 PRs / issues:#566 / #589 / #592 / #594 / #571 / #536 / #573 / #545
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
**发布日期 / Release Date**:2026-05-19
|
|
153
|
+
**版本号 / Version**:v0.8.21
|
|
154
|
+
**兼容性 / Compatibility**:OpenClaw Gateway 2026.4.9+
|
package/docs/TROUBLESHOOTING.md
CHANGED
|
@@ -14,6 +14,34 @@
|
|
|
14
14
|
|
|
15
15
|
---
|
|
16
16
|
|
|
17
|
+
## 群聊 @ 机器人只返回「任务执行完成(无文本输出)」或「暂未收到模型回复内容」
|
|
18
|
+
|
|
19
|
+
**症状**:在群聊里 @ 机器人,只看到兜底文案,单聊却一切正常。
|
|
20
|
+
|
|
21
|
+
**根因**:上游 OpenClaw 的 `source-reply-delivery-mode.ts` 在群聊场景下默认走 `message_tool_only`,会跳过 `onPartialReply` 回调,导致本插件累积的文本始终为空,最终落到 connector 的空回复兜底分支。
|
|
22
|
+
|
|
23
|
+
**解决方案**:在 `~/.openclaw/openclaw.json` 顶层添加 / 合并:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"messages": {
|
|
28
|
+
"groupChat": {
|
|
29
|
+
"visibleReplies": "automatic"
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
然后重启网关让配置生效:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
openclaw gateway restart
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
> 本仓库较新版本中,群聊空回复时兜底文案会自带这段修复指引,日志(warn 级别)也会打印完整的 `openclaw.json` 配置片段,便于运维直接修复;若你看到的仍是冷文案「任务执行完成(无文本输出)」,建议升级到最新 connector。
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
17
45
|
## 配置字段不合法(additional properties)
|
|
18
46
|
|
|
19
47
|
**症状**:
|