dsh-working-activity 0.3.2 → 0.4.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/README.i18n.yaml CHANGED
@@ -2,5 +2,5 @@
2
2
  # side as of the last confirmed-consistent state. Both languages carry equal authority;
3
3
  # after editing either side, bring the other along and re-record with:
4
4
  # pnpm run verify-translation-pairing --write packages/activity/working-activity/README.md
5
- README.md: bf0787caac11a292882ba1a35963878e373e7db8
6
- README.zh.md: de09a7945dbe42c85d359fd256cdac0264d4b6cd
5
+ README.md: 5ec920a3514e566f80d36b7952c1f9b6f9a06a5a
6
+ README.zh.md: 71f191268687d26345371877e54571950f17367b
package/README.md CHANGED
@@ -38,6 +38,10 @@ inserting a second same-id row:
38
38
  publishIntervalMs: 500
39
39
  ```
40
40
 
41
+ Host and TUI integration supports the DSH `0.1.0-rc.6` and `0.1.1-rc` lines,
42
+ plus `0.1.2-alpha.2`. The optional Web client still uses the rc client-runtime
43
+ contract and is not covered by alpha.2 compatibility.
44
+
41
45
  ## TUI usage
42
46
 
43
47
  Install the plugin into a profile that composes the official `dsh-tui`, then
package/README.zh.md CHANGED
@@ -27,6 +27,10 @@ plugins:
27
27
  name: '@deepseek-ai/dsh-working-activity'
28
28
  ```
29
29
 
30
+ Host 与 TUI 集成支持 DSH `0.1.0-rc.6`、`0.1.1-rc` 版本线和
31
+ `0.1.2-alpha.2`。可选 Web 客户端仍使用 rc 版 client-runtime 契约,不在
32
+ alpha.2 兼容范围内。
33
+
30
34
  ## TUI 用法
31
35
 
32
36
  与 `dsh-tui` 一起启用插件,并把槽位加进左侧 prompt 模板:
package/lib/client.js CHANGED
@@ -16,11 +16,11 @@ window.__ModuleLoader__.load({
16
16
  document.head.appendChild(tag);
17
17
  }
18
18
  var WorkingLine_module_css_default = {
19
- "working-pulse": "TG_uoa_working-pulse",
20
19
  "tools": "TG_uoa_tools",
21
20
  "line": "TG_uoa_line",
22
21
  "text": "TG_uoa_text",
23
- "marker": "TG_uoa_marker"
22
+ "marker": "TG_uoa_marker",
23
+ "working-pulse": "TG_uoa_working-pulse"
24
24
  };
25
25
  //#endregion
26
26
  //#region src/client/WorkingLine.tsx
@@ -62,6 +62,11 @@ declare const dict: {
62
62
  readonly zh: "{{count}} 工具";
63
63
  readonly en: "{{count}} tools";
64
64
  };
65
+ /** Consecutive-tool streak badge (replaces the old flame emoji). */
66
+ readonly 'tool-streak': {
67
+ readonly zh: "工具x{{count}}";
68
+ readonly en: "tool x{{count}}";
69
+ };
65
70
  /** Subagent count in the done summary. */
66
71
  readonly 'subagent-count': {
67
72
  readonly zh: "子代理 {{count}} 个";
@@ -1 +1 @@
1
- {"version":3,"file":"lang.d.ts","sourceRoot":"","sources":["../../src/lang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAK1C,QAAA,MAAM,IAAI;;;;;IAQR,wCAAwC;;;;;;;;;IAGxC,2DAA2D;;;;;IAE3D,mEAAmE;;;;;IAEnE,+BAA+B;;;;;;;;;;;;;IAO/B,0CAA0C;;;;;IAE1C,0DAA0D;;;;;CAElD,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAKxD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wEAAwE;AACxE,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,GAAG,SAAS,CAmB/C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
1
+ {"version":3,"file":"lang.d.ts","sourceRoot":"","sources":["../../src/lang.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAMH,MAAM,MAAM,IAAI,GAAG,IAAI,GAAG,IAAI,CAAA;AAE9B,+DAA+D;AAC/D,eAAO,MAAM,KAAK,uBAAwB,CAAA;AAK1C,QAAA,MAAM,IAAI;;;;;IAQR,wCAAwC;;;;;;;;;IAGxC,2DAA2D;;;;;IAE3D,mEAAmE;;;;;IAEnE,+BAA+B;;;;;;;;;;;;;IAO/B,oEAAoE;;;;;IAEpE,0CAA0C;;;;;IAE1C,0DAA0D;;;;;CAElD,CAAA;AAEV,MAAM,MAAM,OAAO,GAAG,MAAM,OAAO,IAAI,CAAA;AACvC,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,CAAA;AAKxD,2EAA2E;AAC3E,wBAAgB,eAAe,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,CAEzD;AAED,qCAAqC;AACrC,wBAAgB,OAAO,IAAI,IAAI,CAK9B;AAED,wEAAwE;AACxE,wBAAgB,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,GAAE,UAAe,GAAG,MAAM,CAM/D;AAED,gDAAgD;AAChD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,IAAI,CAEpD;AAED;;;;GAIG;AACH,wBAAgB,YAAY,IAAI,IAAI,GAAG,SAAS,CAmB/C;AAKD;;;;;GAKG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAWvC"}
package/lib/types/lang.js CHANGED
@@ -50,6 +50,8 @@ const dict = {
50
50
  },
51
51
  'tool-count-one': { zh: '{{count}} 工具', en: '{{count}} tool' },
52
52
  'tool-count-many': { zh: '{{count}} 工具', en: '{{count}} tools' },
53
+ /** Consecutive-tool streak badge (replaces the old flame emoji). */
54
+ 'tool-streak': { zh: '工具x{{count}}', en: 'tool x{{count}}' },
53
55
  /** Subagent count in the done summary. */
54
56
  'subagent-count': { zh: '子代理 {{count}} 个', en: '{{count}} subagents' },
55
57
  /** Work-reminder copy after `workRemindAt` turn-hours. */
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/status.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../src/status.ts"],"names":[],"mappings":"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"}
@@ -353,7 +353,7 @@ export class ActivityTracker {
353
353
  const git = tool.isGit
354
354
  ? (this.gitBranch !== undefined ? ` · git ${this.gitBranch}` : ' · git')
355
355
  : '';
356
- const combo = this.streak >= COMBO_SHOW_AT ? ` · 🔥x${this.streak}` : '';
356
+ const combo = this.streak >= COMBO_SHOW_AT ? ` · ${t('tool-streak', { count: this.streak })}` : '';
357
357
  const narration = this.freshNarration(nowMs);
358
358
  const line = narration === null
359
359
  ? `${fragment} · ${elapsed}${git}${combo}`
@@ -427,12 +427,12 @@ export class ActivityTracker {
427
427
  const phrase = this.previousPhrase ?? (this.phase === 'waiting'
428
428
  ? waitingPhrase()
429
429
  : this.livelyPhrase(thinkingMs, nowMs));
430
- // Ellipsis breathing (pi DOT_FRAMES) + optional estimated tps prefix.
431
- const dots = DOT_FRAMES[Math.floor(nowMs / TICK_MS) % DOT_FRAMES.length];
430
+ // The indicator animation (whale etc.) already signals activity, so the
431
+ // pi DOT_FRAMES ellipsis breathing is dropped for the DSH line.
432
432
  const tps = this.tpsPrefix(nowMs);
433
433
  return {
434
434
  phase: this.phase,
435
- line: `${tps}${phrase}${dots} · ${elapsedLine}`,
435
+ line: `${tps}${phrase} · ${elapsedLine}`,
436
436
  phrase,
437
437
  toolCount: this.toolCount,
438
438
  turnElapsedMs: this.turnElapsedMs(nowMs),
@@ -511,7 +511,7 @@ export class ActivityTracker {
511
511
  const { thinkingMs, toolMs, toolCount } = this.stats();
512
512
  const tokens = this.turnTokens > 0 ? ` · 🔥 ${fmtTokens(this.turnTokens)}` : '';
513
513
  const sub = this.subagentCount > 0 ? ` · ${t('subagent-count', { count: this.subagentCount })}` : '';
514
- const combo = this.maxStreak >= COMBO_SHOW_AT ? ` · 🔥x${this.maxStreak}` : '';
514
+ const combo = this.maxStreak >= COMBO_SHOW_AT ? ` · ${t('tool-streak', { count: this.maxStreak })}` : '';
515
515
  const tools = t(toolCount === 1 ? 'tool-count-one' : 'tool-count-many', { count: toolCount });
516
516
  const summary = t('done-summary', {
517
517
  tools,
@@ -564,10 +564,6 @@ const COMBO_GAP_MS = 10_000;
564
564
  const COMBO_SHOW_AT = 2;
565
565
  /** The tps estimate stays fresh this long after the last chunk. */
566
566
  const TPS_WINDOW_MS = 3500;
567
- /** Render tick cadence (matches the TUI's 500ms activity timer). */
568
- const TICK_MS = 500;
569
- /** Ellipsis breathing frames appended to thinking lines (pi parity). */
570
- const DOT_FRAMES = ['', ' ·', ' ··', ' ···', ' ··', ' ·'];
571
567
  /** Cap on replayed done cards; older entries drop. */
572
568
  const DONE_QUEUE_MAX = 6;
573
569
  /** Show the last tool's fragment in the done line for this long after it ends. */
@@ -576,15 +572,57 @@ const DONE_FRAGMENT_MS = 3000;
576
572
  const STREAM_BUFFER_CHARS = 300;
577
573
  /** A narration stays visible this long after the stream went quiet. */
578
574
  const NARRATE_GRACE_MS = 5000;
575
+ /** Narration budget in display columns: 40 CJK chars ≈ 12 English words. */
576
+ const NARRATION_MAX_COLUMNS = 80;
577
+ /** A CJK/full-width code point renders 2 terminal columns; the rest render 1. */
578
+ function isWide(cp) {
579
+ return ((cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo
580
+ (cp >= 0x2e80 && cp <= 0xa4cf) || // CJK radicals, kana, Yi
581
+ (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul syllables
582
+ (cp >= 0xf900 && cp <= 0xfaff) || // CJK compatibility ideographs
583
+ (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK compatibility forms
584
+ (cp >= 0xff00 && cp <= 0xff60) || // full-width forms
585
+ (cp >= 0xffe0 && cp <= 0xffe6) || // full-width signs
586
+ (cp >= 0x20000 && cp <= 0x3fffd) // CJK extensions
587
+ );
588
+ }
589
+ /** Cut before `max` columns, backing off to the last word or clause boundary. */
590
+ function cutToWidth(text, max) {
591
+ let width = 0;
592
+ let cut = text.length;
593
+ const chars = [...text];
594
+ for (let i = 0; i < chars.length; i++) {
595
+ const w = isWide(chars[i].codePointAt(0)) ? 2 : 1;
596
+ if (width + w > max) {
597
+ cut = chars.slice(0, i).join('').length;
598
+ break;
599
+ }
600
+ width += w;
601
+ }
602
+ if (cut === text.length)
603
+ return text;
604
+ // Back off to a soft boundary so English words survive intact; dense CJK has
605
+ // none and hard-cuts at the budget like the original fixed 40-char capture.
606
+ const head = text.slice(0, cut);
607
+ const soft = Math.max(head.lastIndexOf(' '), head.lastIndexOf('\t'), head.lastIndexOf(','), head.lastIndexOf('、'), head.lastIndexOf(';'), head.lastIndexOf(';'), head.lastIndexOf(','));
608
+ return soft > 0 ? head.slice(0, soft) : head;
609
+ }
579
610
  /** Extract the latest `⏵` self-narration line from a stream buffer. */
580
611
  export function extractNarration(buffer) {
581
- const matches = [...buffer.matchAll(/⏵\s*([^\n⏵]{1,40})/g)];
582
- if (matches.length === 0)
583
- return null;
584
- const latest = matches[matches.length - 1]?.[1];
585
- if (latest === undefined)
612
+ const matches = [...buffer.matchAll(/⏵[ \t]*([^\n⏵]*)/g)];
613
+ const latest = matches[matches.length - 1]?.[1]?.trim();
614
+ if (!latest)
586
615
  return null;
587
- const text = latest.replace(/[。..!!,,、;;]+$/, '').trim();
616
+ // A missing newline must not turn the rest of the response into status text.
617
+ // Stop at sentence ends and clause delimiters, while leaving dotted
618
+ // identifiers such as `ink.tsx`, `agent.ctx` and `README.MD` intact. A dot
619
+ // ends a sentence only before whitespace, the end, or a Title-case word
620
+ // (`safely.The`); dot-uppercase-uppercase stays an extension (`.MD`).
621
+ const boundary = latest.search(/[。.!?!?;;]|\.(?=\s|$|[A-Z][a-z])/);
622
+ const sentence = boundary < 0 ? latest : latest.slice(0, boundary + 1);
623
+ // The budget is display width, so English and Chinese render equally long:
624
+ // 40 CJK characters and ~12 English words share the same line budget.
625
+ const text = cutToWidth(sentence, NARRATION_MAX_COLUMNS).replace(/[。..!!,,、;;]+$/, '').trim();
588
626
  return text.length === 0 ? null : text;
589
627
  }
590
628
  /** Format a token count compactly (`12.3k`, `1.2M`). */
package/package.json CHANGED
@@ -1,127 +1,128 @@
1
- {
2
- "name": "dsh-working-activity",
3
- "description": "Live model working-status line: playful copy, running tool, turn elapsed — for TUI prompt and Web UI",
4
- "version": "0.3.2",
5
- "type": "module",
6
- "main": "lib/types/index.js",
7
- "types": "lib/types/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/types/index.d.ts",
11
- "default": "./lib/types/index.js"
12
- },
13
- "./client": {
14
- "types": "./lib/types/client/index.d.ts",
15
- "default": "./lib/client.js"
16
- },
17
- "./config": {
18
- "types": "./lib/types/config.d.ts",
19
- "default": "./lib/types/config.js"
20
- },
21
- "./events": {
22
- "types": "./lib/types/events.d.ts",
23
- "default": "./lib/types/events.js"
24
- },
25
- "./frames": {
26
- "types": "./lib/types/frames.d.ts",
27
- "default": "./lib/types/frames.js"
28
- },
29
- "./invariant": {
30
- "types": "./lib/types/invariant.d.ts",
31
- "default": "./lib/types/invariant.js"
32
- },
33
- "./status": {
34
- "types": "./lib/types/status.d.ts",
35
- "default": "./lib/types/status.js"
36
- },
37
- "./cordis.patch.yml": "./cordis.patch.yml",
38
- "./src/*": "./src/*",
39
- "./package.json": "./package.json"
40
- },
41
- "files": [
42
- "lib",
43
- "src",
44
- "cordis.patch.yml"
45
- ],
46
- "engines": {
47
- "node": "^22.19 || >=24"
48
- },
49
- "scripts": {
50
- "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
51
- "build:client": "tsdown -c tsdown.config.ts",
52
- "test": "vitest run",
53
- "verify:deps": "node scripts/verify-manifest-deps.mjs",
54
- "verify:lockfiles": "node scripts/verify-lockfiles.mjs",
55
- "prepublishOnly": "npm run verify:deps && npm run verify:lockfiles && npm run build && npm run build:client"
56
- },
57
- "license": "BSD-3-Clause",
58
- "repository": {
59
- "type": "git",
60
- "url": "git+https://github.com/ccch1mneyyy/working-activity.git"
61
- },
62
- "bugs": {
63
- "url": "https://github.com/ccch1mneyyy/working-activity/issues"
64
- },
65
- "homepage": "https://github.com/ccch1mneyyy/working-activity#readme",
66
- "dsh": {
67
- "bundle": {
68
- "patch": "./cordis.patch.yml"
69
- },
70
- "client": {
71
- "platform": "web",
72
- "inject": [
73
- "@deepseek-ai/dsh-client-runtime",
74
- "@deepseek-ai/dsh-client-ui-conversation",
75
- "@deepseek-ai/dsh-client-ui-slots"
76
- ]
77
- }
78
- },
79
- "peerDependencies": {
80
- "@deepseek-ai/cordis": "^4.0.1",
81
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
82
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
83
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
84
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
85
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
86
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
87
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
88
- "@deepseek-ai/schemastery": "^3.18.1",
89
- "react": "^18.2.0"
90
- },
91
- "devDependencies": {
92
- "@deepseek-ai/cordis": "^4.0.1",
93
- "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
94
- "@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.6",
95
- "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.0-rc.6",
96
- "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
97
- "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
98
- "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
99
- "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
100
- "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
101
- "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
102
- "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
103
- "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
104
- "@deepseek-ai/schemastery": "^3.18.1",
105
- "@types/node": "^22.0.0",
106
- "@types/react": "~18.3.1",
107
- "lightningcss": "^1.32.0",
108
- "react": "^18.2.0",
109
- "tsdown": "^0.22.2",
110
- "typescript": "^6.0.3",
111
- "vitest": "^3.2.4"
112
- },
113
- "peerDependenciesMeta": {
114
- "@deepseek-ai/dsh-client-runtime": {
115
- "optional": true
116
- },
117
- "@deepseek-ai/dsh-client-ui-conversation": {
118
- "optional": true
119
- },
120
- "@deepseek-ai/dsh-client-ui-slots": {
121
- "optional": true
122
- },
123
- "react": {
124
- "optional": true
125
- }
126
- }
127
- }
1
+ {
2
+ "name": "dsh-working-activity",
3
+ "description": "Live model working-status line: playful copy, running tool, turn elapsed — for TUI prompt and Web UI",
4
+ "version": "0.4.0",
5
+ "type": "module",
6
+ "main": "lib/types/index.js",
7
+ "types": "lib/types/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/types/index.d.ts",
11
+ "default": "./lib/types/index.js"
12
+ },
13
+ "./client": {
14
+ "types": "./lib/types/client/index.d.ts",
15
+ "default": "./lib/client.js"
16
+ },
17
+ "./config": {
18
+ "types": "./lib/types/config.d.ts",
19
+ "default": "./lib/types/config.js"
20
+ },
21
+ "./events": {
22
+ "types": "./lib/types/events.d.ts",
23
+ "default": "./lib/types/events.js"
24
+ },
25
+ "./frames": {
26
+ "types": "./lib/types/frames.d.ts",
27
+ "default": "./lib/types/frames.js"
28
+ },
29
+ "./invariant": {
30
+ "types": "./lib/types/invariant.d.ts",
31
+ "default": "./lib/types/invariant.js"
32
+ },
33
+ "./status": {
34
+ "types": "./lib/types/status.d.ts",
35
+ "default": "./lib/types/status.js"
36
+ },
37
+ "./cordis.patch.yml": "./cordis.patch.yml",
38
+ "./src/*": "./src/*",
39
+ "./package.json": "./package.json"
40
+ },
41
+ "files": [
42
+ "lib",
43
+ "src",
44
+ "cordis.patch.yml"
45
+ ],
46
+ "engines": {
47
+ "node": "^22.19 || >=24"
48
+ },
49
+ "scripts": {
50
+ "build": "tsc -p tsconfig.json && tsc -p tsconfig.client.json",
51
+ "build:client": "tsdown -c tsdown.config.ts",
52
+ "test": "vitest run",
53
+ "test:alpha2": "pnpm run build && pnpm --dir ../working-activity-alpha2-compat install --frozen-lockfile --strict-peer-dependencies && pnpm --dir ../working-activity-alpha2-compat test",
54
+ "verify:deps": "node scripts/verify-manifest-deps.mjs",
55
+ "verify:lockfiles": "node scripts/verify-lockfiles.mjs",
56
+ "prepublishOnly": "npm run verify:deps && npm run verify:lockfiles && npm run build && npm run build:client"
57
+ },
58
+ "license": "BSD-3-Clause",
59
+ "repository": {
60
+ "type": "git",
61
+ "url": "git+https://github.com/ccch1mneyyy/working-activity.git"
62
+ },
63
+ "bugs": {
64
+ "url": "https://github.com/ccch1mneyyy/working-activity/issues"
65
+ },
66
+ "homepage": "https://github.com/ccch1mneyyy/working-activity#readme",
67
+ "dsh": {
68
+ "bundle": {
69
+ "patch": "./cordis.patch.yml"
70
+ },
71
+ "client": {
72
+ "platform": "web",
73
+ "inject": [
74
+ "@deepseek-ai/dsh-client-runtime",
75
+ "@deepseek-ai/dsh-client-ui-conversation",
76
+ "@deepseek-ai/dsh-client-ui-slots"
77
+ ]
78
+ }
79
+ },
80
+ "peerDependencies": {
81
+ "@deepseek-ai/cordis": "^4.0.1",
82
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
83
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
84
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
85
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
86
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
87
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
88
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6 || ^0.1.1-rc.1 || ^0.1.2-alpha.2",
89
+ "@deepseek-ai/schemastery": "^3.18.1",
90
+ "react": "^18.2.0"
91
+ },
92
+ "devDependencies": {
93
+ "@deepseek-ai/cordis": "^4.0.1",
94
+ "@deepseek-ai/dsh-agent": "^0.1.0-rc.6",
95
+ "@deepseek-ai/dsh-agent-loop": "^0.1.0-rc.6",
96
+ "@deepseek-ai/dsh-agent-loop-testkit": "^0.1.0-rc.6",
97
+ "@deepseek-ai/dsh-client-runtime": "^0.1.0-rc.6",
98
+ "@deepseek-ai/dsh-client-ui-conversation": "^0.1.0-rc.6",
99
+ "@deepseek-ai/dsh-client-ui-slots": "^0.1.0-rc.6",
100
+ "@deepseek-ai/dsh-invariants": "^0.1.0-rc.6",
101
+ "@deepseek-ai/dsh-llm": "^0.1.0-rc.6",
102
+ "@deepseek-ai/dsh-session": "^0.1.0-rc.6",
103
+ "@deepseek-ai/dsh-system-prompt": "^0.1.0-rc.6",
104
+ "@deepseek-ai/dsh-tools": "^0.1.0-rc.6",
105
+ "@deepseek-ai/schemastery": "^3.18.1",
106
+ "@types/node": "^22.0.0",
107
+ "@types/react": "~18.3.1",
108
+ "lightningcss": "^1.32.0",
109
+ "react": "^18.2.0",
110
+ "tsdown": "^0.22.2",
111
+ "typescript": "^6.0.3",
112
+ "vitest": "^3.2.4"
113
+ },
114
+ "peerDependenciesMeta": {
115
+ "@deepseek-ai/dsh-client-runtime": {
116
+ "optional": true
117
+ },
118
+ "@deepseek-ai/dsh-client-ui-conversation": {
119
+ "optional": true
120
+ },
121
+ "@deepseek-ai/dsh-client-ui-slots": {
122
+ "optional": true
123
+ },
124
+ "react": {
125
+ "optional": true
126
+ }
127
+ }
128
+ }
package/src/lang.ts CHANGED
@@ -57,6 +57,8 @@ const dict = {
57
57
  },
58
58
  'tool-count-one': { zh: '{{count}} 工具', en: '{{count}} tool' },
59
59
  'tool-count-many': { zh: '{{count}} 工具', en: '{{count}} tools' },
60
+ /** Consecutive-tool streak badge (replaces the old flame emoji). */
61
+ 'tool-streak': { zh: '工具x{{count}}', en: 'tool x{{count}}' },
60
62
  /** Subagent count in the done summary. */
61
63
  'subagent-count': { zh: '子代理 {{count}} 个', en: '{{count}} subagents' },
62
64
  /** Work-reminder copy after `workRemindAt` turn-hours. */
package/src/status.ts CHANGED
@@ -432,7 +432,7 @@ export class ActivityTracker {
432
432
  const git = tool.isGit
433
433
  ? (this.gitBranch !== undefined ? ` · git ${this.gitBranch}` : ' · git')
434
434
  : ''
435
- const combo = this.streak >= COMBO_SHOW_AT ? ` · 🔥x${this.streak}` : ''
435
+ const combo = this.streak >= COMBO_SHOW_AT ? ` · ${t('tool-streak', { count: this.streak })}` : ''
436
436
  const narration = this.freshNarration(nowMs)
437
437
  const line = narration === null
438
438
  ? `${fragment} · ${elapsed}${git}${combo}`
@@ -507,12 +507,12 @@ export class ActivityTracker {
507
507
  const phrase = this.previousPhrase ?? (this.phase === 'waiting'
508
508
  ? waitingPhrase()
509
509
  : this.livelyPhrase(thinkingMs, nowMs))
510
- // Ellipsis breathing (pi DOT_FRAMES) + optional estimated tps prefix.
511
- const dots = DOT_FRAMES[Math.floor(nowMs / TICK_MS) % DOT_FRAMES.length]
510
+ // The indicator animation (whale etc.) already signals activity, so the
511
+ // pi DOT_FRAMES ellipsis breathing is dropped for the DSH line.
512
512
  const tps = this.tpsPrefix(nowMs)
513
513
  return {
514
514
  phase: this.phase,
515
- line: `${tps}${phrase}${dots} · ${elapsedLine}`,
515
+ line: `${tps}${phrase} · ${elapsedLine}`,
516
516
  phrase,
517
517
  toolCount: this.toolCount,
518
518
  turnElapsedMs: this.turnElapsedMs(nowMs),
@@ -598,7 +598,7 @@ export class ActivityTracker {
598
598
  const { thinkingMs, toolMs, toolCount } = this.stats()
599
599
  const tokens = this.turnTokens > 0 ? ` · 🔥 ${fmtTokens(this.turnTokens)}` : ''
600
600
  const sub = this.subagentCount > 0 ? ` · ${t('subagent-count', { count: this.subagentCount })}` : ''
601
- const combo = this.maxStreak >= COMBO_SHOW_AT ? ` · 🔥x${this.maxStreak}` : ''
601
+ const combo = this.maxStreak >= COMBO_SHOW_AT ? ` · ${t('tool-streak', { count: this.maxStreak })}` : ''
602
602
  const tools = t(toolCount === 1 ? 'tool-count-one' : 'tool-count-many', { count: toolCount })
603
603
  const summary = t('done-summary', {
604
604
  tools,
@@ -653,10 +653,6 @@ const COMBO_GAP_MS = 10_000
653
653
  const COMBO_SHOW_AT = 2
654
654
  /** The tps estimate stays fresh this long after the last chunk. */
655
655
  const TPS_WINDOW_MS = 3500
656
- /** Render tick cadence (matches the TUI's 500ms activity timer). */
657
- const TICK_MS = 500
658
- /** Ellipsis breathing frames appended to thinking lines (pi parity). */
659
- const DOT_FRAMES = ['', ' ·', ' ··', ' ···', ' ··', ' ·']
660
656
  /** Cap on replayed done cards; older entries drop. */
661
657
  const DONE_QUEUE_MAX = 6
662
658
  /** Show the last tool's fragment in the done line for this long after it ends. */
@@ -666,13 +662,69 @@ const STREAM_BUFFER_CHARS = 300
666
662
  /** A narration stays visible this long after the stream went quiet. */
667
663
  const NARRATE_GRACE_MS = 5000
668
664
 
665
+ /** Narration budget in display columns: 40 CJK chars ≈ 12 English words. */
666
+ const NARRATION_MAX_COLUMNS = 80
667
+
668
+ /** A CJK/full-width code point renders 2 terminal columns; the rest render 1. */
669
+ function isWide(cp: number): boolean {
670
+ return (
671
+ (cp >= 0x1100 && cp <= 0x115f) || // Hangul Jamo
672
+ (cp >= 0x2e80 && cp <= 0xa4cf) || // CJK radicals, kana, Yi
673
+ (cp >= 0xac00 && cp <= 0xd7a3) || // Hangul syllables
674
+ (cp >= 0xf900 && cp <= 0xfaff) || // CJK compatibility ideographs
675
+ (cp >= 0xfe30 && cp <= 0xfe4f) || // CJK compatibility forms
676
+ (cp >= 0xff00 && cp <= 0xff60) || // full-width forms
677
+ (cp >= 0xffe0 && cp <= 0xffe6) || // full-width signs
678
+ (cp >= 0x20000 && cp <= 0x3fffd) // CJK extensions
679
+ )
680
+ }
681
+
682
+ /** Cut before `max` columns, backing off to the last word or clause boundary. */
683
+ function cutToWidth(text: string, max: number): string {
684
+ let width = 0
685
+ let cut = text.length
686
+ const chars = [...text]
687
+ for (let i = 0; i < chars.length; i++) {
688
+ const w = isWide(chars[i]!.codePointAt(0)!) ? 2 : 1
689
+ if (width + w > max) {
690
+ cut = chars.slice(0, i).join('').length
691
+ break
692
+ }
693
+ width += w
694
+ }
695
+ if (cut === text.length) return text
696
+ // Back off to a soft boundary so English words survive intact; dense CJK has
697
+ // none and hard-cuts at the budget like the original fixed 40-char capture.
698
+ const head = text.slice(0, cut)
699
+ const soft = Math.max(
700
+ head.lastIndexOf(' '),
701
+ head.lastIndexOf('\t'),
702
+ head.lastIndexOf(','),
703
+ head.lastIndexOf('、'),
704
+ head.lastIndexOf(';'),
705
+ head.lastIndexOf(';'),
706
+ head.lastIndexOf(','),
707
+ )
708
+ return soft > 0 ? head.slice(0, soft) : head
709
+ }
710
+
669
711
  /** Extract the latest `⏵` self-narration line from a stream buffer. */
670
712
  export function extractNarration(buffer: string): string | null {
671
- const matches = [...buffer.matchAll(/⏵\s*([^\n⏵]{1,40})/g)]
672
- if (matches.length === 0) return null
673
- const latest = matches[matches.length - 1]?.[1]
674
- if (latest === undefined) return null
675
- const text = latest.replace(/[。..!!,,、;;]+$/, '').trim()
713
+ const matches = [...buffer.matchAll(/⏵[ \t]*([^\n⏵]*)/g)]
714
+ const latest = matches[matches.length - 1]?.[1]?.trim()
715
+ if (!latest) return null
716
+
717
+ // A missing newline must not turn the rest of the response into status text.
718
+ // Stop at sentence ends and clause delimiters, while leaving dotted
719
+ // identifiers such as `ink.tsx`, `agent.ctx` and `README.MD` intact. A dot
720
+ // ends a sentence only before whitespace, the end, or a Title-case word
721
+ // (`safely.The`); dot-uppercase-uppercase stays an extension (`.MD`).
722
+ const boundary = latest.search(/[。.!?!?;;]|\.(?=\s|$|[A-Z][a-z])/)
723
+ const sentence = boundary < 0 ? latest : latest.slice(0, boundary + 1)
724
+
725
+ // The budget is display width, so English and Chinese render equally long:
726
+ // 40 CJK characters and ~12 English words share the same line budget.
727
+ const text = cutToWidth(sentence, NARRATION_MAX_COLUMNS).replace(/[。..!!,,、;;]+$/, '').trim()
676
728
  return text.length === 0 ? null : text
677
729
  }
678
730