@wecom/wecom-openclaw-plugin 2026.3.24 → 2026.3.25-beta.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/README.md CHANGED
@@ -4,7 +4,16 @@
4
4
  >
5
5
  > 💬 扫码加入企业微信交流群:
6
6
  >
7
- > <img src="https://wwcdn.weixin.qq.com/node/wework/images/202603181801.a77d987fb8.png" alt="扫码入群交流" width="200" />
7
+ > <img src="https://wwcdn.weixin.qq.com/node/wework/images/202603241759.3fb01c32cc.png" alt="扫码入群交流" width="200" />
8
+
9
+ # 特别说明
10
+ > ****2026.3.22 版本 OpenClaw 兼容说明****
11
+ >
12
+ > 如果你的 OpenClaw 是 2026.3.22 及以上的版本,请升级插件到 2026.3.24 及以上版本。
13
+ >
14
+ > 如果你的 OpenClaw 是 2026.3.22 以下的版本,请保持插件版本在 2026.3.20 版本。
15
+ >
16
+ > 你可以使用以下命令快速安装: `npx -y @wecom/wecom-openclaw-cli install --force`
8
17
 
9
18
  # 🤖 WeCom OpenClaw Plugin
10
19
 
package/dist/index.cjs.js CHANGED
@@ -1525,14 +1525,6 @@ const PLUGIN_VERSION = getVersion();
1525
1525
  * - state-manager.ts : 全局状态管理(带 TTL 清理)
1526
1526
  * - timeout.ts : 超时工具
1527
1527
  */
1528
- /**
1529
- * 去除文本中的 `<think>...</think>` 标签(支持跨行),返回剩余可见文本。
1530
- * 用于判断大模型回复中是否包含实际用户可见内容(而非仅有 thinking 推理过程)。
1531
- */
1532
- function stripThinkTags(text) {
1533
- return text;
1534
- // return text.replace(/<think>[\s\S]*?<\/think>/g, "").trim();
1535
- }
1536
1528
  // ============================================================================
1537
1529
  // 媒体本地路径白名单扩展
1538
1530
  // ============================================================================
@@ -1678,15 +1670,6 @@ async function sendThinkingReply(params) {
1678
1670
  runtime.error?.(`[wecom] Failed to send thinking message: ${String(err)}`);
1679
1671
  }
1680
1672
  }
1681
- /**
1682
- * 累积文本并判断是否有可见内容(去除 <think> 标签后)
1683
- */
1684
- function accumulateText(state, text) {
1685
- state.accumulatedText += text;
1686
- if (!state.hasText && stripThinkTags(state.accumulatedText)) {
1687
- state.hasText = true;
1688
- }
1689
- }
1690
1673
  /**
1691
1674
  * 上传并发送一批媒体文件(统一走主动发送通道)
1692
1675
  *
@@ -1742,30 +1725,22 @@ async function sendMediaBatch(ctx, mediaUrls) {
1742
1725
  */
1743
1726
  async function finishThinkingStream(ctx) {
1744
1727
  const { wsClient, frame, state, runtime } = ctx;
1745
- const visibleText = stripThinkTags(state.accumulatedText);
1746
- let finishText;
1747
- if (visibleText) {
1748
- // 有可见文本:用完整文本关闭流(覆盖 thinking 为真实内容)
1749
- finishText = state.accumulatedText;
1750
- }
1751
- else if (state.hasMedia) {
1752
- // 媒体成功发送:用友好提示告知用户
1753
- finishText = "📎 文件已发送,请查收。";
1754
- }
1755
- else if (state.hasMediaFailed && state.mediaErrorSummary) {
1756
- // 媒体发送失败:直接用错误摘要替换 thinking 流(不再额外发 sendMessage)
1757
- finishText = state.mediaErrorSummary;
1728
+ let finishText = state.accumulatedText;
1729
+ if (state.hasMedia) {
1730
+ if (state.hasMediaFailed && state.mediaErrorSummary) {
1731
+ // 媒体成功发送:用友好提示告知用户
1732
+ finishText = finishText ? `${finishText}\n\n${state.mediaErrorSummary}` : state.mediaErrorSummary;
1733
+ }
1734
+ else if (!finishText) {
1735
+ finishText = "📎 文件已发送,请查收。";
1736
+ }
1758
1737
  }
1759
- else {
1760
- // 核心无可见文本且 deliver 中未处理过媒体。
1761
- //
1762
- // 不使用错误提示,因为 agent 可能已通过内置 message 工具直接调用
1763
- // outbound.sendMedia 成功发送了文件——该路径完全绕过 monitor 的 deliver
1764
- // 回调,所以 state.deliverCalled / state.hasMedia 均为 false,但文件
1765
- // 实际已送达用户。此时显示 "未生成回复" 会误导用户。
1766
- finishText = "✅ 处理完成。";
1738
+ // if (!finishText) {
1739
+ // finishText = "✅ 处理完成。";
1740
+ // }
1741
+ if (finishText) {
1742
+ await sendWeComReply({ wsClient, frame, text: finishText, runtime, finish: true, streamId: state.streamId });
1767
1743
  }
1768
- await sendWeComReply({ wsClient, frame, text: finishText, runtime, finish: true, streamId: state.streamId });
1769
1744
  }
1770
1745
  /**
1771
1746
  * 路由消息到核心处理流程并处理回复
@@ -1782,17 +1757,29 @@ async function routeAndDispatchMessage(params) {
1782
1757
  onCleanup();
1783
1758
  }
1784
1759
  };
1760
+ let isShowThink = !(account.sendThinkingMessage ?? true);
1785
1761
  try {
1786
1762
  await core.channel.reply.dispatchReplyWithBufferedBlockDispatcher({
1787
1763
  ctx: ctxPayload,
1788
1764
  cfg: config,
1789
1765
  dispatcherOptions: {
1766
+ onReplyStart: async () => {
1767
+ if (!isShowThink && state.streamId && !state.accumulatedText) {
1768
+ try {
1769
+ await sendThinkingReply({ wsClient, frame, streamId: state.streamId, runtime });
1770
+ }
1771
+ catch (e) {
1772
+ runtime.error?.(`[wecom] sendThinkingReply threw err: ${String(e)}`);
1773
+ }
1774
+ isShowThink = true;
1775
+ }
1776
+ },
1790
1777
  deliver: async (payload, info) => {
1791
1778
  state.deliverCalled = true;
1792
1779
  // runtime.log?.(`[openclaw -> plugin] kind=${info.kind}, text=${payload.text ?? ''}, mediaUrl=${payload.mediaUrl ?? ''}, mediaUrls=${JSON.stringify(payload.mediaUrls ?? [])}`);
1793
1780
  // 累积文本
1794
1781
  if (payload.text) {
1795
- accumulateText(state, payload.text);
1782
+ state.accumulatedText += (payload.text || '');
1796
1783
  }
1797
1784
  // 发送媒体(统一走主动发送)
1798
1785
  const mediaUrls = payload.mediaUrls?.length ? payload.mediaUrls : payload.mediaUrl ? [payload.mediaUrl] : [];
@@ -1930,11 +1917,11 @@ async function processWeComMessage(params) {
1930
1917
  const cleanupState = () => {
1931
1918
  deleteMessageState(messageId);
1932
1919
  };
1933
- // Step 6: 发送"思考中"消息
1934
- const shouldSendThinking = account.sendThinkingMessage ?? true;
1935
- if (shouldSendThinking) {
1936
- await sendThinkingReply({ wsClient, frame, streamId, runtime });
1937
- }
1920
+ // // Step 6: 发送"思考中"消息
1921
+ // const shouldSendThinking = account.sendThinkingMessage ?? true;
1922
+ // if (shouldSendThinking) {
1923
+ // await sendThinkingReply({ wsClient, frame, streamId, runtime });
1924
+ // }
1938
1925
  // Step 7: 构建上下文并路由到核心处理流程(带整体超时保护)
1939
1926
  const ctxPayload = buildMessageContext(frame, account, config, text, mediaList, quoteContent);
1940
1927
  // runtime.log?.(`[plugin -> openclaw] body=${text}, mediaPaths=${JSON.stringify(mediaList.map(m => m.path))}${quoteContent ? `, quote=${quoteContent}` : ''}`);
@@ -1952,22 +1939,6 @@ async function processWeComMessage(params) {
1952
1939
  }
1953
1940
  catch (err) {
1954
1941
  runtime.error?.(`[wecom][plugin] Message processing failed or timed out: ${String(err)}`);
1955
- // 确保 thinking 流被关闭,防止异常/超时时 thinking 消息一直残留
1956
- try {
1957
- if (shouldSendThinking) {
1958
- await sendWeComReply({
1959
- wsClient,
1960
- frame,
1961
- text: "处理消息时出现异常,请稍后重试。",
1962
- runtime,
1963
- finish: true,
1964
- streamId: state.streamId,
1965
- });
1966
- }
1967
- }
1968
- catch (finishErr) {
1969
- runtime.error?.(`[wecom] Failed to finish thinking stream on error: ${String(finishErr)}`);
1970
- }
1971
1942
  cleanupState();
1972
1943
  }
1973
1944
  }