@xmanrui/dsh-im 3.0.2 → 3.0.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xmanrui/dsh-im",
3
- "version": "3.0.2",
3
+ "version": "3.0.4",
4
4
  "description": "把九种 IM 机器人和公网 AI Office 接入本机 DeepSeek Harness。 Connect nine IM channels and a public AI Office to a local DeepSeek Harness.",
5
5
  "keywords": [
6
6
  "deepseek-harness",
@@ -245,8 +245,6 @@ const CSS = String.raw`
245
245
  .dim-panel .ddt-qrFrame, .dim-panel .ddt-countdown { width: min(270px, 100%); }
246
246
  @container (max-width: 680px) {
247
247
  .dim-panel .bxf-headingTools, .dim-panel .dxw-tools, .dim-panel .ddt-tools { gap: 6px; }
248
- .dim-panel .dim-botCardTop { flex-direction: column; align-items: stretch; }
249
- .dim-panel .dim-botHealthGroup { justify-items: start; }
250
248
  .dim-panel .dim-bindActions { gap: 6px; }
251
249
  .dim-panel .bxf-headingTools .dim-scanButton, .dim-panel .dxw-tools .dim-scanButton, .dim-panel .ddt-tools .dim-scanButton, .dim-panel .dim-credentialButton { gap: 5px; padding-inline: 8px; font-size: 12px; }
252
250
  .dim-panel .dim-actionIcon { width: 13px; height: 13px; flex-basis: 13px; }
@@ -270,10 +268,6 @@ const CSS = String.raw`
270
268
  .dim-rail { max-height: none; overflow: visible; padding-right: 1px; }
271
269
  .dim-channel { min-height: 48px; }
272
270
  }
273
- @media (max-width: 720px) {
274
- .dim-panel .dim-botCardTop { flex-direction: column; align-items: stretch; }
275
- .dim-panel .dim-botHealthGroup { justify-items: start; }
276
- }
277
271
  @media (max-width: 560px) {
278
272
  .dim-title { flex-direction: column; gap: 10px; }
279
273
  .dim-title p { white-space: normal; }
@@ -296,12 +296,23 @@ function splitUtf8(text, maxBytes = MAX_REPLY_BYTES) {
296
296
  return chunks;
297
297
  }
298
298
 
299
- function progressText(update) {
300
- if (update?.type === 'text') return update.text;
299
+ function thinkingProgressText(update) {
301
300
  if (update?.type === 'tool') return t('正在使用{name}…', { name: update.name });
302
301
  return update?.text;
303
302
  }
304
303
 
304
+ function streamContent(thinkingText, answerText = '', { finish = false } = {}) {
305
+ const thinking = String(thinkingText ?? '')
306
+ .replace(/<\/?think>/gi, '')
307
+ .trim();
308
+ const answer = String(answerText ?? '').trim();
309
+ if (!thinking) return answer;
310
+ const thinkBlock = finish || answer
311
+ ? `<think>${thinking}</think>`
312
+ : `<think>${thinking}`;
313
+ return answer ? `${thinkBlock}\n${answer}` : thinkBlock;
314
+ }
315
+
305
316
  function artifactFailureText(fileName, error) {
306
317
  const name = String(fileName ?? t('结果文件')).replace(/[\r\n]+/g, ' ').trim()
307
318
  || t('结果文件');
@@ -867,6 +878,8 @@ export class WecomHarnessBridge {
867
878
  const key = conversationKey(frame);
868
879
  let streamId = null;
869
880
  let streamStarted = false;
881
+ let streamThinkingText = t('正在思考中…');
882
+ let streamAnswerText = '';
870
883
  let batchSettled = batchSubmission === null;
871
884
  let promptRecorded = false;
872
885
  try {
@@ -920,7 +933,12 @@ export class WecomHarnessBridge {
920
933
 
921
934
  streamId = this.#generateReqId('stream');
922
935
  try {
923
- await this.#client.replyStream(frame, streamId, t('正在思考中…'), false);
936
+ await this.#client.replyStream(
937
+ frame,
938
+ streamId,
939
+ streamContent(streamThinkingText),
940
+ false,
941
+ );
924
942
  streamStarted = true;
925
943
  } catch (error) {
926
944
  this.#logger.warn?.('[dsh-im:wecom] unable to start a stream; using an active reply:', error);
@@ -945,8 +963,15 @@ export class WecomHarnessBridge {
945
963
  control: { owner: this, key },
946
964
  onUpdate: streamStarted && typeof this.#client.replyStreamNonBlocking === 'function'
947
965
  ? async (update) => {
948
- const progress = splitUtf8(progressText(update))[0];
949
- if (progress) await this.#client.replyStreamNonBlocking(frame, streamId, progress, false);
966
+ if (update?.type === 'text') {
967
+ streamAnswerText = update.text;
968
+ } else {
969
+ streamThinkingText = thinkingProgressText(update) || streamThinkingText;
970
+ }
971
+ const preview = splitUtf8(
972
+ streamContent(streamThinkingText, streamAnswerText),
973
+ )[0];
974
+ if (preview) await this.#client.replyStreamNonBlocking(frame, streamId, preview, false);
950
975
  }
951
976
  : undefined,
952
977
  onInteraction: (interaction) => this.#handleInteraction(interaction, {
@@ -966,18 +991,20 @@ export class WecomHarnessBridge {
966
991
 
967
992
  this.#signal?.throwIfAborted();
968
993
  const displayAnswer = answerTextForDelivery(answer, artifacts);
969
- const chunks = splitUtf8(displayAnswer);
994
+ const streamChunks = splitUtf8(
995
+ streamContent(streamThinkingText, displayAnswer, { finish: true }),
996
+ );
970
997
  let finalSent = false;
971
998
  let textReceipt = null;
972
999
  let textSendError = null;
973
1000
  try {
974
- if (streamStarted && chunks.length > 0) {
1001
+ if (streamStarted && streamChunks.length > 0) {
975
1002
  try {
976
1003
  const providerMessageIds = [];
977
- const streamed = await this.#client.replyStream(frame, streamId, chunks[0], true);
1004
+ const streamed = await this.#client.replyStream(frame, streamId, streamChunks[0], true);
978
1005
  const streamedMessageId = providerMessageId(streamed);
979
1006
  if (streamedMessageId) providerMessageIds.push(streamedMessageId);
980
- for (const chunk of chunks.slice(1)) {
1007
+ for (const chunk of streamChunks.slice(1)) {
981
1008
  const sent = await this.#client.sendMessage(
982
1009
  chatId,
983
1010
  { msgtype: 'markdown', markdown: { content: chunk } },
@@ -1040,7 +1067,12 @@ export class WecomHarnessBridge {
1040
1067
  }
1041
1068
  if (error?.code === 'turn-stopped') {
1042
1069
  if (streamStarted && streamId) {
1043
- await this.#client.replyStream(frame, streamId, t('已停止。'), true)
1070
+ await this.#client.replyStream(
1071
+ frame,
1072
+ streamId,
1073
+ streamContent(streamThinkingText, t('已停止。'), { finish: true }),
1074
+ true,
1075
+ )
1044
1076
  .catch(() => undefined);
1045
1077
  }
1046
1078
  if (!promptRecorded) await this.#state.markSeen(messageId);
@@ -1063,7 +1095,12 @@ export class WecomHarnessBridge {
1063
1095
  : errorText;
1064
1096
  try {
1065
1097
  if (streamStarted && streamId) {
1066
- await this.#client.replyStream(frame, streamId, visibleError, true);
1098
+ await this.#client.replyStream(
1099
+ frame,
1100
+ streamId,
1101
+ streamContent(streamThinkingText, visibleError, { finish: true }),
1102
+ true,
1103
+ );
1067
1104
  } else {
1068
1105
  await this.#sendImmediate(frame, chatId, visibleError);
1069
1106
  }