@deepseek-harness-tui/dsh-tui 0.8.4 → 0.8.5

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 (174) hide show
  1. package/bin/dsh-tui.js +0 -0
  2. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -13
  3. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -59
  4. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -15
  5. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -32
  6. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -59
  7. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -166
  8. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -42
  9. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -143
  10. package/dsh-ecosystem-spec/registry/README.md +14 -14
  11. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -56
  12. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -13
  13. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -149
  14. package/lib/types/activityPrefs.d.ts +10 -0
  15. package/lib/types/activityPrefs.d.ts.map +1 -1
  16. package/lib/types/activityPrefs.js +17 -0
  17. package/lib/types/components/MessageList.d.ts +4 -1
  18. package/lib/types/components/MessageList.d.ts.map +1 -1
  19. package/lib/types/components/MessageList.js +4 -4
  20. package/lib/types/components/SplitDiffView.d.ts +3 -1
  21. package/lib/types/components/SplitDiffView.d.ts.map +1 -1
  22. package/lib/types/components/SplitDiffView.js +12 -5
  23. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  24. package/lib/types/components/StreamingMarkdown.js +10 -2
  25. package/lib/types/components/activityFrames.d.ts +7 -28
  26. package/lib/types/components/activityFrames.d.ts.map +1 -1
  27. package/lib/types/components/activityFrames.js +7 -94
  28. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  29. package/lib/types/components/messages/AssistantThinkingMessage.js +1 -1
  30. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +4 -1
  31. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  32. package/lib/types/components/messages/AssistantToolUseMessage.js +14 -3
  33. package/lib/types/dsh-adapter/channel.d.ts +17 -0
  34. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  35. package/lib/types/dsh-adapter/channel.js +161 -19
  36. package/lib/types/dsh-adapter/index.d.ts +5 -0
  37. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  38. package/lib/types/dsh-adapter/index.js +19 -0
  39. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  40. package/lib/types/dsh-adapter/plugin.js +176 -4
  41. package/lib/types/dsh-adapter/settings-sections.d.ts +13 -0
  42. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  43. package/lib/types/dsh-adapter/settings-sections.js +31 -6
  44. package/lib/types/ink/ink.d.ts +9 -0
  45. package/lib/types/ink/ink.d.ts.map +1 -1
  46. package/lib/types/ink/ink.js +24 -1
  47. package/lib/types/screens/Chat.d.ts.map +1 -1
  48. package/lib/types/screens/Chat.js +34 -4
  49. package/lib/types/screens/Settings.d.ts.map +1 -1
  50. package/lib/types/screens/Settings.js +109 -50
  51. package/lib/types/screens/StatusLine.d.ts +8 -0
  52. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  53. package/lib/types/screens/StatusLine.js +62 -39
  54. package/lib/types/settings-sections.d.ts +1 -1
  55. package/lib/types/settings-sections.d.ts.map +1 -1
  56. package/lib/types/tips.js +2 -2
  57. package/lib/types/tuiDisplayPrefs.d.ts +47 -0
  58. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -0
  59. package/lib/types/tuiDisplayPrefs.js +60 -0
  60. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -8
  61. package/node_modules/@dsh-std/command/LICENSE +21 -21
  62. package/node_modules/@dsh-std/command/README.md +26 -26
  63. package/node_modules/@dsh-std/command/README.zh.md +28 -28
  64. package/node_modules/@dsh-std/command/lib/index.js.map +1 -1
  65. package/node_modules/@dsh-std/command/package.json +22 -22
  66. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  67. package/node_modules/@dsh-std/connection/LICENSE +21 -21
  68. package/node_modules/@dsh-std/connection/README.md +42 -42
  69. package/node_modules/@dsh-std/connection/README.zh.md +42 -42
  70. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  71. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  72. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  73. package/node_modules/@dsh-std/connection/package.json +20 -20
  74. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  75. package/node_modules/@dsh-std/core/LICENSE +21 -21
  76. package/node_modules/@dsh-std/core/README.md +13 -13
  77. package/node_modules/@dsh-std/core/README.zh.md +13 -13
  78. package/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  79. package/node_modules/@dsh-std/core/package.json +21 -21
  80. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  81. package/node_modules/@dsh-std/manifest/LICENSE +21 -21
  82. package/node_modules/@dsh-std/manifest/README.md +5 -5
  83. package/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  84. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  85. package/node_modules/@dsh-std/manifest/package.json +20 -20
  86. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  87. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -8
  88. package/node_modules/@dsh-std/messages/LICENSE +21 -21
  89. package/node_modules/@dsh-std/messages/README.md +5 -5
  90. package/node_modules/@dsh-std/messages/README.zh.md +7 -7
  91. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -1
  92. package/node_modules/@dsh-std/messages/package.json +20 -20
  93. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -54
  94. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  95. package/node_modules/@dsh-std/presentation/LICENSE +21 -21
  96. package/node_modules/@dsh-std/presentation/README.md +27 -27
  97. package/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  98. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  99. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  100. package/node_modules/@dsh-std/presentation/package.json +25 -25
  101. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -8
  102. package/node_modules/@dsh-std/storage/LICENSE +21 -21
  103. package/node_modules/@dsh-std/storage/README.md +5 -5
  104. package/node_modules/@dsh-std/storage/README.zh.md +7 -7
  105. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -1
  106. package/node_modules/@dsh-std/storage/package.json +20 -20
  107. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -56
  108. package/package.json +6 -3
  109. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  110. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -21
  111. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -42
  112. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -42
  113. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  114. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  115. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  116. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -20
  117. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  118. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -21
  119. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -13
  120. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -13
  121. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  122. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -21
  123. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -9
  124. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -21
  125. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -5
  126. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -5
  127. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -1
  128. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -20
  129. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -144
  130. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -9
  131. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -21
  132. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -27
  133. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -29
  134. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -1
  135. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  136. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -25
  137. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  138. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -21
  139. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -13
  140. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -13
  141. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  142. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -21
  143. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  144. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -21
  145. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -13
  146. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -13
  147. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  148. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -21
  149. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  150. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -21
  151. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -13
  152. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -13
  153. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  154. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -21
  155. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -8
  156. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -21
  157. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -42
  158. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -42
  159. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -1
  160. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  161. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -1
  162. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -20
  163. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  164. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -21
  165. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -13
  166. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -13
  167. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  168. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -21
  169. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -8
  170. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -21
  171. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -13
  172. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -13
  173. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -1
  174. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -21
@@ -24,7 +24,10 @@ import { homeDir, LEGACY_DATA_DIR } from '../utils/paths.js';
24
24
  import { extractMentions } from '../utils/mentions.js';
25
25
  import { t } from '../i18n.js';
26
26
  import { modeDisplayName, resolveSessionModes } from '../sessionModes.js';
27
+ import { normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
27
28
  import { ActivityTracker } from 'dsh-working-activity/status';
29
+ import { featureOn } from 'dsh-working-activity/config';
30
+ import { readActivityConfig } from '../activityPrefs.js';
28
31
  import { attachSessionToWorkspace } from './workspace.js';
29
32
  import { createLocalWorkspaceRuntime, getHostWorkspaceRuntime } from './workspaces.js';
30
33
  import { getHostCommandTrees } from './command-trees.js';
@@ -968,6 +971,8 @@ export function createChannel(ctx, initialAgent, options) {
968
971
  activityFrames: options.activityFrames,
969
972
  diffLayout: options.diffLayout ?? 'auto',
970
973
  thinkingFold: options.thinkingFold ?? 'preview',
974
+ toolBackground: normalizeToolBackground(options.toolBackground),
975
+ statusBar: normalizeStatusBar(options.statusBar),
971
976
  activityEnabled: options.activity !== false,
972
977
  contextBarEnabled: options.contextBar !== false,
973
978
  agentPreset: options.agentPreset,
@@ -1613,10 +1618,16 @@ export function createChannel(ctx, initialAgent, options) {
1613
1618
  streaming = undefined;
1614
1619
  reasoning = undefined;
1615
1620
  // Stale sealed/thinking bookkeeping belongs to the OLD agent's rows;
1616
- // keep it out of the next turn's settle logs and revive cache.
1621
+ // keep it out of the next turn's settle logs and revive cache. Event
1622
+ // sequence numbers restart in the fresh session, so its dedupe ledgers
1623
+ // must not retain the old session's sequence ids.
1617
1624
  sealedReasoning.length = 0;
1618
1625
  lastReasoningRow = undefined;
1619
1626
  toolCards.clear();
1627
+ handledAssistantMessages.clear();
1628
+ handledAssistantChunks.clear();
1629
+ assistantRowsByStep.clear();
1630
+ lastTextDelta.clear();
1620
1631
  nextRowId = 0;
1621
1632
  state.rows.length = 0;
1622
1633
  // Goal/todo/title are session-scoped; the replay re-derives them for
@@ -1644,6 +1655,7 @@ export function createChannel(ctx, initialAgent, options) {
1644
1655
  state.tpsSamples = [];
1645
1656
  state.lastUsage = undefined;
1646
1657
  state.workingActivity = undefined;
1658
+ state.loadedContext = undefined;
1647
1659
  state.contextWindow = undefined;
1648
1660
  state.contextSegments = {
1649
1661
  system: 0,
@@ -1837,6 +1849,8 @@ export function createChannel(ctx, initialAgent, options) {
1837
1849
  agent = handle.agent;
1838
1850
  currentHandle = handle;
1839
1851
  bindAgent();
1852
+ // Model-switch quip rides the fresh tracker (pi parity).
1853
+ activityTracker.onModelSwitch(model);
1840
1854
  refreshCommandList();
1841
1855
  void refreshLoadedContext();
1842
1856
  void refreshSkillCommands();
@@ -1913,6 +1927,21 @@ export function createChannel(ctx, initialAgent, options) {
1913
1927
  state.thinkingFold = mode;
1914
1928
  state.emit();
1915
1929
  },
1930
+ setToolBackground(background) {
1931
+ const normalized = normalizeToolBackground(background);
1932
+ if (normalized === state.toolBackground)
1933
+ return;
1934
+ state.toolBackground = normalized;
1935
+ state.emit();
1936
+ },
1937
+ setStatusBar(config) {
1938
+ const next = normalizeStatusBar({ ...state.statusBar, ...config });
1939
+ const changed = Object.keys(next).some(key => next[key] !== state.statusBar[key]);
1940
+ if (!changed)
1941
+ return;
1942
+ state.statusBar = next;
1943
+ state.emit();
1944
+ },
1916
1945
  setActivityFrames(name) {
1917
1946
  if (!isPresetName(name)) {
1918
1947
  state.notify(t('unknown-activity-preset', { name }), { color: 'error' });
@@ -2318,6 +2347,9 @@ export function createChannel(ctx, initialAgent, options) {
2318
2347
  .compactNow(agent, signal)
2319
2348
  .then((result) => {
2320
2349
  state.notify(result ? t('compact-done') : t('compact-nothing'));
2350
+ // Compaction quip rides the next thinking rotation (pi parity).
2351
+ if (result)
2352
+ activityTracker.onCompact('done');
2321
2353
  })
2322
2354
  .catch((error) => {
2323
2355
  state.notify(t('compact-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'error', timeoutMs: 8000 });
@@ -2983,6 +3015,39 @@ ${output}
2983
3015
  let tpsStep;
2984
3016
  /** Tool cards by callId, so tool/result can settle the running card. */
2985
3017
  const toolCards = new Map();
3018
+ /**
3019
+ * Session events are delivered live and can also be replayed around a
3020
+ * reconnect. A repeated sealed message must not create a second assistant
3021
+ * row for the same durable sequence number.
3022
+ */
3023
+ const handledAssistantMessages = new Set();
3024
+ const handledAssistantChunks = new Set();
3025
+ const assistantRowsByStep = new Map();
3026
+ const lastTextDelta = new Map();
3027
+ const stepKey = (turn, step) => `${turn}:${step}`;
3028
+ /** Append a stream delta idempotently. Providers normally send a pure
3029
+ * delta, but reconnect/proxy paths can resend a cumulative prefix or a
3030
+ * delta whose beginning overlaps the previous tail. Merge the overlap
3031
+ * instead of blindly concatenating it into the visible transcript. */
3032
+ const appendTextDelta = (row, delta) => {
3033
+ if (delta === '')
3034
+ return;
3035
+ if (lastTextDelta.get(row) === delta)
3036
+ return;
3037
+ lastTextDelta.set(row, delta);
3038
+ if (delta.startsWith(row.text)) {
3039
+ row.text = delta;
3040
+ return;
3041
+ }
3042
+ const maxOverlap = Math.min(row.text.length, delta.length, 4096);
3043
+ for (let size = maxOverlap; size > 0; size--) {
3044
+ if (row.text.endsWith(delta.slice(0, size))) {
3045
+ row.text += delta.slice(size);
3046
+ return;
3047
+ }
3048
+ }
3049
+ row.text += delta;
3050
+ };
2986
3051
  /** The host-plane tools registry (dsh-tools). Resolved once; absent in
2987
3052
  * bare embedders — every presenter call soft-fails to undefined and the
2988
3053
  * card falls back to raw text. */
@@ -3034,11 +3099,22 @@ ${output}
3034
3099
  */
3035
3100
  const firstTextOf = (content) => (content ?? []).find(block => block.type === 'text')?.text.trim() ?? '';
3036
3101
  const ensureStreaming = (seq) => {
3037
- if (streaming === undefined) {
3038
- streaming = { id: nextRowId, kind: 'assistant', text: '', streaming: true, ...seq !== undefined ? { seq } : {} };
3039
- nextRowId += 1;
3040
- state.rows.push(streaming);
3102
+ if (streaming !== undefined)
3103
+ return streaming;
3104
+ // A reconnect can replay the first delta after the sealed message was
3105
+ // already observed. Reuse that durable row instead of opening a second
3106
+ // assistant bubble for the same event sequence.
3107
+ const existing = seq === undefined
3108
+ ? undefined
3109
+ : [...state.rows].reverse().find(row => row.kind === 'assistant' && row.seq === seq);
3110
+ if (existing !== undefined) {
3111
+ existing.streaming = true;
3112
+ streaming = existing;
3113
+ return existing;
3041
3114
  }
3115
+ streaming = { id: nextRowId, kind: 'assistant', text: '', streaming: true, ...seq !== undefined ? { seq } : {} };
3116
+ nextRowId += 1;
3117
+ state.rows.push(streaming);
3042
3118
  return streaming;
3043
3119
  };
3044
3120
  /** Latest reasoning row keyed by its (turn, step) — lets a resumed
@@ -3169,6 +3245,13 @@ ${output}
3169
3245
  * using it would rebuild a second thinking block per step. */
3170
3246
  let replaying = false;
3171
3247
  const replayEvents = (events) => {
3248
+ // Event sequence numbers restart with a replacement session; reset the
3249
+ // idempotency ledger before replay so an old session cannot suppress a
3250
+ // legitimate message in the new transcript.
3251
+ handledAssistantMessages.clear();
3252
+ handledAssistantChunks.clear();
3253
+ assistantRowsByStep.clear();
3254
+ lastTextDelta.clear();
3172
3255
  replaying = true;
3173
3256
  try {
3174
3257
  for (const event of prepareReplayEvents(events))
@@ -3256,6 +3339,9 @@ ${output}
3256
3339
  break;
3257
3340
  }
3258
3341
  case 'assistant/chunk': {
3342
+ if (handledAssistantChunks.has(event.seq))
3343
+ break;
3344
+ handledAssistantChunks.add(event.seq);
3259
3345
  const chunk = event.data.chunk;
3260
3346
  if (chunk.type === 'text-delta') {
3261
3347
  if (chunk.text) {
@@ -3263,13 +3349,20 @@ ${output}
3263
3349
  // window (see foldLiveReasoning) — before this text grows the
3264
3350
  // transcript and pushes the block into scrollback.
3265
3351
  foldLiveReasoning('first text token');
3266
- ensureStreaming(event.seq).text += chunk.text;
3267
- state.responseChars += chunk.text.length;
3352
+ const key = stepKey(event.data.turn, event.data.step);
3353
+ const row = assistantRowsByStep.get(key) ?? ensureStreaming(event.seq);
3354
+ assistantRowsByStep.set(key, row);
3355
+ streaming = row;
3356
+ row.streaming = true;
3357
+ const before = row.text.length;
3358
+ appendTextDelta(row, chunk.text);
3359
+ state.responseChars += Math.max(0, row.text.length - before);
3268
3360
  }
3269
3361
  }
3270
3362
  else if (chunk.type === 'reasoning-delta') {
3271
3363
  if (chunk.text) {
3272
- ensureReasoning(event.seq, event.data.turn, event.data.step).text += chunk.text;
3364
+ const row = ensureReasoning(event.seq, event.data.turn, event.data.step);
3365
+ appendTextDelta(row, chunk.text);
3273
3366
  }
3274
3367
  }
3275
3368
  const step = tpsStep;
@@ -3290,6 +3383,9 @@ ${output}
3290
3383
  break;
3291
3384
  }
3292
3385
  case 'assistant/message': {
3386
+ if (handledAssistantMessages.has(event.seq))
3387
+ break;
3388
+ handledAssistantMessages.add(event.seq);
3293
3389
  const text = textOf(event.data.message.content);
3294
3390
  // Replay without chunk deltas (prepareReplayEvents drops settled
3295
3391
  // ones): rebuild the reasoning row from the sealed message's
@@ -3319,8 +3415,22 @@ ${output}
3319
3415
  // anyway leaves an empty `●` bullet in the transcript. A pre-existing
3320
3416
  // streaming row always has text (ensureStreaming is only reached on
3321
3417
  // non-empty text deltas), so only create one when text arrives.
3322
- const row = streaming ?? (text ? ensureStreaming(event.seq) : undefined);
3418
+ // Key the step→row ledger only when the event carries a durable
3419
+ // turn/step; a message without them must never collide onto a
3420
+ // previous step's row (a bare `undefined:undefined` key would make
3421
+ // every turn/step-less message reuse the FIRST one's assistant row).
3422
+ const msgTurn = event.data.turn;
3423
+ const msgStep = event.data.step;
3424
+ const msgKey = msgTurn !== undefined && msgStep !== undefined
3425
+ ? stepKey(msgTurn, msgStep)
3426
+ : undefined;
3427
+ const row = (msgKey !== undefined ? assistantRowsByStep.get(msgKey) : undefined) ?? streaming ??
3428
+ (text
3429
+ ? ([...state.rows].reverse().find(candidate => candidate.kind === 'assistant' && candidate.seq === event.seq) ?? ensureStreaming(event.seq))
3430
+ : undefined);
3323
3431
  if (row !== undefined) {
3432
+ if (msgKey !== undefined)
3433
+ assistantRowsByStep.set(msgKey, row);
3324
3434
  row.time = event.time;
3325
3435
  if (text)
3326
3436
  row.text = text;
@@ -3616,11 +3726,36 @@ ${output}
3616
3726
  // Live subscription list and activity timer, rebound to every replacement
3617
3727
  // agent so no status from the previous session can leak across a swap.
3618
3728
  let agentSubscriptions = [];
3619
- let activityTracker = new ActivityTracker({
3620
- phrases: true,
3621
- detailLimit: 40,
3622
- showIdle: false,
3623
- });
3729
+ /** Tracker knobs + custom actions from the persisted pi-style config
3730
+ * (`~/.dsh-tui/working-activity.json`); a missing file means lively
3731
+ * defaults (all eggs on). */
3732
+ const activityPrefsSnapshot = () => {
3733
+ const cfg = readActivityConfig();
3734
+ if (cfg === undefined) {
3735
+ return { config: { phrases: true, detailLimit: 40, showIdle: false } };
3736
+ }
3737
+ return {
3738
+ config: {
3739
+ phrases: featureOn(cfg, 'phrases'),
3740
+ detailLimit: 40,
3741
+ showIdle: false,
3742
+ features: {
3743
+ rareEggs: featureOn(cfg, 'rareEggs'),
3744
+ weekend: featureOn(cfg, 'weekend'),
3745
+ holidays: featureOn(cfg, 'holidays'),
3746
+ nightPhrases: featureOn(cfg, 'nightPhrases'),
3747
+ },
3748
+ customPhrases: cfg.customPhrases,
3749
+ showTokPerSec: cfg.showTokPerSec,
3750
+ workRemindAt: cfg.workRemindAt,
3751
+ },
3752
+ customActions: cfg.customActions,
3753
+ };
3754
+ };
3755
+ let activityTracker = (() => {
3756
+ const prefs = activityPrefsSnapshot();
3757
+ return new ActivityTracker(prefs.config, Date.now, prefs.customActions);
3758
+ })();
3624
3759
  let activityTickTimer;
3625
3760
  const stopActivityTick = () => {
3626
3761
  if (activityTickTimer === undefined)
@@ -3642,11 +3777,8 @@ ${output}
3642
3777
  for (const dispose of agentSubscriptions)
3643
3778
  dispose();
3644
3779
  stopActivityTick();
3645
- activityTracker = new ActivityTracker({
3646
- phrases: true,
3647
- detailLimit: 40,
3648
- showIdle: false,
3649
- });
3780
+ const prefs = activityPrefsSnapshot();
3781
+ activityTracker = new ActivityTracker(prefs.config, Date.now, prefs.customActions);
3650
3782
  activityTracker.onAgentStatus(agent.status);
3651
3783
  renderWorkingActivity();
3652
3784
  activityTickTimer = setInterval(() => {
@@ -3742,6 +3874,14 @@ ${output}
3742
3874
  // publish never throws into this arm.
3743
3875
  messageObserver?.publish(session, event);
3744
3876
  activityTracker.onSessionEvent(event);
3877
+ // Interrupt quip: an aborted/interrupted turn ends the round; the
3878
+ // comeback copy shows on the next thinking rotation (pi parity).
3879
+ if (event.type === 'turn/end') {
3880
+ const reason = event.data.reason;
3881
+ if (reason?.kind === 'aborted' || reason?.kind === 'interrupted') {
3882
+ activityTracker.onInterrupted();
3883
+ }
3884
+ }
3745
3885
  renderWorkingActivity();
3746
3886
  // Mode-affecting atoms fold into the Shift+Tab mode indicator the
3747
3887
  // moment they land (whether appended by cycleMode or by hand).
@@ -3809,6 +3949,8 @@ ${output}
3809
3949
  // only show a branch for sessions this install actually used — which
3810
3950
  // is exactly what the column claims.
3811
3951
  noteBranch(agent.session.id, branch);
3952
+ // Feed the working line so git tools can show ` · git <branch>`.
3953
+ activityTracker.onGitBranch(branch);
3812
3954
  state.emit();
3813
3955
  }
3814
3956
  })
@@ -9,6 +9,7 @@
9
9
  import type { Context } from '@deepseek-ai/cordis';
10
10
  import Schema from '@deepseek-ai/schemastery';
11
11
  import type { SessionModeSpec } from '../sessionModes.js';
12
+ import { type StatusBarConfig, type ToolBackground } from '../tuiDisplayPrefs.js';
12
13
  export declare const name = "dsh-tui";
13
14
  export declare const inject: string[];
14
15
  /**
@@ -70,6 +71,10 @@ export interface Config {
70
71
  * preview and folds each step when it settles; `full` keeps thinking
71
72
  * expanded until the whole turn ends. Editable live from `/settings`. */
72
73
  thinkingFold?: 'preview' | 'full';
74
+ /** Tool-card background strength; defaults to no added background. */
75
+ toolBackground?: ToolBackground;
76
+ /** Status-footer field visibility and compact presentation preferences. */
77
+ statusBar?: Partial<StatusBarConfig>;
73
78
  /** Shift+Tab session-mode cycle (array order IS the cycle order; index 0
74
79
  * is the unmarked base mode). Each entry bundles any subset of the
75
80
  * `plan`/`sandbox`/`approval` atoms; absent → the built-in
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AAEzD,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6DAEyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC;;8EAE0E;IAC1E,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA4BhC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,MAAM,MAAM,0BAA0B,CAAA;AAC7C,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAA;AACzD,OAAO,EAAsB,KAAK,eAAe,EAAE,KAAK,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAErG,eAAO,MAAM,IAAI,YAAY,CAAA;AAU7B,eAAO,MAAM,MAAM,UAAa,CAAA;AAEhC;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,0EAA0E;IAC1E,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;;yEAKqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB;gDAC4C;IAC5C,KAAK,CAAC,EAAE,MAAM,CAAA;IACd;;gFAE4E;IAC5E,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ;oCACgC;IAChC,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;sBAIkB;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB;;;4BAGwB;IACxB,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB;;wDAEoD;IACpD,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;kFAC8E;IAC9E,IAAI,CAAC,EAAE,MAAM,CAAA;IACb;;;gCAG4B;IAC5B,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;6DAEyD;IACzD,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC;;8EAE0E;IAC1E,YAAY,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;IACjC,sEAAsE;IACtE,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B,2EAA2E;IAC3E,SAAS,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAA;IACpC;;;yDAGqD;IACrD,KAAK,CAAC,EAAE,eAAe,EAAE,CAAA;CAC1B;AAED,eAAO,MAAM,MAAM,EAAE,MAAM,CAAC,MAAM,CA8ChC,CAAA;AAEF;;;;;;GAMG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAWvE"}
@@ -1,4 +1,5 @@
1
1
  import Schema from '@deepseek-ai/schemastery';
2
+ import { DEFAULT_STATUS_BAR } from '../tuiDisplayPrefs.js';
2
3
  export const name = 'dsh-tui';
3
4
  // `tuiWorkspaces` must stay OUT of this code-level inject (issue #183): the
4
5
  // dsh CLI resolves the bundle's cordis.patch.yml from the FIRST copy of this
@@ -29,6 +30,24 @@ export const Config = Schema.object({
29
30
  preset: Schema.string().required(false),
30
31
  diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
31
32
  thinkingFold: Schema.union(['preview', 'full']).default('preview'),
33
+ toolBackground: Schema.union(['none', 'subtle', 'strong']).default('none'),
34
+ statusBar: Schema.object({
35
+ compact: Schema.boolean().default(DEFAULT_STATUS_BAR.compact),
36
+ model: Schema.boolean().default(DEFAULT_STATUS_BAR.model),
37
+ thinking: Schema.boolean().default(DEFAULT_STATUS_BAR.thinking),
38
+ cwd: Schema.boolean().default(DEFAULT_STATUS_BAR.cwd),
39
+ contextUsage: Schema.boolean().default(DEFAULT_STATUS_BAR.contextUsage),
40
+ cache: Schema.boolean().default(DEFAULT_STATUS_BAR.cache),
41
+ tokens: Schema.boolean().default(DEFAULT_STATUS_BAR.tokens),
42
+ tps: Schema.boolean().default(DEFAULT_STATUS_BAR.tps),
43
+ gitBranch: Schema.boolean().default(DEFAULT_STATUS_BAR.gitBranch),
44
+ sessionTitle: Schema.boolean().default(DEFAULT_STATUS_BAR.sessionTitle),
45
+ mode: Schema.boolean().default(DEFAULT_STATUS_BAR.mode),
46
+ contextBar: Schema.boolean().default(DEFAULT_STATUS_BAR.contextBar),
47
+ activity: Schema.boolean().default(DEFAULT_STATUS_BAR.activity),
48
+ trajectory: Schema.boolean().default(DEFAULT_STATUS_BAR.trajectory),
49
+ shortcutHint: Schema.boolean().default(DEFAULT_STATUS_BAR.shortcutHint),
50
+ }).default({ ...DEFAULT_STATUS_BAR }),
32
51
  modes: Schema.array(Schema.object({
33
52
  id: Schema.string(),
34
53
  label: Schema.string().required(false),
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAmCnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAonBvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAoCnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAsyBvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
@@ -23,6 +23,7 @@ import { clearResumeTarget, writeResumeTarget } from '../sessionHistory.js';
23
23
  import { resolveSessionCwd } from '../utils/workspaceRoot.js';
24
24
  import { checkForTuiUpdate, installedTuiVersion, isBootDeadlockTarget, isVersionNewer, resolveDshProfileName, resolveTuiUpdateTarget, updateTuiAndRestart } from '../update.js';
25
25
  import { getLang, isLang, resolveStartupLang, setLang, t, writeLangPref } from '../i18n.js';
26
+ import { DEFAULT_STATUS_BAR, normalizeStatusBar, normalizeToolBackground } from '../tuiDisplayPrefs.js';
26
27
  import { detectLegacyEnv, migrateLegacyDataDir, RENAMED_ENV } from '../utils/paths.js';
27
28
  import { Chat } from '../screens/Chat.js';
28
29
  import { getHostDialogStore } from './dialogs.js';
@@ -326,6 +327,8 @@ export async function apply(ctx, config) {
326
327
  // screen edits this key live through the dsh-tui namespace.
327
328
  diffLayout: config.diffLayout,
328
329
  thinkingFold: config.thinkingFold,
330
+ toolBackground: config.toolBackground,
331
+ statusBar: config.statusBar,
329
332
  handle,
330
333
  });
331
334
  // Register the dsh-tui settings namespace so the /settings screen can
@@ -336,6 +339,24 @@ export async function apply(ctx, config) {
336
339
  const scope = settingsCtx.settings.register(settingsNamespace('dsh-tui'), Schema.object({
337
340
  diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
338
341
  thinkingFold: Schema.union(['preview', 'full']).default('preview'),
342
+ toolBackground: Schema.union(['none', 'subtle', 'strong']).default('none'),
343
+ statusBar: Schema.object({
344
+ compact: Schema.boolean().default(DEFAULT_STATUS_BAR.compact),
345
+ model: Schema.boolean().default(DEFAULT_STATUS_BAR.model),
346
+ thinking: Schema.boolean().default(DEFAULT_STATUS_BAR.thinking),
347
+ cwd: Schema.boolean().default(DEFAULT_STATUS_BAR.cwd),
348
+ contextUsage: Schema.boolean().default(DEFAULT_STATUS_BAR.contextUsage),
349
+ cache: Schema.boolean().default(DEFAULT_STATUS_BAR.cache),
350
+ tokens: Schema.boolean().default(DEFAULT_STATUS_BAR.tokens),
351
+ tps: Schema.boolean().default(DEFAULT_STATUS_BAR.tps),
352
+ gitBranch: Schema.boolean().default(DEFAULT_STATUS_BAR.gitBranch),
353
+ sessionTitle: Schema.boolean().default(DEFAULT_STATUS_BAR.sessionTitle),
354
+ mode: Schema.boolean().default(DEFAULT_STATUS_BAR.mode),
355
+ contextBar: Schema.boolean().default(DEFAULT_STATUS_BAR.contextBar),
356
+ activity: Schema.boolean().default(DEFAULT_STATUS_BAR.activity),
357
+ trajectory: Schema.boolean().default(DEFAULT_STATUS_BAR.trajectory),
358
+ shortcutHint: Schema.boolean().default(DEFAULT_STATUS_BAR.shortcutHint),
359
+ }).default({ ...DEFAULT_STATUS_BAR }),
339
360
  // No default on purpose: an unset `lang` keeps the field showing
340
361
  // the effective language (see the section's format below) and lets
341
362
  // cordis.yml / lang.json keep their precedence.
@@ -355,15 +376,17 @@ export async function apply(ctx, config) {
355
376
  writeLangPref(value.lang);
356
377
  }
357
378
  };
358
- // thinkingFold rides the same namespace: /settings writes it live and
359
- // the channel picks it up at the next step seal.
360
- const applyThinkingFold = (value) => {
379
+ // Display preferences ride the same namespace: /settings writes them
380
+ // live and future render consumers observe the channel version bump.
381
+ const applyDisplay = (value) => {
361
382
  channel.setThinkingFold(value.thinkingFold ?? config.thinkingFold ?? 'preview');
383
+ channel.setToolBackground(normalizeToolBackground(value.toolBackground ?? config.toolBackground));
384
+ channel.setStatusBar(normalizeStatusBar(value.statusBar ?? config.statusBar));
362
385
  };
363
386
  const apply = (next) => {
364
387
  applyLayout(next);
365
388
  applyLang(next);
366
- applyThinkingFold(next);
389
+ applyDisplay(next);
367
390
  };
368
391
  apply(scope.get());
369
392
  scope.watch(next => {
@@ -379,6 +402,7 @@ export async function apply(ctx, config) {
379
402
  const unregister = settingsSections.register({
380
403
  ns: 'dsh-tui',
381
404
  title: 'dsh-tui',
405
+ groups: [{ id: 'status-bar', title: 'Status bar', descriptions: { zh: '底栏设置' } }],
382
406
  fields: [
383
407
  {
384
408
  path: ['lang'],
@@ -423,6 +447,154 @@ export async function apply(ctx, config) {
423
447
  { value: 'full', label: 'Full until turn end', descriptions: { zh: '展开至轮末' } },
424
448
  ],
425
449
  },
450
+ {
451
+ path: ['toolBackground'],
452
+ label: 'Tool background',
453
+ descriptions: { zh: '工具卡背景' },
454
+ hint: 'Choose whether tool-call cards add no, subtle, or strong background emphasis.',
455
+ hintDescriptions: { zh: '选择工具调用卡片不添加、轻微或明显的背景强调。' },
456
+ kind: 'select',
457
+ options: [
458
+ { value: 'none', label: 'None', descriptions: { zh: '无' } },
459
+ { value: 'subtle', label: 'Subtle', descriptions: { zh: '轻微' } },
460
+ { value: 'strong', label: 'Strong', descriptions: { zh: '明显' } },
461
+ ],
462
+ },
463
+ {
464
+ path: ['statusBar', 'compact'],
465
+ label: 'Compact status bar',
466
+ descriptions: { zh: '紧凑状态栏' },
467
+ hint: 'Prefer the compact status presentation when terminal space allows.',
468
+ hintDescriptions: { zh: '终端空间允许时优先使用紧凑状态栏布局。' },
469
+ group: 'status-bar',
470
+ kind: 'boolean',
471
+ },
472
+ {
473
+ path: ['statusBar', 'model'],
474
+ label: 'Show model',
475
+ descriptions: { zh: '显示模型' },
476
+ hint: 'Show the live model id in the status bar.',
477
+ hintDescriptions: { zh: '在状态栏显示当前模型标识。' },
478
+ group: 'status-bar',
479
+ kind: 'boolean',
480
+ },
481
+ {
482
+ path: ['statusBar', 'thinking'],
483
+ label: 'Show thinking',
484
+ descriptions: { zh: '显示思考' },
485
+ hint: 'Show the live reasoning effort or thinking mode.',
486
+ hintDescriptions: { zh: '显示当前推理强度或思考模式。' },
487
+ group: 'status-bar',
488
+ kind: 'boolean',
489
+ },
490
+ {
491
+ path: ['statusBar', 'cwd'],
492
+ label: 'Show working directory',
493
+ descriptions: { zh: '显示工作目录' },
494
+ hint: 'Show the session working directory.',
495
+ hintDescriptions: { zh: '显示当前会话的工作目录。' },
496
+ group: 'status-bar',
497
+ kind: 'boolean',
498
+ },
499
+ {
500
+ path: ['statusBar', 'contextUsage'],
501
+ label: 'Show context usage',
502
+ descriptions: { zh: '显示上下文用量' },
503
+ hint: 'Show current context-window consumption.',
504
+ hintDescriptions: { zh: '显示当前上下文窗口占用情况。' },
505
+ group: 'status-bar',
506
+ kind: 'boolean',
507
+ },
508
+ {
509
+ path: ['statusBar', 'cache'],
510
+ label: 'Show cache',
511
+ descriptions: { zh: '显示缓存' },
512
+ hint: 'Show prompt-cache hit information.',
513
+ hintDescriptions: { zh: '显示提示词缓存命中信息。' },
514
+ group: 'status-bar',
515
+ kind: 'boolean',
516
+ },
517
+ {
518
+ path: ['statusBar', 'tokens'],
519
+ label: 'Show token totals',
520
+ descriptions: { zh: '显示 Token 总量' },
521
+ hint: 'Show running input and output token totals.',
522
+ hintDescriptions: { zh: '显示累计输入与输出 Token。' },
523
+ group: 'status-bar',
524
+ kind: 'boolean',
525
+ },
526
+ {
527
+ path: ['statusBar', 'tps'],
528
+ label: 'Show output speed',
529
+ descriptions: { zh: '显示输出速度' },
530
+ hint: 'Show live and recent tokens-per-second metrics.',
531
+ hintDescriptions: { zh: '显示实时及近期每秒 Token 指标。' },
532
+ group: 'status-bar',
533
+ kind: 'boolean',
534
+ },
535
+ {
536
+ path: ['statusBar', 'gitBranch'],
537
+ label: 'Show git branch',
538
+ descriptions: { zh: '显示 Git 分支' },
539
+ hint: 'Show the current git branch when available.',
540
+ hintDescriptions: { zh: '可用时显示当前 Git 分支。' },
541
+ group: 'status-bar',
542
+ kind: 'boolean',
543
+ },
544
+ {
545
+ path: ['statusBar', 'sessionTitle'],
546
+ label: 'Show session title',
547
+ descriptions: { zh: '显示会话标题' },
548
+ hint: 'Show the current session title.',
549
+ hintDescriptions: { zh: '显示当前会话标题。' },
550
+ group: 'status-bar',
551
+ kind: 'boolean',
552
+ },
553
+ {
554
+ path: ['statusBar', 'mode'],
555
+ label: 'Show session mode',
556
+ descriptions: { zh: '显示会话模式' },
557
+ hint: 'Show the active non-default session mode.',
558
+ hintDescriptions: { zh: '显示当前启用的非默认会话模式。' },
559
+ group: 'status-bar',
560
+ kind: 'boolean',
561
+ },
562
+ {
563
+ path: ['statusBar', 'contextBar'],
564
+ label: 'Show context progress bar',
565
+ descriptions: { zh: '显示上下文进度条' },
566
+ hint: 'Show the segmented context progress bar on its own footer row.',
567
+ hintDescriptions: { zh: '在底部单独一行显示分段上下文进度条。' },
568
+ group: 'status-bar',
569
+ kind: 'boolean',
570
+ },
571
+ {
572
+ path: ['statusBar', 'activity'],
573
+ label: 'Show activity summary',
574
+ descriptions: { zh: '显示活动摘要' },
575
+ hint: 'Show the idle working-activity summary.',
576
+ hintDescriptions: { zh: '显示空闲时的工作活动摘要。' },
577
+ group: 'status-bar',
578
+ kind: 'boolean',
579
+ },
580
+ {
581
+ path: ['statusBar', 'trajectory'],
582
+ label: 'Show trajectory strip',
583
+ descriptions: { zh: '显示轨迹条' },
584
+ hint: 'Show the animated mini trajectory strip at the footer edge.',
585
+ hintDescriptions: { zh: '在状态栏边缘显示动态迷你轨迹条。' },
586
+ group: 'status-bar',
587
+ kind: 'boolean',
588
+ },
589
+ {
590
+ path: ['statusBar', 'shortcutHint'],
591
+ label: 'Show shortcut reminder',
592
+ descriptions: { zh: '显示快捷键提示' },
593
+ hint: 'Control only the idle `? for shortcuts` reminder; pressing ? and the Esc shortcut hints are unaffected.',
594
+ hintDescriptions: { zh: '仅控制空闲时的 `? for shortcuts` 提示;按 ? 打开快捷键以及 Esc 快捷提示均不受影响。' },
595
+ group: 'status-bar',
596
+ kind: 'boolean',
597
+ },
426
598
  ],
427
599
  });
428
600
  ctx.effect(() => unregister);
@@ -21,6 +21,15 @@ export interface TuiSettingsFieldOption {
21
21
  /** Provider-owned translations for the label. */
22
22
  descriptions?: LocalizedDescriptions;
23
23
  }
24
+ /** Optional navigation group inside one settings section. */
25
+ export interface TuiSettingsGroup {
26
+ /** Stable identifier, unique inside the section. */
27
+ id: string;
28
+ /** Group title (English; also the fallback). */
29
+ title: string;
30
+ /** Provider-owned translations for the title. */
31
+ descriptions?: LocalizedDescriptions;
32
+ }
24
33
  /** The write one field's draft stages when the section is saved. */
25
34
  export type TuiSettingsFieldWrite = {
26
35
  kind: 'set';
@@ -42,6 +51,8 @@ export interface TuiSettingsField {
42
51
  hint?: string;
43
52
  /** Provider-owned translations for the hint. */
44
53
  hintDescriptions?: LocalizedDescriptions;
54
+ /** Optional group id; grouped fields render on that group's subpage. */
55
+ group?: string;
45
56
  kind: TuiSettingsFieldKind;
46
57
  /** Choices for `kind: 'select'` (ignored otherwise). */
47
58
  options?: readonly TuiSettingsFieldOption[];
@@ -83,6 +94,8 @@ export interface TuiSettingsSection {
83
94
  title: string;
84
95
  /** Provider-owned translations for the title. */
85
96
  descriptions?: LocalizedDescriptions;
97
+ /** Optional navigation groups, in display order. */
98
+ groups?: readonly TuiSettingsGroup[];
86
99
  /** Editable fields, in display order. */
87
100
  fields: readonly TuiSettingsField[];
88
101
  }
@@ -1 +1 @@
1
- {"version":3,"file":"settings-sections.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/settings-sections.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AACtD,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gBAAgB,CAAA;AAG3D,iEAAiE;AACjE,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,CAAA;AAE3E,MAAM,WAAW,sBAAsB;IACrC,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,YAAY,CAAC,EAAE,qBAAqB,CAAA;CACrC;AAED,oEAAoE;AACpE,MAAM,MAAM,qBAAqB,GAC7B;IAAE,IAAI,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,OAAO,CAAA;CAAE,GAC/B;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,CAAA;AAErB,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,IAAI,EAAE,SAAS,MAAM,EAAE,CAAA;IACvB,sDAAsD;IACtD,KAAK,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,uDAAuD;IACvD,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,gDAAgD;IAChD,gBAAgB,CAAC,EAAE,qBAAqB,CAAA;IACxC,IAAI,EAAE,oBAAoB,CAAA;IAC1B,wDAAwD;IACxD,OAAO,CAAC,EAAE,SAAS,sBAAsB,EAAE,CAAA;IAC3C,uDAAuD;IACvD,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB;;;;;;OAMG;IACH,MAAM,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;IACxB;;;OAGG;IACH,MAAM,CAAC,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAAA;IAC/B;;;;;;OAMG;IACH,KAAK,CAAC,CAAC,IAAI,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAAA;CACxD;AAED,2DAA2D;AAC3D,MAAM,WAAW,kBAAkB;IACjC;;;;OAIG;IACH,EAAE,EAAE,MAAM,CAAA;IACV,kDAAkD;IAClD,KAAK,EAAE,MAAM,CAAA;IACb,iDAAiD;IACjD,YAAY,CAAC,EAAE,qBAAqB,CAAA;IACpC,yCAAyC;IACzC,MAAM,EAAE,SAAS,gBAAgB,EAAE,CAAA;CACpC;AAED,6EAA6E;AAC7E,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,IAAI,CAAA;IACjD,IAAI,IAAI,SAAS,kBAAkB,EAAE,CAAA;IACrC,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;CAC5C;AAED,OAAO,QAAQ,qBAAqB,CAAC;IACnC,UAAU,OAAO;QACf,mBAAmB,EAAE,0BAA0B,CAAA;KAChD;CACF;AAED,eAAO,MAAM,IAAI,8BAA8B,CAAA;AAE/C;;;GAGG;AACH,qBAAa,0BAA2B,SAAQ,OAAO;gBACzC,GAAG,EAAE,OAAO;IAmBxB,QAAQ,CAAC,OAAO,EAAE,kBAAkB,GAAG,MAAM,IAAI;IASjD,iDAAiD;IACjD,IAAI,IAAI,SAAS,kBAAkB,EAAE;IAQrC,sDAAsD;IACtD,OAAO,CAAC,EAAE,EAAE,MAAM,GAAG,kBAAkB,GAAG,SAAS;IAQnD;;;OAGG;IACH,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI;CAS5C;AAgED,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,0BAA0B,GAAG,SAAS,GAAG,uBAAuB,GAAG,SAAS,CAO5H;AAED,eAAe,0BAA0B,CAAA"}
1
+ {"version":3,"file":"settings-sections.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/settings-sections.ts"],"names":[],"mappings":"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"}