@xmanrui/dsh-im 2.4.0 → 2.6.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.
Files changed (60) hide show
  1. package/README.en.md +2 -2
  2. package/README.md +2 -2
  3. package/lib/client.js +322 -109
  4. package/lib/index.js +218 -211
  5. package/package.json +1 -1
  6. package/plugin-src/client/channel-card-meta.js +36 -1
  7. package/plugin-src/client/channels/dingtalk/api.js +2 -0
  8. package/plugin-src/client/channels/dingtalk/index.js +9 -1
  9. package/plugin-src/client/channels/feishu/api.js +3 -0
  10. package/plugin-src/client/channels/feishu/index.js +9 -1
  11. package/plugin-src/client/channels/qq/api.js +2 -0
  12. package/plugin-src/client/channels/qq/index.js +9 -1
  13. package/plugin-src/client/channels/shared/token-api.js +2 -0
  14. package/plugin-src/client/channels/shared/token-channel.js +9 -1
  15. package/plugin-src/client/channels/wecom/api.js +2 -0
  16. package/plugin-src/client/channels/wecom/index.js +9 -1
  17. package/plugin-src/client/channels/weixin/api.js +2 -10
  18. package/plugin-src/client/channels/weixin/index.js +9 -5
  19. package/plugin-src/client/channels/whatsapp/api.js +2 -0
  20. package/plugin-src/client/channels/whatsapp/index.js +9 -1
  21. package/plugin-src/client/i18n.js +4 -0
  22. package/plugin-src/client/index.js +16 -2
  23. package/plugin-src/client/last-message-error.js +17 -0
  24. package/plugin-src/client/styles.js +5 -1
  25. package/plugin-src/host/channels/feishu/rpc.mjs +3 -0
  26. package/src/channels/dingtalk/dingtalk-api.mjs +102 -6
  27. package/src/channels/dingtalk/dingtalk-bridge.mjs +247 -38
  28. package/src/channels/dingtalk/dingtalk-card-stream.mjs +2 -2
  29. package/src/channels/dingtalk/dingtalk-controller.mjs +2 -0
  30. package/src/channels/discord/discord-api.mjs +35 -0
  31. package/src/channels/discord/discord-bridge.mjs +1 -0
  32. package/src/channels/discord/discord-runtime.mjs +26 -0
  33. package/src/channels/feishu/bridge.mjs +115 -69
  34. package/src/channels/feishu/multi-bot-controller.mjs +2 -0
  35. package/src/channels/qq/qq-bridge.mjs +83 -28
  36. package/src/channels/qq/qq-controller.mjs +2 -0
  37. package/src/channels/shared/harness-client.mjs +40 -4
  38. package/src/channels/shared/i18n-en/shared-a.mjs +77 -0
  39. package/src/channels/shared/message-failure.mjs +244 -0
  40. package/src/channels/shared/semantic/artifact-delivery.mjs +9 -2
  41. package/src/channels/shared/status-reaction.mjs +107 -0
  42. package/src/channels/shared/text-harness-bridge.mjs +123 -66
  43. package/src/channels/shared/token-bot-controller.mjs +2 -0
  44. package/src/channels/slack/manifest.mjs +1 -0
  45. package/src/channels/slack/slack-api.mjs +28 -0
  46. package/src/channels/slack/slack-bridge.mjs +5 -0
  47. package/src/channels/slack/slack-controller.mjs +2 -0
  48. package/src/channels/slack/slack-runtime.mjs +24 -0
  49. package/src/channels/telegram/telegram-api.mjs +9 -0
  50. package/src/channels/telegram/telegram-bridge.mjs +1 -0
  51. package/src/channels/telegram/telegram-runtime.mjs +20 -0
  52. package/src/channels/wecom/wecom-bridge.mjs +49 -15
  53. package/src/channels/wecom/wecom-controller.mjs +2 -0
  54. package/src/channels/weixin/weixin-api.mjs +47 -0
  55. package/src/channels/weixin/weixin-bridge.mjs +261 -43
  56. package/src/channels/weixin/weixin-controller.mjs +2 -15
  57. package/src/channels/weixin/weixin-runtime.mjs +1 -0
  58. package/src/channels/whatsapp/whatsapp-bridge.mjs +1 -0
  59. package/src/channels/whatsapp/whatsapp-controller.mjs +2 -0
  60. package/src/channels/whatsapp/whatsapp-runtime.mjs +36 -0
@@ -29,6 +29,7 @@ import {
29
29
  import {
30
30
  fetchImageBuffer,
31
31
  hasInboundImages,
32
+ imagePromptDiagnostic,
32
33
  imagePromptUserMessage,
33
34
  promptContentForMessage,
34
35
  } from '../shared/image-prompt.mjs';
@@ -45,6 +46,12 @@ import {
45
46
  createDeliveryReceipt,
46
47
  providerMessageIdsFor,
47
48
  } from '../shared/semantic/delivery.mjs';
49
+ import {
50
+ channelDeliveryFailure,
51
+ clearLastMessageFailure,
52
+ messageFailureText,
53
+ setLastMessageFailure,
54
+ } from '../shared/message-failure.mjs';
48
55
  import { sendMarkdownReply } from './markdown-reply.mjs';
49
56
  import { t } from '../shared/i18n.mjs';
50
57
 
@@ -216,9 +223,15 @@ function answerTextForDelivery(answer, artifacts) {
216
223
  }
217
224
 
218
225
  function qqArtifactError(error, { dispatched = false } = {}) {
219
- if (error?.code?.startsWith?.('artifact-') || error?.name === 'UploadDailyLimitExceededError') {
226
+ if (error?.code?.startsWith?.('artifact-')) {
220
227
  return error;
221
228
  }
229
+ if (error?.name === 'UploadDailyLimitExceededError') {
230
+ const wrapped = new Error('QQ daily file upload limit exceeded', { cause: error });
231
+ wrapped.name = error.name;
232
+ wrapped.code = 'artifact-rate-limited';
233
+ return wrapped;
234
+ }
222
235
  const status = Number(error?.httpStatus);
223
236
  const wrapped = new Error('QQ file delivery failed', { cause: error });
224
237
  if (status === 401 || status === 403) wrapped.code = 'artifact-permission-required';
@@ -341,6 +354,7 @@ export function createQqBridgeStatus() {
341
354
  lastReplyAt: null,
342
355
  lastRejectedAt: null,
343
356
  lastError: null,
357
+ lastMessageError: null,
344
358
  };
345
359
  }
346
360
 
@@ -470,8 +484,12 @@ export class QqHarnessBridge {
470
484
  ).catch((error) => {
471
485
  if (error?.code === 'turn-stopped' || this.#signal?.aborted) return;
472
486
  this.#status.lastError = error?.message ?? String(error);
473
- this.#logger.error?.('[dsh-im:qq] failed to process a command:', error);
474
- return this.#bot.sendText(message.replyTarget, t('消息处理失败,请稍后重试。'))
487
+ const failure = setLastMessageFailure(this.#status, error);
488
+ this.#logger.error?.(
489
+ `[dsh-im:qq] failed to process a command [${failure.referenceId}]:`,
490
+ error,
491
+ );
492
+ return this.#bot.sendText(message.replyTarget, messageFailureText(failure))
475
493
  .catch(() => undefined);
476
494
  }).finally(() => {
477
495
  this.#acceptedMessageIds.delete(messageId);
@@ -614,8 +632,12 @@ export class QqHarnessBridge {
614
632
  }).catch(async (error) => {
615
633
  if (this.#signal?.aborted) return;
616
634
  this.#status.lastError = error?.message ?? String(error);
617
- this.#logger.error?.('[dsh-im:qq] failed to process a batch input message:', error);
618
- await this.#bot.sendText(message.replyTarget, t('消息处理失败,请稍后重试。'))
635
+ const failure = setLastMessageFailure(this.#status, error);
636
+ this.#logger.error?.(
637
+ `[dsh-im:qq] failed to process a batch input message [${failure.referenceId}]:`,
638
+ error,
639
+ );
640
+ await this.#bot.sendText(message.replyTarget, messageFailureText(failure))
619
641
  .catch(() => undefined);
620
642
  }).finally(() => {
621
643
  this.#acceptedMessageIds.delete(messageId);
@@ -627,7 +649,7 @@ export class QqHarnessBridge {
627
649
 
628
650
  async #deliverArtifacts(target, replyTo, artifacts = [], baseReceipt = null) {
629
651
  if (artifacts.length === 0) {
630
- return { receipt: baseReceipt, failureNoticeVisible: false };
652
+ return { receipt: baseReceipt, failureNoticeVisible: false, artifactSendErrors: 0 };
631
653
  }
632
654
  const delivery = await deliverOutboundArtifacts({
633
655
  artifacts,
@@ -645,9 +667,13 @@ export class QqHarnessBridge {
645
667
  signal: this.#signal,
646
668
  timeoutMs: this.#fileUploadTimeoutMs,
647
669
  }),
648
- sendFailureNotice: (artifact, error) => this.#bot.sendText(
670
+ onFailure: (artifact, error) => setLastMessageFailure(this.#status, error, {
671
+ userMessage: artifactFailureText(artifact?.fileName, error),
672
+ reason: error?.code,
673
+ }),
674
+ sendFailureNotice: (_artifact, _error, failure) => this.#bot.sendText(
649
675
  target,
650
- artifactFailureText(artifact?.fileName, error),
676
+ messageFailureText(failure),
651
677
  ),
652
678
  logger: this.#logger,
653
679
  });
@@ -658,6 +684,7 @@ export class QqHarnessBridge {
658
684
  return {
659
685
  receipt: delivery.receipt,
660
686
  failureNoticeVisible: delivery.failureNoticeVisible,
687
+ artifactSendErrors: delivery.artifactSendErrors,
661
688
  };
662
689
  }
663
690
 
@@ -676,6 +703,12 @@ export class QqHarnessBridge {
676
703
  this.#status.messagesReceived += 1;
677
704
  this.#status.lastMessageAt = new Date().toISOString();
678
705
  }
706
+ let messageRecorded = alreadyRecorded;
707
+ const markMessageSeen = async () => {
708
+ if (messageRecorded) return;
709
+ await this.#state.markSeen(messageId);
710
+ messageRecorded = true;
711
+ };
679
712
  if (this.#ownerUserOpenid !== '*' && sender !== this.#ownerUserOpenid) {
680
713
  this.#status.messagesRejected += 1;
681
714
  this.#status.lastRejectedAt = new Date().toISOString();
@@ -694,25 +727,25 @@ export class QqHarnessBridge {
694
727
  try {
695
728
  if (!text && !hasImages && !hasFiles) {
696
729
  await this.#bot.sendText(target, t('目前支持文字、图片和文件消息。'));
697
- await this.#state.markSeen(messageId);
730
+ await markMessageSeen();
698
731
  return;
699
732
  }
700
733
  const command = text.toLowerCase();
701
734
  if (!hasImages && !hasFiles && command === '/help') {
702
735
  await this.#bot.sendText(target, helpText());
703
- await this.#state.markSeen(messageId);
736
+ await markMessageSeen();
704
737
  return;
705
738
  }
706
739
  if (!hasImages && !hasFiles && command === '/status') {
707
740
  await this.#harness.ensureRunning({ signal: this.#signal });
708
741
  await this.#bot.sendText(target, t('QQ 机器人与 DeepSeek Harness 连接正常。'));
709
- await this.#state.markSeen(messageId);
742
+ await markMessageSeen();
710
743
  return;
711
744
  }
712
745
  if (!hasImages && !hasFiles && command === '/new') {
713
746
  await this.#state.clearSession(key);
714
747
  await this.#bot.sendText(target, t('已开启新会话。请发送你的问题。'));
715
- await this.#state.markSeen(messageId);
748
+ await markMessageSeen();
716
749
  return;
717
750
  }
718
751
  const workspaceCommand = hasImages || hasFiles
@@ -722,7 +755,7 @@ export class QqHarnessBridge {
722
755
  for (const reply of workspaceCommand.messages ?? [workspaceCommand.message]) {
723
756
  await this.#bot.sendText(target, reply);
724
757
  }
725
- await this.#state.markSeen(messageId);
758
+ await markMessageSeen();
726
759
  return;
727
760
  }
728
761
  const compactCommand = hasImages || hasFiles
@@ -736,7 +769,7 @@ export class QqHarnessBridge {
736
769
  );
737
770
  if (compactCommand) {
738
771
  await this.#bot.sendText(target, compactCommand.message);
739
- await this.#state.markSeen(messageId);
772
+ await markMessageSeen();
740
773
  return;
741
774
  }
742
775
 
@@ -757,6 +790,9 @@ export class QqHarnessBridge {
757
790
  let answer;
758
791
  let artifacts = [];
759
792
  try {
793
+ // Persist consumption before handing the prompt to Harness. Provider
794
+ // redelivery after a failed error notice must never execute it twice.
795
+ await markMessageSeen();
760
796
  ({ answer, artifacts = [] } = await askInWorkspaceSession({
761
797
  harness: this.#harness,
762
798
  state: this.#state,
@@ -771,10 +807,13 @@ export class QqHarnessBridge {
771
807
  progressMode: 'all',
772
808
  onUpdate: (update) => {
773
809
  if (update.error) {
774
- const label = nonEmptyString(update.toolName)
775
- ? `Tool call ${update.toolName}` : 'Tool call';
776
- const text = `${label}\nError: ${update.error}`;
777
- toolErrors.push(text);
810
+ const name = (nonEmptyString(update.toolName) ?? t('工具'))
811
+ .replace(/[\r\n]+/gu, ' ')
812
+ .slice(0, 80);
813
+ toolErrors.push(t(
814
+ '工具调用「{name}」未成功,请检查工具配置或稍后重试。',
815
+ { name },
816
+ ));
778
817
  }
779
818
  },
780
819
  onInteraction: (interaction) => this.#handleInteraction(interaction, {
@@ -836,7 +875,7 @@ export class QqHarnessBridge {
836
875
  });
837
876
  }
838
877
  } catch (error) {
839
- textSendError = error;
878
+ textSendError = channelDeliveryFailure(error);
840
879
  this.#logger.warn?.('[dsh-im:qq] final text delivery failed; continuing with result files:', error);
841
880
  }
842
881
  const delivery = await this.#deliverArtifacts(target, messageId, artifacts, textReceipt);
@@ -846,10 +885,15 @@ export class QqHarnessBridge {
846
885
  if (textSendError && !artifactDispatched && !delivery.failureNoticeVisible) {
847
886
  throw textSendError;
848
887
  }
849
- await this.#state.markSeen(messageId);
888
+ if (textSendError && delivery.artifactSendErrors === 0) {
889
+ setLastMessageFailure(this.#status, textSendError);
890
+ }
850
891
  this.#status.messagesReplied += 1;
851
892
  this.#status.lastReplyAt = new Date().toISOString();
852
893
  this.#status.lastError = null;
894
+ if (!textSendError && delivery.artifactSendErrors === 0) {
895
+ clearLastMessageFailure(this.#status);
896
+ }
853
897
  return delivery.receipt;
854
898
  } catch (error) {
855
899
  let batchFailureMessage = null;
@@ -872,7 +916,7 @@ export class QqHarnessBridge {
872
916
  } catch (sendError) {
873
917
  this.#logger.warn?.('[dsh-im:qq] unable to announce a stopped QQ turn:', sendError);
874
918
  }
875
- await this.#state.markSeen(messageId);
919
+ await markMessageSeen();
876
920
  return;
877
921
  }
878
922
  try {
@@ -882,16 +926,23 @@ export class QqHarnessBridge {
882
926
  }
883
927
  if (this.#signal?.aborted) return;
884
928
  this.#status.lastError = error?.message ?? String(error);
885
- this.#logger.error?.('[dsh-im:qq] failed to process an inbound message:', error);
929
+ const userMessage = inboundFileUserMessage(error)
930
+ ?? imagePromptUserMessage(error);
931
+ const failure = setLastMessageFailure(this.#status, error, {
932
+ userMessage,
933
+ reason: imagePromptDiagnostic(error)?.reason,
934
+ });
935
+ this.#logger.error?.(
936
+ `[dsh-im:qq] failed to process an inbound message [${failure.referenceId}]:`,
937
+ error,
938
+ );
886
939
  try {
887
- const errorMessage = inboundFileUserMessage(error)
888
- ?? imagePromptUserMessage(error)
889
- ?? t('消息处理失败,请稍后重试。');
940
+ const errorMessage = messageFailureText(failure);
890
941
  await this.#bot.sendText(
891
942
  target,
892
943
  batchFailureMessage ? `${errorMessage}\n\n${batchFailureMessage}` : errorMessage,
893
944
  );
894
- await this.#state.markSeen(messageId);
945
+ await markMessageSeen();
895
946
  } catch (sendError) {
896
947
  this.#logger.error?.('[dsh-im:qq] failed to send the safe error reply:', sendError);
897
948
  }
@@ -1166,11 +1217,15 @@ export class QqHarnessBridge {
1166
1217
  async #handleInteractionFailure(message, messageId, error) {
1167
1218
  if (this.#signal?.aborted) return;
1168
1219
  this.#status.lastError = error?.message ?? String(error);
1169
- this.#logger.error?.('[dsh-im:qq] failed to process an interaction reply:', error);
1220
+ const failure = setLastMessageFailure(this.#status, error);
1221
+ this.#logger.error?.(
1222
+ `[dsh-im:qq] failed to process an interaction reply [${failure.referenceId}]:`,
1223
+ error,
1224
+ );
1170
1225
  if (!this.#state.hasSeen(messageId)) {
1171
1226
  await this.#state.markSeen(messageId).catch(() => undefined);
1172
1227
  }
1173
- await this.#bot.sendText(message.replyTarget, t('消息处理失败,请稍后重试。'))
1228
+ await this.#bot.sendText(message.replyTarget, messageFailureText(failure))
1174
1229
  .catch(() => undefined);
1175
1230
  }
1176
1231
  }
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
2
2
 
3
3
  import { connectionTestMessage } from '../shared/connection-test.mjs';
4
4
  import { t } from '../shared/i18n.mjs';
5
+ import { publicMessageFailure } from '../shared/message-failure.mjs';
5
6
  import { deriveQqBotIdentity, maskQqAppId } from './config-store.mjs';
6
7
 
7
8
  const ACTIVE_ATTEMPT_STATES = new Set(['starting', 'pending', 'refreshing', 'connecting']);
@@ -319,6 +320,7 @@ export class QqController {
319
320
  messagesReceived: runtimeStatus?.messagesReceived ?? 0,
320
321
  messagesReplied: runtimeStatus?.messagesReplied ?? 0,
321
322
  },
323
+ lastMessageError: publicMessageFailure(runtimeStatus?.lastMessageError),
322
324
  error: structuredClone(this.#errors.get(config.botId) ?? null),
323
325
  };
324
326
  });
@@ -532,6 +532,41 @@ export class HarnessInteractionError extends Error {
532
532
  }
533
533
  }
534
534
 
535
+ export class HarnessTurnError extends Error {
536
+ constructor(code, { reason, providerCode } = {}) {
537
+ super(`Harness turn failed (${code})`);
538
+ this.name = 'HarnessTurnError';
539
+ this.code = code;
540
+ this.promptAccepted = true;
541
+ if (reason && typeof reason === 'object') this.reason = reason;
542
+ if (typeof providerCode === 'string' && providerCode) this.providerCode = providerCode;
543
+ }
544
+ }
545
+
546
+ function harnessTurnError(reason) {
547
+ const kind = nonEmptyText(reason?.kind) ?? nonEmptyText(reason);
548
+ if (kind === 'error') {
549
+ const failure = reason?.error ?? reason?.failure;
550
+ return new HarnessTurnError('harness-turn-failed', {
551
+ reason,
552
+ providerCode: nonEmptyText(failure?.code) ?? undefined,
553
+ });
554
+ }
555
+ if (kind === 'max-tokens') return new HarnessTurnError('model-max-tokens', { reason });
556
+ if (kind === 'blocked') return new HarnessTurnError('turn-blocked', { reason });
557
+ if (['interrupted', 'stopped', 'cancelled', 'canceled'].includes(kind)) {
558
+ return new HarnessTurnError('turn-interrupted', { reason });
559
+ }
560
+ if (kind === 'aborted') return new HarnessTurnError('turn-aborted', { reason });
561
+ if (kind === 'completed') return new HarnessTurnError('model-empty-response', { reason });
562
+ return new HarnessTurnError('harness-turn-failed', { reason });
563
+ }
564
+
565
+ function harnessTurnSucceeded(reason) {
566
+ if (reason === null || reason === undefined) return true;
567
+ return (nonEmptyText(reason?.kind) ?? nonEmptyText(reason)) === 'completed';
568
+ }
569
+
535
570
  export class HarnessClient {
536
571
  #baseUrl;
537
572
  #workspace;
@@ -1282,6 +1317,9 @@ export class HarnessClient {
1282
1317
  }
1283
1318
  if (!tracker.finished) continue;
1284
1319
  turnFinished = true;
1320
+ if (!ownership?.stopRequested && !harnessTurnSucceeded(tracker.reason)) {
1321
+ throw harnessTurnError(tracker.reason);
1322
+ }
1285
1323
  // An accepted /stop revokes attachment delivery even when Harness
1286
1324
  // preserved a useful partial text answer for the existing UX.
1287
1325
  const artifactCount = ownership?.stopRequested
@@ -1292,11 +1330,9 @@ export class HarnessClient {
1292
1330
  }
1293
1331
  if (artifactCount > 0) return '';
1294
1332
  if (ownership?.stopRequested) throw turnStoppedError();
1295
- throw new Error(
1296
- `Harness turn ended without a text reply${tracker.reason ? ` (${JSON.stringify(tracker.reason)})` : ''}`,
1297
- );
1333
+ throw harnessTurnError(tracker.reason);
1298
1334
  }
1299
- throw new Error(`Harness reply timed out after ${Math.round(timeoutMs / 1_000)} seconds`);
1335
+ throw new HarnessTurnError('harness-reply-timeout');
1300
1336
  } catch (error) {
1301
1337
  // Once cancellation was accepted, transport/poll failures and timeouts
1302
1338
  // describe the convergence of that stop, not an unrelated ask failure.
@@ -27,6 +27,83 @@ export default {
27
27
  '结果文件「{name}」已生成,但当前渠道暂时未能发送,请稍后重试。':
28
28
  'The result file "{name}" was generated, but this channel could not send it right now. Please try again later.',
29
29
  '消息处理失败,请稍后重试。': 'Failed to process the message. Please try again later.',
30
+ '卡片已结束,请查看后续消息。': 'This card has ended. Please check the next message.',
31
+ '工具调用「{name}」未成功,请检查工具配置或稍后重试。': 'Tool call "{name}" did not succeed. Check the tool configuration or try again later.',
32
+ '无法连接处理服务,消息尚未提交。请确认 DeepSeek Harness 正在运行后重试。':
33
+ 'Could not connect to the processing service, so the message was not submitted. Make sure DeepSeek Harness is running, then try again.',
34
+ '处理服务响应超时,消息尚未开始处理。请稍后重试。':
35
+ 'The processing service timed out before the message started. Please try again later.',
36
+ '暂时无法确认任务状态,任务可能已经开始。请先等待或发送 /stop,不要立即重复提交。':
37
+ 'The task status could not be confirmed and the task may have started. Wait or send /stop before submitting it again.',
38
+ '消息提交结果未能确认,任务可能已经开始。请先等待或发送 /status 查看状态,不要立即重复提交。':
39
+ 'The message submission could not be confirmed and the task may have started. Wait or send /status before submitting it again.',
40
+ '暂时无法读取任务进度,任务可能仍在运行。请先等待或发送 /stop,不要立即重复提交。':
41
+ 'Task progress is temporarily unavailable and the task may still be running. Wait or send /stop before submitting it again.',
42
+ '处理服务拒绝了机器人连接。请管理员检查 Harness 地址、代理或访问配置后重试。':
43
+ 'The processing service rejected the bot connection. Ask an administrator to check the Harness address, proxy, or access settings.',
44
+ '机器人与 DeepSeek Harness 的接口不兼容。请管理员检查 Harness 地址并更新相关版本。':
45
+ 'The bot and DeepSeek Harness use incompatible APIs. Ask an administrator to check the Harness address and update the related components.',
46
+ 'DeepSeek Harness 暂时无法完成请求,请稍后重试。':
47
+ 'DeepSeek Harness could not complete the request. Please try again later.',
48
+ '等待模型回复超时,任务可能仍在运行。请先等待或发送 /stop,不要立即重复提交。':
49
+ 'Waiting for the model reply timed out and the task may still be running. Wait or send /stop before submitting it again.',
50
+ '模型凭据缺失或已失效。请管理员检查模型配置后重试。':
51
+ 'The model credentials are missing or invalid. Ask an administrator to check the model settings.',
52
+ '模型额度或余额不足,本次任务未完成。请管理员补充额度或切换模型后重试。':
53
+ 'The model quota or balance is insufficient. Ask an administrator to add quota or switch models, then try again.',
54
+ '模型服务正在限流,本次任务未完成。请稍后重试。':
55
+ 'The model service is rate-limiting requests. Please try again later.',
56
+ '当前会话内容超过模型上下文上限。请发送 /compact 或 /new 后重试。':
57
+ 'This conversation exceeds the model context limit. Send /compact or /new, then try again.',
58
+ '当前模型不存在或暂不可用。请发送 /models 查看并使用 /model 切换模型。':
59
+ 'The current model does not exist or is unavailable. Send /models and use /model to switch models.',
60
+ '当前模型不存在或不支持所选配置。请发送 /models,并使用 /model 重新选择。':
61
+ 'The current model does not exist or does not support the selected settings. Send /models and use /model to choose again.',
62
+ '当前模型不支持这类内容或所选配置。请调整内容、模型或推理等级后重试。':
63
+ 'The current model does not support this content or the selected settings. Adjust the content, model, or reasoning effort and try again.',
64
+ '当前模型不支持所选配置。请切换模型或推理等级后重试。':
65
+ 'The current model does not support the selected settings. Switch the model or reasoning effort, then try again.',
66
+ '模型服务响应超时,本次任务未完成。请稍后重试。':
67
+ 'The model service timed out and the task did not finish. Please try again later.',
68
+ '暂时无法连接模型服务,本次任务未完成。请稍后重试。':
69
+ 'The model service is temporarily unreachable and the task did not finish. Please try again later.',
70
+ '模型服务暂时异常,本次任务未完成。请稍后重试。':
71
+ 'The model service is temporarily unavailable and the task did not finish. Please try again later.',
72
+ '模型回复中断或格式异常,本次任务未完成。请重试。':
73
+ 'The model reply was interrupted or malformed, so the task did not finish. Please try again.',
74
+ '模型没有返回可显示的内容。请重试;若持续发生,请切换模型。':
75
+ 'The model returned no displayable content. Try again, or switch models if this keeps happening.',
76
+ '模型拒绝处理当前内容。请调整问题内容后重试。':
77
+ 'The model rejected the current content. Revise the request and try again.',
78
+ '模型达到输出长度上限,但没有生成可显示的结果。请缩小任务范围后重试。':
79
+ 'The model reached its output limit without producing a displayable result. Reduce the task scope and try again.',
80
+ '任务正在等待无法在当前渠道完成的操作。请在 DeepSeek Harness 中处理后再试。':
81
+ 'The task is waiting for an action that cannot be completed in this channel. Handle it in DeepSeek Harness, then try again.',
82
+ '任务被意外中断,本次未完成。请重试。':
83
+ 'The task was unexpectedly interrupted and did not finish. Please try again.',
84
+ '当前会话已不存在。请发送 /new 创建新会话后重试。':
85
+ 'The current Session no longer exists. Send /new to create one, then try again.',
86
+ '当前会话仍在处理上一项任务。请等待完成,或发送 /stop 后重试。':
87
+ 'The current Session is still processing the previous task. Wait for it to finish, or send /stop before trying again.',
88
+ '工作区或会话状态刚刚发生变化。请重新发送这条消息。':
89
+ 'The Workspace or Session state just changed. Please send this message again.',
90
+ '当前工作区不存在或暂不可用。请重新选择工作区后重试。':
91
+ 'The current Workspace does not exist or is unavailable. Select another Workspace and try again.',
92
+ '当前 Agent Preset 不存在或暂不可用。请发送 /presetlist 后重新选择。':
93
+ 'The current Agent Preset does not exist or is unavailable. Send /presetlist and select another one.',
94
+ '回复已经生成,但机器人没有发送权限。请联系管理员检查渠道权限或重新绑定机器人。':
95
+ 'The reply was generated, but the bot cannot send it. Ask an administrator to check channel permissions or reconnect the bot.',
96
+ '回复已经生成,但当前渠道正在限流,暂时无法发送。请稍后重试。':
97
+ 'The reply was generated, but this channel is rate-limiting messages. Please try again later.',
98
+ '回复发送结果未能确认。请先检查聊天内是否已经收到,不要立即重复提交。':
99
+ 'Reply delivery could not be confirmed. Check whether it already arrived before submitting the task again.',
100
+ '回复已经生成,但当前渠道暂时无法发送。请稍后重试。':
101
+ 'The reply was generated, but this channel could not send it. Please try again later.',
102
+ '当前消息包含无法处理的图片或文件。请调整后重新发送。':
103
+ 'This message contains an image or file that cannot be processed. Adjust it and send it again.',
104
+ '任务未完成,暂时无法确定原因。请重试;若持续发生,请将参考号提供给管理员。':
105
+ 'The task did not finish and the cause could not be determined. Try again; if it persists, give the reference ID to an administrator.',
106
+ '错误码:{code};参考号:{referenceId}': 'Error code: {code}; reference: {referenceId}',
30
107
  '{label}机器人': '{label} bot',
31
108
  '目前支持文字和图片消息。': 'Only text and image messages are supported at the moment.',
32
109
  '目前支持文字、图片和文件消息。':