liangzimixin 0.3.67 → 0.3.68

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/dist/index.cjs CHANGED
@@ -17522,6 +17522,8 @@ var DEFAULT_INTERNAL_CONFIG = {
17522
17522
  reconnectBaseMs: 1e3,
17523
17523
  reconnectMaxMs: 6e4,
17524
17524
  maxReconnectAttempts: 10,
17525
+ tailBackoffDelays: [12e4, 24e4, 48e4],
17526
+ persistentRetryIntervalMs: 3e5,
17525
17527
  dedup: {
17526
17528
  ttlMs: 3e5,
17527
17529
  maxEntries: 5e3
@@ -18654,6 +18656,48 @@ var TTLMap = class {
18654
18656
  }
18655
18657
  };
18656
18658
 
18659
+ // src/utils/msg-type-detect.ts
18660
+ var MARKDOWN_PATTERNS = [
18661
+ // ── 块级元素 (高置信度) ──
18662
+ // 围栏代码块: ```language
18663
+ { pattern: /^```/m, label: "fenced-code-block" },
18664
+ // 标题: # Title (行首,# 后必须跟空格)
18665
+ { pattern: /^#{1,6}\s+\S/m, label: "heading" },
18666
+ // 表格: | cell | cell | (行首行尾都有 |,且至少有 2 个分隔符)
18667
+ { pattern: /^\|.+\|.+\|\s*$/m, label: "table" },
18668
+ // 引用块: > text (行首)
18669
+ { pattern: /^>\s+\S/m, label: "blockquote" },
18670
+ // 分隔线: --- 或 *** (独占一行,至少 3 个)
18671
+ { pattern: /^[-*]{3,}\s*$/m, label: "horizontal-rule" },
18672
+ // ── 内联元素 (中置信度) ──
18673
+ // 粗体: **text** 或 __text__
18674
+ { pattern: /\*\*[^*]+\*\*/, label: "bold-asterisk" },
18675
+ { pattern: /__[^_]+__/, label: "bold-underscore" },
18676
+ // 链接: [text](url)
18677
+ { pattern: /\[[^\]]+\]\([^)]+\)/, label: "link" },
18678
+ // 图片: ![alt](url)
18679
+ { pattern: /!\[[^\]]*\]\([^)]+\)/, label: "image" },
18680
+ // 行内代码: `code` (但不是 ```)
18681
+ { pattern: /(?<!`)`[^`\n]+`(?!`)/, label: "inline-code" },
18682
+ // ── 列表 (需行首匹配) ──
18683
+ // 有序列表: 1. text
18684
+ { pattern: /^\d+\.\s+\S/m, label: "ordered-list" },
18685
+ // 无序列表: - text 或 * text (行首,后跟空格和非空字符)
18686
+ { pattern: /^[-*]\s+\S/m, label: "unordered-list" }
18687
+ ];
18688
+ function isMarkdownContent(text) {
18689
+ if (!text) return false;
18690
+ for (const { pattern } of MARKDOWN_PATTERNS) {
18691
+ if (pattern.test(text)) {
18692
+ return true;
18693
+ }
18694
+ }
18695
+ return false;
18696
+ }
18697
+ function resolveTextMsgType(text) {
18698
+ return isMarkdownContent(text) ? "markdown" : "text";
18699
+ }
18700
+
18657
18701
  // src/channel/outbound.ts
18658
18702
  var log7 = createLogger("channel/outbound");
18659
18703
  var messageEncryptionStatus = new TTLMap({
@@ -18727,7 +18771,7 @@ var quantumImOutbound = {
18727
18771
  await messagePipe.sendMessage({
18728
18772
  chatId,
18729
18773
  senderId: chatId,
18730
- msgType: "markdown",
18774
+ msgType: resolveTextMsgType(text),
18731
18775
  content: JSON.stringify({ content: text }),
18732
18776
  skipEncrypt
18733
18777
  });
@@ -19080,7 +19124,7 @@ function createQuantumImDeliverFn(deps) {
19080
19124
  await messagePipe.sendMessage({
19081
19125
  chatId,
19082
19126
  senderId,
19083
- msgType: "markdown",
19127
+ msgType: resolveTextMsgType(payload.text),
19084
19128
  content: JSON.stringify({ content: payload.text }),
19085
19129
  replyToMessageId,
19086
19130
  skipEncrypt: shouldSkipEncrypt
@@ -19119,11 +19163,12 @@ function createQuantumImDeliverFn(deps) {
19119
19163
  mediaUrl,
19120
19164
  error: err.message
19121
19165
  });
19166
+ const fallbackText = `[\u{1F4CE} \u6587\u4EF6\u94FE\u63A5](${mediaUrl})`;
19122
19167
  await messagePipe.sendMessage({
19123
19168
  chatId,
19124
19169
  senderId,
19125
- msgType: "markdown",
19126
- content: JSON.stringify({ content: `\u{1F4CE} ${mediaUrl}` }),
19170
+ msgType: resolveTextMsgType(fallbackText),
19171
+ content: JSON.stringify({ content: fallbackText }),
19127
19172
  replyToMessageId,
19128
19173
  skipEncrypt: shouldSkipEncrypt
19129
19174
  });
@@ -19247,7 +19292,7 @@ var InboundPipeline = class {
19247
19292
  await this.deps.messagePipe.sendMessage({
19248
19293
  chatId: msg.chatId,
19249
19294
  senderId: msg.senderId,
19250
- msgType: "markdown",
19295
+ msgType: resolveTextMsgType(text),
19251
19296
  content: JSON.stringify({ content: text }),
19252
19297
  skipEncrypt: cmdSkipEncrypt
19253
19298
  });
@@ -19291,7 +19336,7 @@ var InboundPipeline = class {
19291
19336
  await this.deps.messagePipe.sendMessage({
19292
19337
  chatId: msg.chatId,
19293
19338
  senderId: msg.senderId,
19294
- msgType: "markdown",
19339
+ msgType: resolveTextMsgType(fallbackText),
19295
19340
  content: JSON.stringify({ content: fallbackText }),
19296
19341
  skipEncrypt: cmdSkipEncrypt
19297
19342
  }).catch((err) => {
@@ -21305,7 +21350,7 @@ var MessagePipe = class _MessagePipe {
21305
21350
  msgUid: callbackData.msgUid,
21306
21351
  reason
21307
21352
  });
21308
- const hintText = this.cryptoIsPassthrough ? "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002" : HINT_NO_QUANTUM_ACCOUNT;
21353
+ const hintText = !this.quantumAccount ? HINT_NO_QUANTUM_ACCOUNT : "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
21309
21354
  await this.sendHintMessage(callbackData, hintText);
21310
21355
  return;
21311
21356
  }
@@ -21359,7 +21404,7 @@ var MessagePipe = class _MessagePipe {
21359
21404
  await this.sendMessage({
21360
21405
  chatId,
21361
21406
  senderId,
21362
- msgType: "markdown",
21407
+ msgType: resolveTextMsgType(hintText),
21363
21408
  content: JSON.stringify({ content: hintText }),
21364
21409
  skipEncrypt: true
21365
21410
  });
@@ -21424,7 +21469,9 @@ var ConnectionManager = class {
21424
21469
  heartbeatTimeoutMs: options?.heartbeatTimeoutMs ?? 1e4,
21425
21470
  reconnectBaseMs: options?.reconnectBaseMs ?? 1e3,
21426
21471
  reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
21427
- maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
21472
+ maxReconnectAttempts: options?.maxReconnectAttempts ?? 10,
21473
+ tailBackoffDelays: options?.tailBackoffDelays ?? [12e4, 24e4, 48e4],
21474
+ persistentRetryIntervalMs: options?.persistentRetryIntervalMs ?? 3e5
21428
21475
  };
21429
21476
  log26.info("ConnectionManager initialized", this.options);
21430
21477
  }
@@ -21535,14 +21582,46 @@ var ConnectionManager = class {
21535
21582
  this.reconnecting = false;
21536
21583
  });
21537
21584
  }
21538
- /** 指数退避重连 */
21585
+ /**
21586
+ * 计算当前重连延迟 — 三阶段退避策略:
21587
+ *
21588
+ * Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
21589
+ * Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
21590
+ * Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
21591
+ */
21592
+ computeReconnectDelay() {
21593
+ const { reconnectBaseMs, reconnectMaxMs, maxReconnectAttempts, tailBackoffDelays, persistentRetryIntervalMs } = this.options;
21594
+ const attempt = this.reconnectAttempts;
21595
+ if (attempt >= maxReconnectAttempts) {
21596
+ return { delay: persistentRetryIntervalMs, phase: "persistent" };
21597
+ }
21598
+ const tailStart = maxReconnectAttempts - tailBackoffDelays.length;
21599
+ if (attempt >= tailStart) {
21600
+ return { delay: tailBackoffDelays[attempt - tailStart], phase: "tail" };
21601
+ }
21602
+ return {
21603
+ delay: Math.min(reconnectBaseMs * 2 ** attempt, reconnectMaxMs),
21604
+ phase: "exponential"
21605
+ };
21606
+ }
21607
+ /** 三阶段退避重连 — 永不放弃 */
21539
21608
  async reconnect() {
21540
- while (this.running && this.reconnectAttempts < this.options.maxReconnectAttempts) {
21541
- const delay = Math.min(
21542
- this.options.reconnectBaseMs * 2 ** this.reconnectAttempts,
21543
- this.options.reconnectMaxMs
21544
- );
21545
- log26.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
21609
+ while (this.running) {
21610
+ const { delay, phase } = this.computeReconnectDelay();
21611
+ const isPersistent = phase === "persistent";
21612
+ if (isPersistent) {
21613
+ log26.warn("ws:persistent-retry", {
21614
+ attempt: this.reconnectAttempts + 1,
21615
+ delayMs: delay,
21616
+ message: "\u5DF2\u8D85\u8FC7\u6700\u5927\u91CD\u8FDE\u6B21\u6570\uFF0C\u8FDB\u5165\u6301\u7EED\u91CD\u8BD5\u6A21\u5F0F (\u6BCF 5 \u5206\u949F)"
21617
+ });
21618
+ } else {
21619
+ log26.info("ws:reconnecting", {
21620
+ attempt: this.reconnectAttempts + 1,
21621
+ delayMs: delay,
21622
+ phase
21623
+ });
21624
+ }
21546
21625
  await sleep2(delay);
21547
21626
  if (!this.running) return;
21548
21627
  this.reconnectAttempts++;
@@ -21558,23 +21637,20 @@ var ConnectionManager = class {
21558
21637
  ...this.appId ? { "X-App-ID": this.appId } : {}
21559
21638
  }
21560
21639
  });
21640
+ const totalAttempts = this.reconnectAttempts;
21561
21641
  this.reconnectAttempts = 0;
21562
21642
  this.registerEvents();
21563
21643
  this.startHeartbeat();
21564
- log26.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
21644
+ log26.info("ws:reconnected", { totalAttempts, url: this.url });
21565
21645
  return;
21566
21646
  } catch (err) {
21567
21647
  log26.warn("ws:reconnect-failed", {
21568
21648
  attempt: this.reconnectAttempts,
21649
+ phase,
21569
21650
  error: err.message
21570
21651
  });
21571
21652
  }
21572
21653
  }
21573
- if (this.running) {
21574
- log26.error("ws:max-reconnect-reached", {
21575
- maxAttempts: this.options.maxReconnectAttempts
21576
- });
21577
- }
21578
21654
  }
21579
21655
  /** 停止连接 — 清理心跳定时器 + 关闭 WS */
21580
21656
  async stop() {
package/dist/index.d.cts CHANGED
@@ -65,8 +65,12 @@ interface InternalConfig {
65
65
  reconnectBaseMs: number;
66
66
  /** 🟡 重连最大退避 (ms) */
67
67
  reconnectMaxMs: number;
68
- /** 🟡 最大重连尝试次数 */
68
+ /** 🟡 最大重连尝试次数 (Phase 1 + Phase 2) */
69
69
  maxReconnectAttempts: number;
70
+ /** 🟡 尾部退避延迟序列 (ms),从 maxReconnectAttempts 末尾倒数 */
71
+ tailBackoffDelays: number[];
72
+ /** 🟡 持续重试间隔 (ms),超过 maxReconnectAttempts 后使用 */
73
+ persistentRetryIntervalMs: number;
70
74
  /** 🟡 消息去重配置 */
71
75
  dedup?: {
72
76
  ttlMs: number;
@@ -989,7 +993,11 @@ declare class MessagePipe {
989
993
  /**
990
994
  * openclaw-liangzimixin — 连接管理器
991
995
  *
992
- * 心跳保活 (30s) + 指数退避重连 (1s→60s, 最多 10 次)
996
+ * 心跳保活 (30s) + 三阶段退避重连:
997
+ * Phase 1: 指数退避 1s → 2s → 4s → ... → 60s (前 7 次)
998
+ * Phase 2: 尾部退避 120s → 240s → 480s (第 8~10 次)
999
+ * Phase 3: 持续重试 300s (5 分钟) 间隔,永不放弃
1000
+ *
993
1001
  * Wild-Goose 离线消息: 重连后网关自动重发 (Redis 缓存 7 天),
994
1002
  * 插件的 dedup.ts 会过滤重复消息。
995
1003
  */
@@ -1004,13 +1012,17 @@ interface ConnectionManagerOptions {
1004
1012
  reconnectBaseMs?: number;
1005
1013
  /** 重连最大退避 (ms),默认 60s */
1006
1014
  reconnectMaxMs?: number;
1007
- /** 最大重连尝试次数,默认 10 */
1015
+ /** Phase 1 最大重连尝试次数 (含 Phase 2 尾部),默认 10 */
1008
1016
  maxReconnectAttempts?: number;
1017
+ /** Phase 2 尾部退避延迟序列 (ms),从 maxReconnectAttempts 末尾倒数,默认 [120s, 240s, 480s] */
1018
+ tailBackoffDelays?: number[];
1019
+ /** Phase 3 持续重试间隔 (ms),超过 maxReconnectAttempts 后使用,默认 300s (5 分钟) */
1020
+ persistentRetryIntervalMs?: number;
1009
1021
  }
1010
1022
  /**
1011
1023
  * 连接管理器 — 管理 WebSocket 的生命周期。
1012
1024
  * - 心跳保活: 每 30s 检查连接状态
1013
- * - 指数退避重连: 1s2s4s → ... → 60s,最多 10 次
1025
+ * - 三阶段重连: 指数退避尾部退避持续重试 (永不放弃)
1014
1026
  */
1015
1027
  declare class ConnectionManager {
1016
1028
  private readonly client;
@@ -1051,7 +1063,15 @@ declare class ConnectionManager {
1051
1063
  private stopHeartbeat;
1052
1064
  /** 调度重连 (防止并发重连) */
1053
1065
  private scheduleReconnect;
1054
- /** 指数退避重连 */
1066
+ /**
1067
+ * 计算当前重连延迟 — 三阶段退避策略:
1068
+ *
1069
+ * Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
1070
+ * Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
1071
+ * Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
1072
+ */
1073
+ private computeReconnectDelay;
1074
+ /** 三阶段退避重连 — 永不放弃 */
1055
1075
  private reconnect;
1056
1076
  /** 停止连接 — 清理心跳定时器 + 关闭 WS */
1057
1077
  stop(): Promise<void>;
@@ -4727,6 +4727,48 @@ var TTLMap = class {
4727
4727
  }
4728
4728
  };
4729
4729
 
4730
+ // src/utils/msg-type-detect.ts
4731
+ var MARKDOWN_PATTERNS = [
4732
+ // ── 块级元素 (高置信度) ──
4733
+ // 围栏代码块: ```language
4734
+ { pattern: /^```/m, label: "fenced-code-block" },
4735
+ // 标题: # Title (行首,# 后必须跟空格)
4736
+ { pattern: /^#{1,6}\s+\S/m, label: "heading" },
4737
+ // 表格: | cell | cell | (行首行尾都有 |,且至少有 2 个分隔符)
4738
+ { pattern: /^\|.+\|.+\|\s*$/m, label: "table" },
4739
+ // 引用块: > text (行首)
4740
+ { pattern: /^>\s+\S/m, label: "blockquote" },
4741
+ // 分隔线: --- 或 *** (独占一行,至少 3 个)
4742
+ { pattern: /^[-*]{3,}\s*$/m, label: "horizontal-rule" },
4743
+ // ── 内联元素 (中置信度) ──
4744
+ // 粗体: **text** 或 __text__
4745
+ { pattern: /\*\*[^*]+\*\*/, label: "bold-asterisk" },
4746
+ { pattern: /__[^_]+__/, label: "bold-underscore" },
4747
+ // 链接: [text](url)
4748
+ { pattern: /\[[^\]]+\]\([^)]+\)/, label: "link" },
4749
+ // 图片: ![alt](url)
4750
+ { pattern: /!\[[^\]]*\]\([^)]+\)/, label: "image" },
4751
+ // 行内代码: `code` (但不是 ```)
4752
+ { pattern: /(?<!`)`[^`\n]+`(?!`)/, label: "inline-code" },
4753
+ // ── 列表 (需行首匹配) ──
4754
+ // 有序列表: 1. text
4755
+ { pattern: /^\d+\.\s+\S/m, label: "ordered-list" },
4756
+ // 无序列表: - text 或 * text (行首,后跟空格和非空字符)
4757
+ { pattern: /^[-*]\s+\S/m, label: "unordered-list" }
4758
+ ];
4759
+ function isMarkdownContent(text) {
4760
+ if (!text) return false;
4761
+ for (const { pattern } of MARKDOWN_PATTERNS) {
4762
+ if (pattern.test(text)) {
4763
+ return true;
4764
+ }
4765
+ }
4766
+ return false;
4767
+ }
4768
+ function resolveTextMsgType(text) {
4769
+ return isMarkdownContent(text) ? "markdown" : "text";
4770
+ }
4771
+
4730
4772
  // src/channel/outbound.ts
4731
4773
  var log7 = createLogger("channel/outbound");
4732
4774
  var messageEncryptionStatus = new TTLMap({
@@ -4800,7 +4842,7 @@ var quantumImOutbound = {
4800
4842
  await messagePipe.sendMessage({
4801
4843
  chatId,
4802
4844
  senderId: chatId,
4803
- msgType: "markdown",
4845
+ msgType: resolveTextMsgType(text),
4804
4846
  content: JSON.stringify({ content: text }),
4805
4847
  skipEncrypt
4806
4848
  });
@@ -18690,6 +18732,8 @@ var DEFAULT_INTERNAL_CONFIG = {
18690
18732
  reconnectBaseMs: 1e3,
18691
18733
  reconnectMaxMs: 6e4,
18692
18734
  maxReconnectAttempts: 10,
18735
+ tailBackoffDelays: [12e4, 24e4, 48e4],
18736
+ persistentRetryIntervalMs: 3e5,
18693
18737
  dedup: {
18694
18738
  ttlMs: 3e5,
18695
18739
  maxEntries: 5e3
@@ -20227,7 +20271,7 @@ var MessagePipe = class _MessagePipe {
20227
20271
  msgUid: callbackData.msgUid,
20228
20272
  reason
20229
20273
  });
20230
- const hintText = this.cryptoIsPassthrough ? "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002" : HINT_NO_QUANTUM_ACCOUNT;
20274
+ const hintText = !this.quantumAccount ? HINT_NO_QUANTUM_ACCOUNT : "\u26A0\uFE0F \u91CF\u5B50\u52A0\u5BC6\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u65E0\u6CD5\u89E3\u5BC6\u52A0\u5BC6\u6D88\u606F\u3002\u8BF7\u53D1\u9001\u660E\u6587\u6D88\u606F\uFF0C\u6216\u7A0D\u540E\u91CD\u8BD5\u3002";
20231
20275
  await this.sendHintMessage(callbackData, hintText);
20232
20276
  return;
20233
20277
  }
@@ -20281,7 +20325,7 @@ var MessagePipe = class _MessagePipe {
20281
20325
  await this.sendMessage({
20282
20326
  chatId,
20283
20327
  senderId,
20284
- msgType: "markdown",
20328
+ msgType: resolveTextMsgType(hintText),
20285
20329
  content: JSON.stringify({ content: hintText }),
20286
20330
  skipEncrypt: true
20287
20331
  });
@@ -20346,7 +20390,9 @@ var ConnectionManager = class {
20346
20390
  heartbeatTimeoutMs: options?.heartbeatTimeoutMs ?? 1e4,
20347
20391
  reconnectBaseMs: options?.reconnectBaseMs ?? 1e3,
20348
20392
  reconnectMaxMs: options?.reconnectMaxMs ?? 6e4,
20349
- maxReconnectAttempts: options?.maxReconnectAttempts ?? 10
20393
+ maxReconnectAttempts: options?.maxReconnectAttempts ?? 10,
20394
+ tailBackoffDelays: options?.tailBackoffDelays ?? [12e4, 24e4, 48e4],
20395
+ persistentRetryIntervalMs: options?.persistentRetryIntervalMs ?? 3e5
20350
20396
  };
20351
20397
  log17.info("ConnectionManager initialized", this.options);
20352
20398
  }
@@ -20457,14 +20503,46 @@ var ConnectionManager = class {
20457
20503
  this.reconnecting = false;
20458
20504
  });
20459
20505
  }
20460
- /** 指数退避重连 */
20506
+ /**
20507
+ * 计算当前重连延迟 — 三阶段退避策略:
20508
+ *
20509
+ * Phase 1 (attempt 0 ~ tailStart-1): 指数退避 1s → 60s
20510
+ * Phase 2 (attempt tailStart ~ maxReconnectAttempts-1): 尾部退避 120s/240s/480s
20511
+ * Phase 3 (attempt >= maxReconnectAttempts): 持续重试 300s
20512
+ */
20513
+ computeReconnectDelay() {
20514
+ const { reconnectBaseMs, reconnectMaxMs, maxReconnectAttempts, tailBackoffDelays, persistentRetryIntervalMs } = this.options;
20515
+ const attempt = this.reconnectAttempts;
20516
+ if (attempt >= maxReconnectAttempts) {
20517
+ return { delay: persistentRetryIntervalMs, phase: "persistent" };
20518
+ }
20519
+ const tailStart = maxReconnectAttempts - tailBackoffDelays.length;
20520
+ if (attempt >= tailStart) {
20521
+ return { delay: tailBackoffDelays[attempt - tailStart], phase: "tail" };
20522
+ }
20523
+ return {
20524
+ delay: Math.min(reconnectBaseMs * 2 ** attempt, reconnectMaxMs),
20525
+ phase: "exponential"
20526
+ };
20527
+ }
20528
+ /** 三阶段退避重连 — 永不放弃 */
20461
20529
  async reconnect() {
20462
- while (this.running && this.reconnectAttempts < this.options.maxReconnectAttempts) {
20463
- const delay = Math.min(
20464
- this.options.reconnectBaseMs * 2 ** this.reconnectAttempts,
20465
- this.options.reconnectMaxMs
20466
- );
20467
- log17.info("ws:reconnecting", { attempt: this.reconnectAttempts + 1, delayMs: delay });
20530
+ while (this.running) {
20531
+ const { delay, phase } = this.computeReconnectDelay();
20532
+ const isPersistent = phase === "persistent";
20533
+ if (isPersistent) {
20534
+ log17.warn("ws:persistent-retry", {
20535
+ attempt: this.reconnectAttempts + 1,
20536
+ delayMs: delay,
20537
+ message: "\u5DF2\u8D85\u8FC7\u6700\u5927\u91CD\u8FDE\u6B21\u6570\uFF0C\u8FDB\u5165\u6301\u7EED\u91CD\u8BD5\u6A21\u5F0F (\u6BCF 5 \u5206\u949F)"
20538
+ });
20539
+ } else {
20540
+ log17.info("ws:reconnecting", {
20541
+ attempt: this.reconnectAttempts + 1,
20542
+ delayMs: delay,
20543
+ phase
20544
+ });
20545
+ }
20468
20546
  await sleep2(delay);
20469
20547
  if (!this.running) return;
20470
20548
  this.reconnectAttempts++;
@@ -20480,23 +20558,20 @@ var ConnectionManager = class {
20480
20558
  ...this.appId ? { "X-App-ID": this.appId } : {}
20481
20559
  }
20482
20560
  });
20561
+ const totalAttempts = this.reconnectAttempts;
20483
20562
  this.reconnectAttempts = 0;
20484
20563
  this.registerEvents();
20485
20564
  this.startHeartbeat();
20486
- log17.info("ws:reconnected", { attempt: this.reconnectAttempts, url: this.url });
20565
+ log17.info("ws:reconnected", { totalAttempts, url: this.url });
20487
20566
  return;
20488
20567
  } catch (err) {
20489
20568
  log17.warn("ws:reconnect-failed", {
20490
20569
  attempt: this.reconnectAttempts,
20570
+ phase,
20491
20571
  error: err.message
20492
20572
  });
20493
20573
  }
20494
20574
  }
20495
- if (this.running) {
20496
- log17.error("ws:max-reconnect-reached", {
20497
- maxAttempts: this.options.maxReconnectAttempts
20498
- });
20499
- }
20500
20575
  }
20501
20576
  /** 停止连接 — 清理心跳定时器 + 关闭 WS */
20502
20577
  async stop() {
@@ -21387,7 +21462,7 @@ function createQuantumImDeliverFn(deps) {
21387
21462
  await messagePipe.sendMessage({
21388
21463
  chatId,
21389
21464
  senderId,
21390
- msgType: "markdown",
21465
+ msgType: resolveTextMsgType(payload.text),
21391
21466
  content: JSON.stringify({ content: payload.text }),
21392
21467
  replyToMessageId,
21393
21468
  skipEncrypt: shouldSkipEncrypt
@@ -21426,11 +21501,12 @@ function createQuantumImDeliverFn(deps) {
21426
21501
  mediaUrl,
21427
21502
  error: err.message
21428
21503
  });
21504
+ const fallbackText = `[\u{1F4CE} \u6587\u4EF6\u94FE\u63A5](${mediaUrl})`;
21429
21505
  await messagePipe.sendMessage({
21430
21506
  chatId,
21431
21507
  senderId,
21432
- msgType: "markdown",
21433
- content: JSON.stringify({ content: `\u{1F4CE} ${mediaUrl}` }),
21508
+ msgType: resolveTextMsgType(fallbackText),
21509
+ content: JSON.stringify({ content: fallbackText }),
21434
21510
  replyToMessageId,
21435
21511
  skipEncrypt: shouldSkipEncrypt
21436
21512
  });
@@ -21542,7 +21618,7 @@ var InboundPipeline = class {
21542
21618
  await this.deps.messagePipe.sendMessage({
21543
21619
  chatId: msg.chatId,
21544
21620
  senderId: msg.senderId,
21545
- msgType: "markdown",
21621
+ msgType: resolveTextMsgType(text),
21546
21622
  content: JSON.stringify({ content: text }),
21547
21623
  skipEncrypt: cmdSkipEncrypt
21548
21624
  });
@@ -21586,7 +21662,7 @@ var InboundPipeline = class {
21586
21662
  await this.deps.messagePipe.sendMessage({
21587
21663
  chatId: msg.chatId,
21588
21664
  senderId: msg.senderId,
21589
- msgType: "markdown",
21665
+ msgType: resolveTextMsgType(fallbackText),
21590
21666
  content: JSON.stringify({ content: fallbackText }),
21591
21667
  skipEncrypt: cmdSkipEncrypt
21592
21668
  }).catch((err) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "liangzimixin",
3
- "version": "0.3.67",
3
+ "version": "0.3.68",
4
4
  "description": "Quantum-encrypted IM channel plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",