dsh-mobile-plugin 0.5.5 → 0.5.6

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.
@@ -37,6 +37,7 @@ function collectTurnImages(entries, lookbackEntries) {
37
37
  return byTurn;
38
38
  }
39
39
  const {
40
+ reasoningText,
40
41
  reasoningStepSummary,
41
42
  toolStepSummary,
42
43
  toolStepState,
@@ -516,7 +517,8 @@ function deriveMessages(events, sessionId, lookbackEvents) {
516
517
  content.forEach((raw, index) => {
517
518
  if (!raw || typeof raw !== 'object') return;
518
519
  if (raw.type === 'reasoning') {
519
- const text = typeof raw.text === 'string' ? raw.text : '';
520
+ // 签名块先剥掉再判空:只剩签名的那一段等于没有推理,不算一步(桌面那边是整行不显示)。
521
+ const text = reasoningText(raw.text);
520
522
  // 空推理块(只有零宽字符那种)不算一步:App 点开一条什么都没有的行没有意义。
521
523
  if (text.trim() === '') return;
522
524
  list.push({
@@ -23,7 +23,7 @@ const MAX_SESSIONS = 4;
23
23
  const TEXT_MAX = 200_000;
24
24
 
25
25
  const { liveStepTurnOf } = require('./session-cache.js');
26
- const { reasoningStepSummary, toolStepSummary } = require('./step-index.js');
26
+ const { reasoningText, reasoningStepSummary, toolStepSummary } = require('./step-index.js');
27
27
 
28
28
  function bucketOf(ctx) {
29
29
  return liveStepTurnOf(ctx);
@@ -328,8 +328,9 @@ function liveStepOf(ctx, sessionId, query) {
328
328
  if (query && query.kind === 'think') {
329
329
  const index = Number.isSafeInteger(query.index) ? query.index : null;
330
330
  if (index === null) return null;
331
- const text = turn.thoughts.get(index);
332
- if (typeof text !== 'string' || text === '') return null;
331
+ // 签名块在展示前剥掉(与桌面同口径);剥完没有正文,这一段思考就当还没有内容。
332
+ const text = reasoningText(turn.thoughts.get(index));
333
+ if (text === '') return null;
333
334
  return { kind: 'think', text, live: true, state: turn.ended ? 'ok' : 'running' };
334
335
  }
335
336
  const callId = query && typeof query.callId === 'string' ? query.callId : '';
@@ -20,7 +20,7 @@ const { sendJson } = require('../shared/http-util');
20
20
  const { unwrap } = require('./envelope.js');
21
21
  const { callSessions } = require('./rpc.js');
22
22
  const { liveStepOf } = require('./live-steps.js');
23
- const { textOfParts } = require('./step-index.js');
23
+ const { reasoningText, textOfParts } = require('./step-index.js');
24
24
 
25
25
  /** 详情单段文本的上限(参数 / 输出 / 思考全文各自适用)。 */
26
26
  const DETAIL_MAX_CHARS = 200_000;
@@ -106,7 +106,9 @@ async function historyStep(ctx, { sessionId, kind, callId, seq, index, resultSeq
106
106
  const message = target && target.data && target.data.message;
107
107
  const content = message && Array.isArray(message.content) ? message.content : [];
108
108
  const block = content[index];
109
- const text = block && block.type === 'reasoning' && typeof block.text === 'string' ? block.text : '';
109
+ // 签名块(`<signature>…</signature>`)先剥掉再判断有没有正文:宿主数据里那段还在,
110
+ // 桌面的剥法是渲染层的,这里不跟着剥就会把一整段签名原文摊给用户看。
111
+ const text = block && block.type === 'reasoning' ? reasoningText(block.text) : '';
110
112
  if (!text) return null;
111
113
  const capped = capText(text);
112
114
  return { kind: 'think', state: 'ok', text: capped.text, ...(capped.truncated ? { truncated: true } : {}) };
@@ -24,9 +24,34 @@ function clampSummary(text, max = STEP_SUMMARY_MAX) {
24
24
  return `${value.slice(0, max - 1)}…`;
25
25
  }
26
26
 
27
+ /**
28
+ * 推理正文里模型带的签名块(`<signature>…</signature>`)在展示前剥掉。
29
+ *
30
+ * 桌面从 0.2.1-rc.5 起在渲染层做同一件事,但宿主存的会话数据里那段还在 —— 手机不从数据里
31
+ * 剥一遍,清单摘要那第一行就是一串 base64、展开详情整段噪声。流式期间只写到半截签名
32
+ * (`<sig`)时返回空串,不让它先闪一下再消失;剥完什么都不剩的块等于没有推理,调用方按空处理。
33
+ */
34
+ function reasoningText(text) {
35
+ const value = typeof text === 'string' ? text : '';
36
+ if (value[0] !== '<') return value;
37
+ const opening = '<signature>';
38
+ if (opening.startsWith(value)) return '';
39
+ if (!value.startsWith(opening)) return value;
40
+ const closing = '</signature>';
41
+ const end = value.indexOf(closing, opening.length);
42
+ if (end === -1) return '';
43
+ let start = end + closing.length;
44
+ while (start < value.length) {
45
+ if (value[start] === '\n') start += 1;
46
+ else if (value[start] === '\r' && value[start + 1] === '\n') start += 2;
47
+ else break;
48
+ }
49
+ return value.slice(start);
50
+ }
51
+
27
52
  /** 推理正文的一行摘要。 */
28
53
  function reasoningStepSummary(text) {
29
- return clampSummary(firstLine(text));
54
+ return clampSummary(firstLine(reasoningText(text)));
30
55
  }
31
56
 
32
57
  /** 参数 JSON → 对象;解析不了就返回空对象(畸形参数不该让清单少一条)。 */
@@ -100,6 +125,7 @@ module.exports = {
100
125
  STEP_SUMMARY_MAX,
101
126
  firstLine,
102
127
  clampSummary,
128
+ reasoningText,
103
129
  reasoningStepSummary,
104
130
  parseArgs,
105
131
  toolStepSummary,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-mobile-plugin",
3
- "version": "0.5.5",
3
+ "version": "0.5.6",
4
4
  "description": "Mobile API (/api/mobile/v1) and relay client for the DSH mobile app — desktop profile build (dsh 0.2.x, peers resolved by the host)",
5
5
  "license": "MIT",
6
6
  "icon": "./icon.png",