@53ai/53ai-openclaw 1.0.9 → 1.1.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/dist/index.esm.js CHANGED
@@ -36,6 +36,12 @@ const WS_RECONNECT_BASE_DELAY_MS = 1000;
36
36
  const TEXT_CHUNK_LIMIT = 4000;
37
37
  /** 消息处理超时(毫秒) */
38
38
  const MESSAGE_PROCESS_TIMEOUT_MS = 120000;
39
+ /**
40
+ * 消息缓存 TTL(毫秒)
41
+ * 设为 35 分钟,确保覆盖最大重连时间(约 28.5 分钟)+ 缓冲
42
+ * @see WS_MAX_RECONNECT_ATTEMPTS - 60 次,指数退避最大 30s
43
+ */
44
+ const MESSAGE_CACHE_TTL_MS = 35 * 60 * 1000;
39
45
  // ============================================================================
40
46
  // 媒体处理配置
41
47
  // ============================================================================
@@ -314,8 +320,9 @@ async function sendReply(params) {
314
320
  const reqId = replyToMsgId || streamId;
315
321
  runtime.log?.(`[53aihub] sendReply START: reqId=${reqId}, finish=${finish}, isError=${isError}, textLen=${text?.length || 0}, wsReadyState=${wsClient.readyState}`);
316
322
  if (wsClient.readyState !== 1) {
317
- runtime.error?.(`[53aihub] WebSocket is not open (readyState=${wsClient.readyState}). Cannot send message to ${toChatId}`);
318
- return;
323
+ const errorMsg = `WebSocket not connected (readyState=${wsClient.readyState}), cannot send message to ${toChatId}`;
324
+ runtime.error?.(`[53aihub] ${errorMsg}`);
325
+ throw new Error(`[53aihub] ${errorMsg}`);
319
326
  }
320
327
  if (isError) {
321
328
  runtime.error?.(`[53aihub] sendReply ERROR: reqId=${reqId}, code=${errorCode}, text=${text?.substring(0, 100)}`);
@@ -426,8 +433,9 @@ async function sendThinkingMessage(wsClient, text, msgId, streamId, runtime) {
426
433
  const wsState = wsClient.readyState;
427
434
  runtime?.log?.(`[53aihub] sendThinkingMessage CALLED: msgId=${msgId}, streamId=${streamId}, text=${text}, wsReadyState=${wsState}`);
428
435
  if (wsState !== 1) {
429
- runtime?.error?.(`[53aihub] sendThinkingMessage SKIPPED: WebSocket not ready (state=${wsState})`);
430
- return;
436
+ const errorMsg = `WebSocket not ready (state=${wsState})`;
437
+ runtime?.error?.(`[53aihub] sendThinkingMessage FAILED: ${errorMsg}`);
438
+ throw new Error(`[53aihub] ${errorMsg}`);
431
439
  }
432
440
  // 使用 OpenAI 兼容格式,确保 Go 后端能正确解析
433
441
  // 关键:req_id 必须使用原始消息的 msgId,这样 Go 后端才能关联请求和响应
@@ -662,7 +670,7 @@ async function warmupReqIdStore(accountId = "default", log) {
662
670
  const store = getOrCreateReqIdStore(accountId);
663
671
  return store.warmup((err) => log?.(`ReqId warmup error: ${String(err)}`));
664
672
  }
665
- async function cleanupAccount(accountId) {
673
+ async function cleanupAccount(accountId, clearCache = false) {
666
674
  const wsClient = wsClientInstances.get(accountId);
667
675
  if (wsClient) {
668
676
  try {
@@ -676,6 +684,10 @@ async function cleanupAccount(accountId) {
676
684
  const store = reqIdStores.get(accountId);
677
685
  if (store)
678
686
  await store.flush();
687
+ if (clearCache) {
688
+ const { clearAccountCache } = await Promise.resolve().then(function () { return messageCache; });
689
+ clearAccountCache(accountId);
690
+ }
679
691
  }
680
692
 
681
693
  /**
@@ -783,41 +795,288 @@ async function downloadAndSaveFiles(params) {
783
795
  return mediaList;
784
796
  }
785
797
 
786
- // 消息队列处理器 - 确保消息按顺序处理,避免竞态条件
798
+ const DEFAULT_CONFIG = {
799
+ maxEntries: 100,
800
+ ttlMs: MESSAGE_CACHE_TTL_MS,
801
+ maxRetries: 3,
802
+ };
803
+ /**
804
+ * 按 accountId 隔离的消息缓存管理器
805
+ */
806
+ class MessageCache {
807
+ cache = new Map();
808
+ config;
809
+ accountId;
810
+ runtime;
811
+ constructor(accountId, config = {}, runtime) {
812
+ this.accountId = accountId;
813
+ this.config = { ...DEFAULT_CONFIG, ...config };
814
+ this.runtime = runtime;
815
+ }
816
+ /**
817
+ * 生成唯一缓存 key
818
+ */
819
+ generateCacheKey(reqId) {
820
+ return `${this.accountId}:${reqId}:${Date.now()}:${Math.random().toString(36).slice(2, 8)}`;
821
+ }
822
+ /**
823
+ * 添加消息到缓存,返回缓存 key
824
+ */
825
+ add(params) {
826
+ this.prune();
827
+ if (this.cache.size >= this.config.maxEntries) {
828
+ // 优先淘汰非终态消息(finish=false 且 isError=false),保留终态消息
829
+ const entries = [...this.cache.entries()];
830
+ // 先找最老的非终态消息
831
+ const oldestNonFinal = entries
832
+ .filter(([, entry]) => !entry.finish && !entry.isError)
833
+ .sort((a, b) => a[1].createdAt - b[1].createdAt)[0];
834
+ if (oldestNonFinal) {
835
+ this.cache.delete(oldestNonFinal[0]);
836
+ this.runtime?.log?.(`[53aihub] MessageCache: evicted oldest non-final entry ${oldestNonFinal[0]}`);
837
+ }
838
+ else {
839
+ // 所有消息都是终态,才淘汰最老的
840
+ const oldest = entries.sort((a, b) => a[1].createdAt - b[1].createdAt)[0];
841
+ if (oldest) {
842
+ this.cache.delete(oldest[0]);
843
+ this.runtime?.log?.(`[53aihub] MessageCache: evicted oldest final entry ${oldest[0]} (all entries are final)`);
844
+ }
845
+ }
846
+ }
847
+ const cacheKey = this.generateCacheKey(params.reqId);
848
+ this.cache.set(cacheKey, {
849
+ cacheKey,
850
+ ...params,
851
+ createdAt: Date.now(),
852
+ retryCount: 0,
853
+ });
854
+ this.runtime?.log?.(`[53aihub] MessageCache: cached message reqId=${params.reqId}, cacheKey=${cacheKey}, cacheSize=${this.cache.size}`);
855
+ return cacheKey;
856
+ }
857
+ /**
858
+ * 获取所有待重试的消息,返回 cacheKey + entry
859
+ */
860
+ getPendingMessages() {
861
+ const now = Date.now();
862
+ const pending = [];
863
+ for (const [key, entry] of this.cache) {
864
+ if (now - entry.createdAt > this.config.ttlMs) {
865
+ this.cache.delete(key);
866
+ continue;
867
+ }
868
+ if (entry.retryCount >= this.config.maxRetries) {
869
+ this.cache.delete(key);
870
+ this.runtime?.log?.(`[53aihub] MessageCache: dropped message after max retries cacheKey=${key}`);
871
+ continue;
872
+ }
873
+ pending.push(entry);
874
+ }
875
+ return pending;
876
+ }
877
+ /**
878
+ * 按 cacheKey 标记消息为已发送
879
+ */
880
+ markSent(cacheKey) {
881
+ if (this.cache.has(cacheKey)) {
882
+ this.cache.delete(cacheKey);
883
+ this.runtime?.log?.(`[53aihub] MessageCache: marked as sent cacheKey=${cacheKey}`);
884
+ }
885
+ }
886
+ /**
887
+ * 按 cacheKey 增加重试计数
888
+ */
889
+ incrementRetry(cacheKey) {
890
+ const entry = this.cache.get(cacheKey);
891
+ if (entry) {
892
+ entry.retryCount++;
893
+ }
894
+ }
895
+ /**
896
+ * 清理过期条目
897
+ */
898
+ prune() {
899
+ const now = Date.now();
900
+ let pruned = 0;
901
+ for (const [key, entry] of this.cache) {
902
+ if (now - entry.createdAt > this.config.ttlMs) {
903
+ this.cache.delete(key);
904
+ pruned++;
905
+ }
906
+ }
907
+ if (pruned > 0) {
908
+ this.runtime?.log?.(`[53aihub] MessageCache: pruned ${pruned} expired entries`);
909
+ }
910
+ }
911
+ size() {
912
+ return this.cache.size;
913
+ }
914
+ clear() {
915
+ this.cache.clear();
916
+ }
917
+ }
918
+ // 按 accountId 隔离的缓存实例
919
+ const cacheInstances = new Map();
920
+ /**
921
+ * 获取或创建按账号隔离的缓存实例
922
+ */
923
+ function getMessageCache(accountId, runtime) {
924
+ let cache = cacheInstances.get(accountId);
925
+ if (!cache) {
926
+ cache = new MessageCache(accountId, {}, runtime);
927
+ cacheInstances.set(accountId, cache);
928
+ }
929
+ return cache;
930
+ }
931
+ /**
932
+ * 清理指定账号的缓存
933
+ */
934
+ function clearAccountCache(accountId) {
935
+ const cache = cacheInstances.get(accountId);
936
+ if (cache) {
937
+ cache.clear();
938
+ cacheInstances.delete(accountId);
939
+ }
940
+ }
941
+ /**
942
+ * 缓存感知的消息发送器
943
+ * 返回 [success, cacheKey?] - 成功时 cacheKey 为 undefined,失败时返回缓存 key
944
+ */
945
+ async function sendWithCache(accountId, params, sendFn) {
946
+ const { wsClient, replyToMsgId, streamId, text, toChatId, finish, isError, errorCode, errorDetails, isThinking, runtime } = params;
947
+ const reqId = replyToMsgId || streamId;
948
+ if (wsClient.readyState !== 1) {
949
+ const cache = getMessageCache(accountId, runtime);
950
+ const cacheKey = cache.add({
951
+ reqId,
952
+ toChatId,
953
+ text,
954
+ finish,
955
+ isError,
956
+ errorCode,
957
+ errorDetails,
958
+ isThinking,
959
+ });
960
+ runtime.error?.(`[53aihub] sendWithCache: WebSocket not ready, cached message cacheKey=${cacheKey}`);
961
+ return { success: false, cacheKey };
962
+ }
963
+ try {
964
+ await sendFn(params);
965
+ return { success: true };
966
+ }
967
+ catch (err) {
968
+ const cache = getMessageCache(accountId, runtime);
969
+ const cacheKey = cache.add({
970
+ reqId,
971
+ toChatId,
972
+ text,
973
+ finish,
974
+ isError,
975
+ errorCode,
976
+ errorDetails,
977
+ isThinking,
978
+ });
979
+ runtime.error?.(`[53aihub] sendWithCache: send failed, cached message cacheKey=${cacheKey}, error=${String(err)}`);
980
+ return { success: false, cacheKey };
981
+ }
982
+ }
983
+ /**
984
+ * 重放缓存的消息
985
+ */
986
+ async function replayCachedMessages(accountId, wsClient, runtime, sendFn) {
987
+ const cache = getMessageCache(accountId, runtime);
988
+ const pending = cache.getPendingMessages();
989
+ if (pending.length === 0) {
990
+ return 0;
991
+ }
992
+ runtime.log?.(`[53aihub] replayCachedMessages: replaying ${pending.length} cached messages for account=${accountId}`);
993
+ let successCount = 0;
994
+ for (const entry of pending) {
995
+ if (wsClient.readyState !== 1) {
996
+ runtime.error?.(`[53aihub] replayCachedMessages: WebSocket not ready, stopping replay`);
997
+ break;
998
+ }
999
+ try {
1000
+ await sendFn({
1001
+ wsClient,
1002
+ text: entry.text,
1003
+ toChatId: entry.toChatId,
1004
+ replyToMsgId: entry.reqId,
1005
+ runtime,
1006
+ finish: entry.finish,
1007
+ streamId: entry.reqId,
1008
+ isError: entry.isError,
1009
+ errorCode: entry.errorCode,
1010
+ errorDetails: entry.errorDetails,
1011
+ isThinking: entry.isThinking,
1012
+ });
1013
+ cache.markSent(entry.cacheKey);
1014
+ successCount++;
1015
+ await new Promise((resolve) => setTimeout(resolve, 100));
1016
+ }
1017
+ catch (err) {
1018
+ const newRetryCount = entry.retryCount + 1;
1019
+ cache.incrementRetry(entry.cacheKey);
1020
+ runtime.error?.(`[53aihub] replayCachedMessages: failed to send cacheKey=${entry.cacheKey}, retryCount=${newRetryCount}`);
1021
+ }
1022
+ }
1023
+ runtime.log?.(`[53aihub] replayCachedMessages: completed, ${successCount}/${pending.length} sent`);
1024
+ return successCount;
1025
+ }
1026
+
1027
+ var messageCache = /*#__PURE__*/Object.freeze({
1028
+ __proto__: null,
1029
+ clearAccountCache: clearAccountCache,
1030
+ getMessageCache: getMessageCache,
1031
+ replayCachedMessages: replayCachedMessages,
1032
+ sendWithCache: sendWithCache
1033
+ });
1034
+
1035
+ // 按 chatId 分队列的消息处理器 - 同一会话串行,不同会话并行
787
1036
  class MessageQueue {
788
- queue = [];
789
- processing = false;
1037
+ queues = new Map();
1038
+ processing = new Map();
790
1039
  runtime;
791
1040
  constructor(runtime) {
792
1041
  this.runtime = runtime;
793
1042
  }
794
- enqueue(task) {
795
- this.queue.push(task);
796
- this.process();
1043
+ enqueue(chatId, task) {
1044
+ if (!this.queues.has(chatId)) {
1045
+ this.queues.set(chatId, []);
1046
+ this.processing.set(chatId, false);
1047
+ }
1048
+ this.queues.get(chatId).push(task);
1049
+ this.process(chatId);
797
1050
  }
798
- async process() {
799
- if (this.processing || this.queue.length === 0)
1051
+ async process(chatId) {
1052
+ const queue = this.queues.get(chatId);
1053
+ const isProcessing = this.processing.get(chatId);
1054
+ if (!queue || isProcessing || queue.length === 0)
800
1055
  return;
801
- this.processing = true;
1056
+ this.processing.set(chatId, true);
802
1057
  try {
803
- const task = this.queue.shift();
1058
+ const task = queue.shift();
804
1059
  if (task) {
805
1060
  await task();
806
1061
  }
807
1062
  }
808
1063
  catch (err) {
809
- this.runtime.error?.(`[53aihub] MessageQueue error: ${String(err)}`);
1064
+ this.runtime.error?.(`[53aihub] MessageQueue error (chatId=${chatId}): ${String(err)}`);
810
1065
  }
811
1066
  finally {
812
- this.processing = false;
813
- // 继续处理队列中的下一条消息
814
- if (this.queue.length > 0) {
815
- this.process();
1067
+ this.processing.set(chatId, false);
1068
+ if (queue.length > 0) {
1069
+ this.process(chatId);
1070
+ }
1071
+ else {
1072
+ this.queues.delete(chatId);
1073
+ this.processing.delete(chatId);
816
1074
  }
817
1075
  }
818
1076
  }
819
1077
  clear() {
820
- this.queue = [];
1078
+ this.queues.clear();
1079
+ this.processing.clear();
821
1080
  }
822
1081
  }
823
1082
  function buildMessageContext(body, account, config, mediaList) {
@@ -882,20 +1141,26 @@ async function processMessage(params) {
882
1141
  runtime.log?.(`[53aihub] processMessage: chatId=${chatId}, msgId=${body.msgId}, text=${parsed.textParts.join(" ").substring(0, 50)}... images=${parsed.imageUrls.length} files=${parsed.fileUrls.length}`);
883
1142
  const core = getRuntime();
884
1143
  const streamId = `stream-${Date.now()}`;
1144
+ // 缓存感知的消息发送器 - 必须在所有 sendReply 调用前定义
1145
+ const cachedSend = async (sendParams) => {
1146
+ await sendWithCache(account.accountId, {
1147
+ ...sendParams,
1148
+ wsClient,
1149
+ runtime,
1150
+ }, sendReply);
1151
+ };
885
1152
  const accessResult = await checkAccessPolicy({
886
1153
  userId: body.userId,
887
1154
  account,
888
1155
  runtime,
889
1156
  });
890
1157
  if (!accessResult.allowed) {
891
- await sendReply({
892
- wsClient,
1158
+ await cachedSend({
893
1159
  text: accessResult.reason === "Pairing required"
894
1160
  ? "您尚未获得授权使用此机器人,请联系管理员进行审核。"
895
1161
  : `⚠️ 访问被拒绝: ${accessResult.reason || "未知原因"}`,
896
1162
  toChatId: chatId,
897
1163
  replyToMsgId: body.msgId,
898
- runtime,
899
1164
  finish: true,
900
1165
  streamId,
901
1166
  isError: true,
@@ -954,12 +1219,10 @@ async function processMessage(params) {
954
1219
  const errorMsg = payload.text || "Unknown error";
955
1220
  const errorCode = inferErrorCode(errorMsg);
956
1221
  runtime.error?.(`[53aihub] deliver ERROR: ${errorMsg}`);
957
- await sendReply({
958
- wsClient,
1222
+ await cachedSend({
959
1223
  text: `⚠️ ${errorMsg}`,
960
1224
  toChatId: chatId,
961
1225
  replyToMsgId: body.msgId,
962
- runtime,
963
1226
  finish: true,
964
1227
  streamId: state.streamId,
965
1228
  isError: true,
@@ -972,12 +1235,10 @@ async function processMessage(params) {
972
1235
  state.accumulatedText.startsWith("🧹 Compacting context");
973
1236
  if (isCompaction && info.kind !== "final") {
974
1237
  runtime.log?.(`[53aihub] deliver COMPACTION: text preview=${payload.text?.substring(0, 50)}...`);
975
- await sendReply({
976
- wsClient,
1238
+ await cachedSend({
977
1239
  text: payload.text || "",
978
1240
  toChatId: chatId,
979
1241
  replyToMsgId: body.msgId,
980
- runtime,
981
1242
  finish: false,
982
1243
  streamId: state.streamId,
983
1244
  isThinking: true,
@@ -987,12 +1248,10 @@ async function processMessage(params) {
987
1248
  state.accumulatedText += payload.text;
988
1249
  if (info.kind !== "final") {
989
1250
  runtime.log?.(`[53aihub] deliver STREAMING: accumulatedText preview=${state.accumulatedText.substring(0, 50)}...`);
990
- await sendReply({
991
- wsClient,
1251
+ await cachedSend({
992
1252
  text: state.accumulatedText,
993
1253
  toChatId: chatId,
994
1254
  replyToMsgId: body.msgId,
995
- runtime,
996
1255
  finish: false,
997
1256
  streamId: state.streamId,
998
1257
  });
@@ -1002,47 +1261,36 @@ async function processMessage(params) {
1002
1261
  runtime.error?.(`[53aihub] onError: kind=${info.kind}, error=${String(err)}`);
1003
1262
  const errorText = String(err);
1004
1263
  const errorCode = inferErrorCode(errorText);
1005
- try {
1006
- await sendReply({
1007
- wsClient,
1008
- text: `⚠️ 系统错误: ${errorText}`,
1009
- toChatId: chatId,
1010
- replyToMsgId: body.msgId,
1011
- runtime,
1012
- finish: true,
1013
- streamId: state.streamId,
1014
- isError: true,
1015
- errorCode,
1016
- errorDetails: `kind=${info.kind}, error=${errorText}`,
1017
- });
1018
- }
1019
- catch (sendErr) {
1020
- runtime.error?.(`[53aihub] Failed to send error notification: ${String(sendErr)}`);
1021
- }
1264
+ await cachedSend({
1265
+ text: `⚠️ 系统错误: ${errorText}`,
1266
+ toChatId: chatId,
1267
+ replyToMsgId: body.msgId,
1268
+ finish: true,
1269
+ streamId: state.streamId,
1270
+ isError: true,
1271
+ errorCode,
1272
+ errorDetails: `kind=${info.kind}, error=${errorText}`,
1273
+ });
1022
1274
  },
1023
1275
  },
1024
1276
  }), MESSAGE_PROCESS_TIMEOUT_MS, `Message processing timed out (msgId=${body.msgId})`);
1025
1277
  runtime.log?.(`[53aihub] processMessage: dispatchReply completed, accumulatedTextLen=${state.accumulatedText.length}`);
1026
1278
  if (state.accumulatedText) {
1027
1279
  runtime.log?.(`[53aihub] processMessage: Sending final reply with accumulatedText`);
1028
- await sendReply({
1029
- wsClient,
1280
+ await cachedSend({
1030
1281
  text: state.accumulatedText,
1031
1282
  toChatId: chatId,
1032
1283
  replyToMsgId: body.msgId,
1033
- runtime,
1034
1284
  finish: true,
1035
1285
  streamId: state.streamId,
1036
1286
  });
1037
1287
  }
1038
1288
  else {
1039
1289
  runtime.log?.(`[53aihub] processMessage: No accumulatedText, sending empty final reply`);
1040
- await sendReply({
1041
- wsClient,
1290
+ await cachedSend({
1042
1291
  text: "",
1043
1292
  toChatId: chatId,
1044
1293
  replyToMsgId: body.msgId,
1045
- runtime,
1046
1294
  finish: true,
1047
1295
  streamId: state.streamId,
1048
1296
  });
@@ -1055,12 +1303,10 @@ async function processMessage(params) {
1055
1303
  const errorCode = inferErrorCode(errorText);
1056
1304
  if (!cleanedUp) {
1057
1305
  try {
1058
- await sendReply({
1059
- wsClient,
1306
+ await cachedSend({
1060
1307
  text: `⚠️ 处理请求时发生异常: ${errorText}`,
1061
1308
  toChatId: chatId,
1062
1309
  replyToMsgId: body.msgId,
1063
- runtime,
1064
1310
  finish: true,
1065
1311
  streamId: state.streamId,
1066
1312
  isError: true,
@@ -1085,17 +1331,6 @@ async function monitorProvider(options) {
1085
1331
  let pingInterval = null;
1086
1332
  let isAborted = false;
1087
1333
  let messageQueue = null;
1088
- const cleanup = async () => {
1089
- if (pingInterval) {
1090
- clearInterval(pingInterval);
1091
- pingInterval = null;
1092
- }
1093
- if (messageQueue) {
1094
- messageQueue.clear();
1095
- messageQueue = null;
1096
- }
1097
- await cleanupAccount(account.accountId);
1098
- };
1099
1334
  const connect = () => {
1100
1335
  if (isAborted)
1101
1336
  return;
@@ -1120,9 +1355,18 @@ async function monitorProvider(options) {
1120
1355
  setWebSocket(account.accountId, wsClient);
1121
1356
  // 初始化消息队列
1122
1357
  messageQueue = new MessageQueue(runtime);
1123
- wsClient.on("open", () => {
1358
+ wsClient.on("open", async () => {
1124
1359
  runtime.log?.(`[${account.accountId}] WebSocket connected successfully`);
1125
1360
  reconnectAttempts = 0;
1361
+ try {
1362
+ const replayedCount = await replayCachedMessages(account.accountId, wsClient, runtime, sendReply);
1363
+ if (replayedCount > 0) {
1364
+ runtime.log?.(`[${account.accountId}] Replayed ${replayedCount} cached messages`);
1365
+ }
1366
+ }
1367
+ catch (err) {
1368
+ runtime.error?.(`[${account.accountId}] Failed to replay cached messages: ${String(err)}`);
1369
+ }
1126
1370
  pingInterval = setInterval(() => {
1127
1371
  if (wsClient?.readyState === WebSocket.OPEN) {
1128
1372
  wsClient.ping();
@@ -1132,8 +1376,20 @@ async function monitorProvider(options) {
1132
1376
  wsClient.on("message", (data) => {
1133
1377
  const rawPayload = data.toString();
1134
1378
  runtime.log?.(`[${account.accountId}] Received WS message: ${rawPayload.substring(0, 200)}...`);
1135
- // 使用消息队列确保顺序处理,避免竞态条件
1136
- messageQueue?.enqueue(async () => {
1379
+ let chatId = "unknown";
1380
+ try {
1381
+ const msg = JSON.parse(rawPayload);
1382
+ if (msg.action === "chat") {
1383
+ chatId = msg.data?.conversation_id || msg.data?.user || "unknown";
1384
+ }
1385
+ else {
1386
+ chatId = msg.data?.chatId || msg.data?.userId || "unknown";
1387
+ }
1388
+ }
1389
+ catch {
1390
+ chatId = "unknown";
1391
+ }
1392
+ messageQueue?.enqueue(chatId, async () => {
1137
1393
  await processMessage({
1138
1394
  rawPayload,
1139
1395
  account,
@@ -1164,8 +1420,8 @@ async function monitorProvider(options) {
1164
1420
  setTimeout(connect, backoff);
1165
1421
  }
1166
1422
  else if (!isAborted) {
1167
- runtime.error?.(`[${account.accountId}] Max reconnect attempts (${WS_MAX_RECONNECT_ATTEMPTS}) reached`);
1168
- await cleanup();
1423
+ runtime.error?.(`[${account.accountId}] Max reconnect attempts (${WS_MAX_RECONNECT_ATTEMPTS}) reached, preserving cache for external recovery`);
1424
+ await cleanupAccount(account.accountId, false);
1169
1425
  reject(new Error(`Max reconnect attempts (${WS_MAX_RECONNECT_ATTEMPTS}) reached`));
1170
1426
  }
1171
1427
  });
@@ -1173,15 +1429,23 @@ async function monitorProvider(options) {
1173
1429
  if (abortSignal) {
1174
1430
  abortSignal.addEventListener("abort", async () => {
1175
1431
  isAborted = true;
1176
- await cleanup();
1432
+ if (pingInterval) {
1433
+ clearInterval(pingInterval);
1434
+ pingInterval = null;
1435
+ }
1436
+ if (messageQueue) {
1437
+ messageQueue.clear();
1438
+ messageQueue = null;
1439
+ }
1440
+ await cleanupAccount(account.accountId, true);
1177
1441
  resolve();
1178
1442
  });
1179
1443
  }
1180
1444
  warmupReqIdStore(account.accountId, (msg) => runtime.log?.(msg))
1181
1445
  .then(() => connect())
1182
1446
  .catch(async (err) => {
1183
- runtime.error?.(`[${account.accountId}] Failed to warmup ReqId store: ${String(err)}`);
1184
- await cleanup();
1447
+ runtime.error?.(`[${account.accountId}] Failed to warmup ReqId store: ${String(err)}, preserving cache for retry`);
1448
+ await cleanupAccount(account.accountId, false);
1185
1449
  reject(err);
1186
1450
  });
1187
1451
  });