@deepseek-harness-tui/dsh-tui 0.11.0 → 0.11.1

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 (131) hide show
  1. package/README.md +6 -1
  2. package/cordis.patch.yml +4 -3
  3. package/cordis.yml +3 -2
  4. package/lib/types/activityPrefs.d.ts +24 -0
  5. package/lib/types/activityPrefs.d.ts.map +1 -1
  6. package/lib/types/activityPrefs.js +50 -1
  7. package/lib/types/adapter/channel/state.d.ts.map +1 -1
  8. package/lib/types/adapter/channel/state.js +3 -1
  9. package/lib/types/adapter/channel/ui-policy.d.ts +1 -1
  10. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  11. package/lib/types/adapter/channel/ui-policy.js +1 -1
  12. package/lib/types/adapter/ports/channel-ui.d.ts +8 -4
  13. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  14. package/lib/types/adapter/ports/channel-view.d.ts +0 -12
  15. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  16. package/lib/types/adapter/ports/channel.d.ts +4 -1
  17. package/lib/types/adapter/ports/channel.d.ts.map +1 -1
  18. package/lib/types/components/ActivityLine.d.ts +7 -2
  19. package/lib/types/components/ActivityLine.d.ts.map +1 -1
  20. package/lib/types/components/ActivityLine.js +0 -8
  21. package/lib/types/components/MessageList.d.ts.map +1 -1
  22. package/lib/types/components/MessageList.js +2 -1
  23. package/lib/types/components/PromptInput.d.ts.map +1 -1
  24. package/lib/types/components/PromptInput.js +48 -2
  25. package/lib/types/components/SplitDiffView.d.ts.map +1 -1
  26. package/lib/types/components/SplitDiffView.js +2 -1
  27. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  28. package/lib/types/components/SubagentCard.js +12 -6
  29. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  30. package/lib/types/components/SubagentDetailScene.js +8 -3
  31. package/lib/types/components/design-system/HintLine.d.ts +1 -2
  32. package/lib/types/components/design-system/HintLine.d.ts.map +1 -1
  33. package/lib/types/components/design-system/HintLine.js +1 -2
  34. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
  35. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  36. package/lib/types/components/messages/AssistantThinkingMessage.js +2 -1
  37. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -2
  38. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  39. package/lib/types/components/messages/AssistantToolUseMessage.js +30 -26
  40. package/lib/types/dsh-adapter/activity-store.d.ts +221 -0
  41. package/lib/types/dsh-adapter/activity-store.d.ts.map +1 -0
  42. package/lib/types/dsh-adapter/activity-store.js +359 -0
  43. package/lib/types/dsh-adapter/channel/background-action.d.ts +1 -1
  44. package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
  45. package/lib/types/dsh-adapter/channel/background-action.js +1 -0
  46. package/lib/types/dsh-adapter/channel/binding-events.d.ts +8 -6
  47. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  48. package/lib/types/dsh-adapter/channel/binding-events.js +5 -4
  49. package/lib/types/dsh-adapter/channel/compaction.d.ts +0 -1
  50. package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
  51. package/lib/types/dsh-adapter/channel/compaction.js +0 -2
  52. package/lib/types/dsh-adapter/channel/model-switch.d.ts +1 -2
  53. package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
  54. package/lib/types/dsh-adapter/channel/model-switch.js +1 -1
  55. package/lib/types/dsh-adapter/channel/session-adoption.d.ts +1 -1
  56. package/lib/types/dsh-adapter/channel/session-adoption.d.ts.map +1 -1
  57. package/lib/types/dsh-adapter/channel/session-adoption.js +1 -0
  58. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +1 -1
  59. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
  60. package/lib/types/dsh-adapter/channel/session-live-adoption.js +1 -0
  61. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  62. package/lib/types/dsh-adapter/channel/session-metadata.js +4 -2
  63. package/lib/types/dsh-adapter/channel/session-reset.d.ts +1 -1
  64. package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
  65. package/lib/types/dsh-adapter/channel/session-reset.js +0 -1
  66. package/lib/types/dsh-adapter/channel/session-resume.d.ts +1 -1
  67. package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
  68. package/lib/types/dsh-adapter/channel/session-resume.js +1 -0
  69. package/lib/types/dsh-adapter/channel/state.d.ts +6 -1
  70. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  71. package/lib/types/dsh-adapter/channel/state.js +2 -2
  72. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +21 -1
  73. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  74. package/lib/types/dsh-adapter/channel/subagent-projection.js +173 -6
  75. package/lib/types/dsh-adapter/channel/types.d.ts +1 -1
  76. package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
  77. package/lib/types/dsh-adapter/channel.d.ts +1 -1
  78. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  79. package/lib/types/dsh-adapter/channel.js +15 -14
  80. package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
  81. package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
  82. package/lib/types/dsh-adapter/compat/index.js +1 -1
  83. package/lib/types/dsh-adapter/compat/sessionLog.d.ts +18 -0
  84. package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
  85. package/lib/types/dsh-adapter/compat/sessionLog.js +18 -3
  86. package/lib/types/dsh-adapter/compat/settings.d.ts +2 -0
  87. package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -1
  88. package/lib/types/dsh-adapter/compat/settings.js +14 -0
  89. package/lib/types/dsh-adapter/contract.d.ts +4 -3
  90. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  91. package/lib/types/dsh-adapter/contract.js +5 -3
  92. package/lib/types/dsh-adapter/plugin.d.ts +1 -1
  93. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  94. package/lib/types/dsh-adapter/plugin.js +25 -8
  95. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  96. package/lib/types/dsh-adapter/settings-sections.js +7 -4
  97. package/lib/types/dsh-adapter/subagents.d.ts +16 -3
  98. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  99. package/lib/types/dsh-adapter/subagents.js +86 -24
  100. package/lib/types/history.d.ts +13 -1
  101. package/lib/types/history.d.ts.map +1 -1
  102. package/lib/types/history.js +15 -2
  103. package/lib/types/i18n.d.ts +90 -1
  104. package/lib/types/i18n.d.ts.map +1 -1
  105. package/lib/types/i18n.js +55 -1
  106. package/lib/types/ink/log-update.d.ts.map +1 -1
  107. package/lib/types/ink/log-update.js +19 -1
  108. package/lib/types/ink/selection.d.ts +28 -0
  109. package/lib/types/ink/selection.d.ts.map +1 -1
  110. package/lib/types/ink/selection.js +36 -1
  111. package/lib/types/screens/Chat.d.ts +4 -1
  112. package/lib/types/screens/Chat.d.ts.map +1 -1
  113. package/lib/types/screens/Chat.js +13 -5
  114. package/lib/types/screens/StatusLine.d.ts +5 -1
  115. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  116. package/lib/types/screens/StatusLine.js +2 -2
  117. package/lib/types/utils/fold-long-lines.d.ts +1 -1
  118. package/lib/types/utils/fold-long-lines.js +1 -1
  119. package/lib/types/working-activity.d.ts +10 -9
  120. package/lib/types/working-activity.d.ts.map +1 -1
  121. package/lib/types/working-activity.js +17 -10
  122. package/package.json +99 -95
  123. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +0 -19
  124. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts.map +0 -1
  125. package/lib/types/components/design-system/KeyboardShortcutHint.js +0 -14
  126. package/lib/types/dsh-adapter/channel/activity.d.ts +0 -20
  127. package/lib/types/dsh-adapter/channel/activity.d.ts.map +0 -1
  128. package/lib/types/dsh-adapter/channel/activity.js +0 -113
  129. package/lib/types/terminal-utils/terminal.d.ts +0 -18
  130. package/lib/types/terminal-utils/terminal.d.ts.map +0 -1
  131. package/lib/types/terminal-utils/terminal.js +0 -96
@@ -24,7 +24,7 @@ import { imagePathMediaType, parsePastedImagePath, stageClipboardFilePaths } fro
24
24
  import { editInExternalEditor } from '../utils/externalEditor.js';
25
25
  import { setPromptEditorNode, EditorButton } from './PromptEditor.js';
26
26
  import { isHiddenCommandName, parseCommandName } from '../commands.js';
27
- import { appendHistory } from '../history.js';
27
+ import { appendHistory, HISTORY_LIMIT, loadHistoryOldestFirst } from '../history.js';
28
28
  import { mentionAtCaret } from '../utils/mentions.js';
29
29
  import { preserveSelection } from '../utils/fileSuggestions.js';
30
30
  import { isMod } from '../utils/modifiers.js';
@@ -35,7 +35,6 @@ import { HelpMenu } from './HelpMenu.js';
35
35
  import { OverlayAbove } from './OverlayAbove.js';
36
36
  import { SuggestionCard, cardContentWidth } from './SuggestionCard.js';
37
37
  import { filterLiveImageBindings, isUsableDraftSnapshot, resolveBindingGeneration, } from './promptDraftCache.js';
38
- const HISTORY_LIMIT = 50;
39
38
  /**
40
39
  * Visible text of the session-entry control at the head of the input row:
41
40
  * U+2338 APL FUNCTIONAL SYMBOL QUAD COLON plus a separator space.
@@ -535,6 +534,8 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
535
534
  const history = React.useRef([]);
536
535
  const historyIndex = React.useRef(-1);
537
536
  const historyDraft = React.useRef({ text: '', images: [] });
537
+ /** The persisted history is read lazily, once per mount (see seedHistory). */
538
+ const historySeeded = React.useRef(false);
538
539
  /** Visible `[Image #N]` labels are presentation only; this sidecar carries
539
540
  * the non-reusable capability for the current draft. History/rewind text
540
541
  * restored without this map can never bind to a later image by accident. */
@@ -1096,7 +1097,26 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
1096
1097
  return;
1097
1098
  }
1098
1099
  };
1100
+ /**
1101
+ * Seed the walk with the persisted history (issue #986). `↑`/`↓` used to
1102
+ * see only what this process submitted, so a restart lost every earlier
1103
+ * entry. Seeding before the first push keeps ONE chronological list —
1104
+ * persisted entries first, this run's submits behind them — instead of two
1105
+ * lists to merge at recall time. Restored text carries no image capability
1106
+ * (the file stores text only), which is also what keeps a recalled entry
1107
+ * from binding to a later staged image by accident.
1108
+ */
1109
+ const seedHistory = () => {
1110
+ if (historySeeded.current)
1111
+ return;
1112
+ historySeeded.current = true;
1113
+ history.current = loadHistoryOldestFirst().map(entry => ({ text: entry.text, images: [] }));
1114
+ historyIndex.current = -1;
1115
+ };
1099
1116
  const rememberHistory = (text, images) => {
1117
+ // Both entries into the walk (a submit and ↑) must see the persisted
1118
+ // prefix, so seed here rather than merging two lists later.
1119
+ seedHistory();
1100
1120
  history.current.push({
1101
1121
  text,
1102
1122
  images: images.map(image => ({ ...image })),
@@ -1203,6 +1223,30 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
1203
1223
  setFileSelected(0);
1204
1224
  channel.notify(t('input-retracted'), { timeoutMs: 2000 });
1205
1225
  };
1226
+ /**
1227
+ * Withdraw the queued copy of the text `↑` just recalled (issue #986): the
1228
+ * message is still parked in the inbox, so editing it and sending again
1229
+ * would run the same text twice. Alt+Up withdraws explicitly; walking the
1230
+ * history to the same text has to land in the same place. The newest match
1231
+ * wins — `↑` walks newest-first and the queue is FIFO. A message the
1232
+ * running turn already claimed cannot be withdrawn, and saying so beats
1233
+ * pretending it was.
1234
+ */
1235
+ const retractRecalledCopy = (text) => {
1236
+ let target;
1237
+ for (const item of channel.pending) {
1238
+ if (item.text === text)
1239
+ target = item;
1240
+ }
1241
+ if (target === undefined)
1242
+ return;
1243
+ if (channel.removePending(target.id)) {
1244
+ channel.notify(t('input-retracted'), { timeoutMs: 2000 });
1245
+ }
1246
+ else {
1247
+ channel.notify(t('input-cannot-retract'), { color: 'warning', timeoutMs: 2500 });
1248
+ }
1249
+ };
1206
1250
  /**
1207
1251
  * Ctrl+Enter: abort the running turn and send the input immediately — the
1208
1252
  * model stops what it is doing and starts on this message right away.
@@ -2086,6 +2130,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
2086
2130
  setSelectedCommand(index => index <= 0 ? suggestions.length - 1 : index - 1);
2087
2131
  return;
2088
2132
  }
2133
+ seedHistory();
2089
2134
  if (history.current.length === 0)
2090
2135
  return;
2091
2136
  if (historyIndex.current < 0) {
@@ -2101,6 +2146,7 @@ export function PromptInput({ channel, suspended = false, draftCache, helpOpen,
2101
2146
  const entry = history.current[historyIndex.current];
2102
2147
  if (entry === undefined)
2103
2148
  return;
2149
+ retractRecalledCopy(entry.text);
2104
2150
  updateFoldBlock(null);
2105
2151
  restoreDraftImages(entry);
2106
2152
  setInput(entry.text);
@@ -1 +1 @@
1
- {"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AAG/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAwR3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,cAAuB,EACvB,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CAwFlB"}
1
+ {"version":3,"file":"SplitDiffView.d.ts","sourceRoot":"","sources":["../../../src/components/SplitDiffView.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAA;AAO7D,OAAO,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AACjE,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AAI/E,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AAwR3D,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,KAAK,EACL,OAAO,EACP,OAAO,EACP,cAAuB,EACvB,MAAM,GACP,EAAE;IACD,QAAQ,CAAC,KAAK,EAAE,SAAS,YAAY,EAAE,CAAA;IACvC,8EAA8E;IAC9E,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,qEAAqE;IACrE,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAA;IACzB,QAAQ,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAA;CAC3C,GAAG,KAAK,CAAC,SAAS,CAwFlB"}
@@ -10,6 +10,7 @@ import { highlightLines, syntaxThemeSignature } from '../terminal-utils/syntaxRu
10
10
  export { chalkFromToken } from '../terminal-utils/syntaxTheme.js';
11
11
  export { parseAnsiRuns, highlightLines } from '../terminal-utils/syntaxRuns.js';
12
12
  import { getTheme } from '../theme.js';
13
+ import { t } from '../i18n.js';
13
14
  import { useTheme } from './design-system/ThemeProvider.js';
14
15
  import { revealLinesOf } from './smoothReveal.js';
15
16
  /** Replace tabs so width math and alignment hold (pi convention). */
@@ -274,5 +275,5 @@ export function SplitDiffView({ diffs, width, maxRows, verbose, toolBackground =
274
275
  ? undefined
275
276
  : { segments: newRuns !== undefined ? mergeRuns(newRuns, row.newWords) : row.newWords };
276
277
  return (_jsxs(Box, { flexDirection: "row", children: [_jsx(PaneLine, { side: oldSide, kind: row.kind === 'add' ? 'context' : row.kind, tone: "old", width: paneWidth, toolBackground: toolBackground }), _jsx(Box, { width: 1, flexShrink: 0, children: _jsx(Text, { dimColor: true, children: "\u2502" }) }), _jsx(PaneLine, { side: newSide, kind: row.kind === 'del' ? 'context' : row.kind, tone: "new", width: paneWidth, toolBackground: toolBackground, padLeft: true })] }, index));
277
- }), hidden > 0 && (_jsx(Text, { dimColor: true, children: `… +${hidden} lines (ctrl+o to expand)` }))] }));
278
+ }), hidden > 0 && (_jsx(Text, { dimColor: true, children: t('lines-folded-expand', { n: hidden }) }))] }));
278
279
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentCard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAGhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAO9D,MAAM,WAAW,iBAAiB;IAAG,QAAQ,EAAE,aAAa,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAAE;AAEpH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,SAAS,CA0C/F"}
1
+ {"version":3,"file":"SubagentCard.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAmB,MAAM,OAAO,CAAA;AAEvC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAGhE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,8BAA8B,CAAA;AAO9D,MAAM,WAAW,iBAAiB;IAAG,QAAQ,EAAE,aAAa,CAAC;IAAC,OAAO,CAAC,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAAE;AAEpH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,SAAS,CAgD/F"}
@@ -9,7 +9,11 @@ function formatDuration(ms) {
9
9
  }
10
10
  export function SubagentCard({ subagent, focused, onClick }) {
11
11
  const running = subagent.status === 'running' || subagent.status === 'starting';
12
- const elapsed = subagent.completedAt ? subagent.completedAt - subagent.startedAt : Date.now() - subagent.startedAt;
12
+ // Only a live run ticks; discovered history (`unknown`) has no end time to
13
+ // count from, so it must not accumulate a fake growing duration.
14
+ const elapsed = running
15
+ ? Date.now() - subagent.startedAt
16
+ : subagent.completedAt !== undefined ? subagent.completedAt - subagent.startedAt : undefined;
13
17
  const total = subagent.tokens?.total ?? ((subagent.tokens?.input ?? 0) + (subagent.tokens?.output ?? 0) || 0);
14
18
  // Mouse affordance: clickable cards tint on hover; the keyboard-focused
15
19
  // card keeps its brand-color header (no double highlight).
@@ -22,12 +26,14 @@ export function SubagentCard({ subagent, focused, onClick }) {
22
26
  const liveLine = running ? subagent.output[subagent.output.length - 1] : undefined;
23
27
  const minimal = isMinimalMode();
24
28
  const glyph = running ? (minimal ? '·' : '🟡')
25
- : subagent.status === 'failed' || subagent.status === 'cancelled' ? (minimal ? '×' : '🔴')
26
- : (minimal ? '✓' : '🟢');
29
+ : subagent.status === 'unknown' ? (minimal ? '·' : '⚪')
30
+ : subagent.status === 'failed' || subagent.status === 'cancelled' ? (minimal ? '×' : '🔴')
31
+ : (minimal ? '✓' : '🟢');
27
32
  const glyphColor = minimal ? undefined
28
33
  : running ? 'warning'
29
- : subagent.status === 'failed' || subagent.status === 'cancelled' ? 'error'
30
- : 'success';
34
+ : subagent.status === 'unknown' ? 'subtle'
35
+ : subagent.status === 'failed' || subagent.status === 'cancelled' ? 'error'
36
+ : 'success';
31
37
  const hoverTint = onClick !== undefined && hovered && !focused;
32
- return _jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, onClick: onClick, onMouseEnter: onClick !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined ? () => setHovered(false) : undefined, backgroundColor: hoverTint ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: glyphColor, children: glyph }), _jsx(Text, { bold: true, color: focused ? 'accent' : undefined, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${total || '—'} tok · ${subagent.toolCalls.length} tools` })] })] }), liveLine && _jsx(Text, { dimColor: true, wrap: "truncate", children: ` │ ${liveLine}` })] });
38
+ return _jsxs(Box, { flexDirection: "column", paddingLeft: 1, marginBottom: 1, onClick: onClick, onMouseEnter: onClick !== undefined ? () => setHovered(true) : undefined, onMouseLeave: onClick !== undefined ? () => setHovered(false) : undefined, backgroundColor: hoverTint ? 'userMessageBackgroundHover' : undefined, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: glyphColor, children: glyph }), _jsx(Text, { bold: true, color: focused ? 'accent' : undefined, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsxs(Text, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsxs(Text, { dimColor: true, children: [elapsed !== undefined ? ` · ${formatDuration(elapsed)} · ` : ' · ', total || '—', " tok \u00B7 ", subagent.toolCalls.length, " tools"] })] })] }), liveLine && _jsx(Text, { dimColor: true, wrap: "truncate", children: ` │ ${liveLine}` })] });
33
39
  }
@@ -1 +1 @@
1
- {"version":3,"file":"SubagentDetailScene.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDetailScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAuGhE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,CA8M5C"}
1
+ {"version":3,"file":"SubagentDetailScene.d.ts","sourceRoot":"","sources":["../../../src/components/SubagentDetailScene.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAA;AAwGhE,MAAM,WAAW,wBAAwB;IACvC,QAAQ,EAAE,aAAa,CAAA;IACvB,MAAM,EAAE,MAAM,IAAI,CAAA;IAClB,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAA;CACxC;AAED;;;;;;GAMG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,WAAW,GACZ,EAAE,wBAAwB,GAAG,KAAK,CAAC,SAAS,CAiN5C"}
@@ -28,6 +28,8 @@ function statusGlyph(status) {
28
28
  return { glyph: minimal ? '×' : '🔴', color: minimal ? undefined : 'error', label: 'failed' };
29
29
  if (status === 'cancelled')
30
30
  return { glyph: minimal ? '×' : '🔴', color: minimal ? undefined : 'error', label: 'cancelled' };
31
+ if (status === 'unknown')
32
+ return { glyph: minimal ? '·' : '⚪', color: minimal ? undefined : 'subtle', label: 'history' };
31
33
  return { glyph: minimal ? '·' : '🟡', color: minimal ? undefined : 'warning', label: 'running' };
32
34
  }
33
35
  const PAGES = ['summary', 'output', 'tools'];
@@ -37,7 +39,7 @@ function StatRow({ label, children }) {
37
39
  }
38
40
  /** Two-column key/value stats grid (Kimi Code settled summary style). */
39
41
  function StatGrid({ subagent, totalTokens, elapsed, statusLabel, statusColor }) {
40
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatRow, { label: t('subagent-status-label'), children: _jsx(Text, { color: statusColor, children: statusLabel }) }), _jsx(StatRow, { label: t('subagent-model'), children: _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }) }), _jsx(StatRow, { label: t('subagent-duration'), children: _jsx(Text, { children: formatDuration(elapsed) }) }), _jsx(StatRow, { label: "tokens", children: _jsxs(Text, { children: [totalTokens || '—', subagent.tokens?.input !== undefined ? ` (in ${subagent.tokens.input} · out ${subagent.tokens.output ?? 0})` : ''] }) }), _jsx(StatRow, { label: t('subagent-tools'), children: _jsx(Text, { children: subagent.toolCalls.length }) }), _jsx(StatRow, { label: t('subagent-started'), children: _jsx(Text, { children: formatTimestamp(subagent.startedAt) }) }), subagent.completedAt !== undefined && (_jsx(StatRow, { label: t('subagent-completed'), children: _jsx(Text, { children: formatTimestamp(subagent.completedAt) }) }))] }));
42
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatRow, { label: t('subagent-status-label'), children: _jsx(Text, { color: statusColor, children: statusLabel }) }), _jsx(StatRow, { label: t('subagent-model'), children: _jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }) }), _jsx(StatRow, { label: t('subagent-duration'), children: _jsx(Text, { children: elapsed !== undefined ? formatDuration(elapsed) : '—' }) }), _jsx(StatRow, { label: "tokens", children: _jsxs(Text, { children: [totalTokens || '—', subagent.tokens?.input !== undefined ? ` (in ${subagent.tokens.input} · out ${subagent.tokens.output ?? 0})` : ''] }) }), _jsx(StatRow, { label: t('subagent-tools'), children: _jsx(Text, { children: subagent.toolCalls.length }) }), _jsx(StatRow, { label: t('subagent-started'), children: _jsx(Text, { children: formatTimestamp(subagent.startedAt) }) }), subagent.completedAt !== undefined && (_jsx(StatRow, { label: t('subagent-completed'), children: _jsx(Text, { children: formatTimestamp(subagent.completedAt) }) }))] }));
41
43
  }
42
44
  /** Tool args line: JSON-looking args get cli-highlight syntax colors (loaded
43
45
  * lazily through the shared promise); anything else stays a dim flat line. */
@@ -77,7 +79,10 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
77
79
  const { rows, columns } = useTerminalSize();
78
80
  const [page, setPage] = React.useState('summary');
79
81
  const isRunning = subagent.status === 'running' || subagent.status === 'starting';
80
- const elapsed = subagent.completedAt ? subagent.completedAt - subagent.startedAt : Date.now() - subagent.startedAt;
82
+ // Only a live run ticks; discovered history (`unknown`) shows no duration.
83
+ const elapsed = isRunning
84
+ ? Date.now() - subagent.startedAt
85
+ : subagent.completedAt !== undefined ? subagent.completedAt - subagent.startedAt : undefined;
81
86
  const info = statusGlyph(subagent.status);
82
87
  const totalTokens = subagent.tokens?.total ?? ((subagent.tokens?.input ?? 0) + (subagent.tokens?.output ?? 0) || 0);
83
88
  const pageIndex = PAGES.indexOf(page);
@@ -137,6 +142,6 @@ export function SubagentDetailScene({ subagent, onBack, onInterrupt, }) {
137
142
  const active = page === name;
138
143
  return (_jsxs(React.Fragment, { children: [_jsx(Box, { onClick: () => setPage(name), backgroundColor: !active ? 'userMessageBackgroundHover' : undefined, children: _jsx(Text, { color: active ? 'accent' : undefined, bold: active, inverse: active, children: ` ${label} ` }) }), _jsx(Text, { dimColor: true, children: name === PAGES[PAGES.length - 1] ? '' : '│' })] }, name));
139
144
  };
140
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsx(Text, { dimColor: true, children: ` · ${formatDuration(elapsed)} · ${totalTokens || '—'} tok · ${subagent.toolCalls.length} tools` })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
145
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: info.color, bold: true, children: info.glyph }), _jsx(Text, { bold: true, children: `${t('subagent-card-prefix')}${subagent.description}` }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { color: info.color, children: info.label }), _jsx(Box, { flexGrow: 1 }), _jsx(ExitButton, { onClick: onBack })] }), _jsxs(Text, { children: [_jsx(Text, { children: subagent.model ?? subagent.provider ?? 'default' }), _jsxs(Text, { dimColor: true, children: [elapsed !== undefined ? ` · ${formatDuration(elapsed)} · ` : ' · ', totalTokens || '—', " tok \u00B7 ", subagent.toolCalls.length, " tools"] })] }), _jsxs(Text, { dimColor: true, children: [`${t('subagent-started')} ${formatTimestamp(subagent.startedAt)}`
141
146
  + (subagent.completedAt ? ` · ${t('subagent-completed')} ${formatTimestamp(subagent.completedAt)}` : ''), ` · id ${subagent.agentId}`] }), subagent.error && (_jsx(Box, { marginTop: 0, children: _jsx(Text, { color: "error", wrap: "wrap", children: `${t('subagent-error-label')}: ${subagent.error}` }) })), _jsxs(Box, { flexDirection: "row", gap: 0, marginTop: 1, children: [tab('summary', t('subagent-tab-summary')), tab('output', t('subagent-output-label')), tab('tools', t('subagent-tools')), _jsx(Text, { dimColor: true, children: ` ${pageIndex + 1}/${PAGES.length}` })] }), _jsx(Text, { dimColor: true, children: '─'.repeat(Math.max(20, Math.min(72, columns - 6))) }), _jsx(Box, { flexDirection: "column", paddingX: 1, maxHeight: Math.max(10, rows - 14), children: _jsxs(ScrollBox, { ref: scrollRef, flexDirection: "column", flexGrow: 1, children: [page === 'summary' && (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatGrid, { subagent: subagent, totalTokens: totalTokens, elapsed: elapsed, statusLabel: info.label, statusColor: info.color }), subagent.summary && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { dimColor: true, bold: true, children: '─ summary ' }), _jsx(Text, { wrap: "wrap", children: subagent.summary })] })), !subagent.summary && (_jsx(Text, { dimColor: true, children: isRunning ? t('subagent-no-output') : t('subagent-no-summary') }))] })), page === 'output' && (subagent.outputEvents.length === 0 && subagent.output.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-output') })) : (subagent.outputEvents.map((line, index) => (_jsxs(Text, { wrap: "wrap", dimColor: line.kind === 'thinking' || line.kind === 'system', color: line.kind === 'error' ? 'error' : undefined, children: [line.kind === 'thinking' ? ' ⌁ ' : ' ', line.text, !line.settled && isRunning ? ' ▍' : ''] }, index))))), page === 'tools' && (subagent.toolCalls.length === 0 ? (_jsx(Text, { dimColor: true, children: t('subagent-no-tools') })) : (subagent.toolCalls.map((tool, index) => (_jsxs(Box, { flexDirection: "column", marginTop: index === 0 ? 0 : 1, children: [_jsxs(Box, { flexDirection: "row", gap: 1, children: [_jsx(Text, { color: tool.status === 'failed' ? 'error' : tool.status === 'running' ? 'warning' : 'success', children: tool.status === 'running' ? '·' : tool.status === 'failed' ? '×' : '✓' }), _jsx(Text, { color: toolNameColor(tool.name), children: tool.name }), tool.endedAt && _jsx(Text, { dimColor: true, children: formatDuration(tool.endedAt - tool.startedAt) })] }), tool.argsPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(JsonArgsText, { raw: tool.argsPreview }) })), tool.resultPreview && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { dimColor: true, wrap: "wrap", children: `⎿ ${tool.resultPreview}` }) })), tool.error && (_jsx(Box, { flexDirection: "row", paddingLeft: 2, children: _jsx(Text, { color: "error", wrap: "wrap", children: tool.error }) }))] }, tool.id ?? index)))))] }) }), _jsx(Divider, { color: "subtle", title: "" }), _jsxs(Box, { marginTop: 0, flexDirection: "row", children: [_jsx(Text, { dimColor: true, children: `←/→ ${t('subagent-hint-page')} · ↑/↓ ${t('subagent-hint-scroll')}` }), isRunning && onInterrupt && (_jsxs(_Fragment, { children: [_jsx(Text, { dimColor: true, children: ' · ' }), _jsx(Box, { onClick: () => onInterrupt(subagent.agentId), children: _jsx(Text, { dimColor: true, bold: true, color: "warning", children: "X interrupt" }) })] })), _jsx(Text, { dimColor: true, children: ` · Esc ${t('subagent-hint-back')}` })] })] }));
142
147
  }
@@ -2,8 +2,7 @@ import React from 'react';
2
2
  /**
3
3
  * Localized shortcut-hint line: renders a `t('hint-*')` string, with the
4
4
  * `**primary shortcut**` segment (the main action's key, wrapped in `**` in
5
- * the dict) in bold — the visual hierarchy KeyboardShortcutHint's `bold`
6
- * prop used to give. Wrap in `<Text dimColor italic>` for the common dim
5
+ * the dict) in bold. Wrap in `<Text dimColor italic>` for the common dim
7
6
  * styling, like the Byline it replaces.
8
7
  */
9
8
  export declare function HintLine({ text }: {
@@ -1 +1 @@
1
- {"version":3,"file":"HintLine.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/HintLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB;;;;;;GAMG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAUpE"}
1
+ {"version":3,"file":"HintLine.d.ts","sourceRoot":"","sources":["../../../../src/components/design-system/HintLine.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,EAAE,IAAI,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,SAAS,CAUpE"}
@@ -3,8 +3,7 @@ import Text from './ThemedText.js';
3
3
  /**
4
4
  * Localized shortcut-hint line: renders a `t('hint-*')` string, with the
5
5
  * `**primary shortcut**` segment (the main action's key, wrapped in `**` in
6
- * the dict) in bold — the visual hierarchy KeyboardShortcutHint's `bold`
7
- * prop used to give. Wrap in `<Text dimColor italic>` for the common dim
6
+ * the dict) in bold. Wrap in `<Text dimColor italic>` for the common dim
8
7
  * styling, like the Byline it replaces.
9
8
  */
10
9
  export function HintLine({ text }) {
@@ -26,7 +26,8 @@ type Props = {
26
26
  onClick?(event: ClickEvent): void;
27
27
  };
28
28
  /**
29
- * Thinking block: settled rows fold to `⚓ Thinking (ctrl+o to expand)`;
29
+ * Thinking block: settled rows fold to `⚓ Thinking` plus the localized
30
+ * ctrl+o expand hint (hint-expand-ctrl-o);
30
31
  * streaming rows switch between a three-line preview and the full reasoning
31
32
  * text on click. The live leading mark is a rotating braille spinner
32
33
  * (`⠋⠙⠹…`, Kimi Code style), settling back to the static anchor (`⚓`). When
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAUjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB;;;sDAGkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,eAAe,EAAE,OAAO,CAAA;IACxB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;4CAEwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;8EAE0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;;;;;GAQG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,OAAO,EACP,SAAiB,EACjB,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAuIzB"}
1
+ {"version":3,"file":"AssistantThinkingMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantThinkingMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAczB,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAUjE,KAAK,KAAK,GAAG;IACX,QAAQ,EAAE,MAAM,CAAA;IAChB;;;sDAGkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,4CAA4C;IAC5C,eAAe,EAAE,OAAO,CAAA;IACxB,4EAA4E;IAC5E,OAAO,EAAE,OAAO,CAAA;IAChB;;4CAEwC;IACxC,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB;;8EAE0E;IAC1E,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,0EAA0E;IAC1E,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;CAClC,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,QAAQ,EACR,eAAe,EACf,OAAO,EACP,SAAiB,EACjB,OAAe,EACf,UAAU,EACV,UAAkB,EAClB,OAAO,GACR,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CAuIzB"}
@@ -17,7 +17,8 @@ import { isMinimalMode } from '../../minimalMode.js';
17
17
  * to exactly this many rows, keeps the block height stream-independent. */
18
18
  const PREVIEW_ROWS = 3;
19
19
  /**
20
- * Thinking block: settled rows fold to `⚓ Thinking (ctrl+o to expand)`;
20
+ * Thinking block: settled rows fold to `⚓ Thinking` plus the localized
21
+ * ctrl+o expand hint (hint-expand-ctrl-o);
21
22
  * streaming rows switch between a three-line preview and the full reasoning
22
23
  * text on click. The live leading mark is a rotating braille spinner
23
24
  * (`⠋⠙⠹…`, Kimi Code style), settling back to the static anchor (`⚓`). When
@@ -15,8 +15,8 @@ type Props = {
15
15
  isExpanded?: boolean;
16
16
  /**
17
17
  * Mouse click (fullscreen): toggles the row's expansion — same action as
18
- * clicking other transcript rows. Also makes the `(ctrl+o to expand)`
19
- * hint actionable with the mouse.
18
+ * clicking other transcript rows. Also makes the localized ctrl+o expand
19
+ * hint (lines-folded-expand) actionable with the mouse.
20
20
  */
21
21
  onClick?(event: ClickEvent): void;
22
22
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AASvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4CAA4C;IAC5C,eAAe,EAAE,OAAO,CAAA;IACxB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;8CAC0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AAsFD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AAsVD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,eAAe,EACf,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,EACvB,UAAU,EACV,mBAA2B,EAC3B,YAAoB,EACpB,KAAa,EACb,aAAa,GACd,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA2PzB"}
1
+ {"version":3,"file":"AssistantToolUseMessage.d.ts","sourceRoot":"","sources":["../../../../src/components/messages/AssistantToolUseMessage.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AAKzB,OAAO,KAAK,EAA8C,OAAO,EAAE,MAAM,8BAA8B,CAAA;AASvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAC3C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAA;AAIjE,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,4CAA4C;IAC5C,eAAe,EAAE,OAAO,CAAA;IACxB,iEAAiE;IACjE,OAAO,EAAE,OAAO,CAAA;IAChB,wCAAwC;IACxC,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB,kEAAkE;IAClE,UAAU,CAAC,EAAE,OAAO,CAAA;IACpB;;;;OAIG;IACH,OAAO,CAAC,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAA;IACjC;;;;;;;OAOG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,oEAAoE;IACpE,UAAU,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAAA;IACzC,2EAA2E;IAC3E,cAAc,CAAC,EAAE,cAAc,CAAA;IAC/B;;;;;OAKG;IACH,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAA;IACnC;;;;OAIG;IACH,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B;;;;;;;OAOG;IACH,YAAY,CAAC,EAAE,OAAO,CAAA;IACtB;8CAC0C;IAC1C,KAAK,CAAC,EAAE,OAAO,CAAA;IACf,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAA;CACvB,CAAA;AA2FD,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,KAAK,CAKtD;AAsVD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CAAC,EACtC,IAAI,EACJ,eAAe,EACf,OAAO,EACP,UAAkB,EAClB,UAAkB,EAClB,OAAO,EACP,QAAQ,EACR,UAAmB,EACnB,cAAuB,EACvB,UAAU,EACV,mBAA2B,EAC3B,YAAoB,EACpB,KAAa,EACb,aAAa,GACd,EAAE,KAAK,GAAG,KAAK,CAAC,SAAS,CA2PzB"}
@@ -14,25 +14,29 @@ import { foldLongLines } from '../../utils/fold-long-lines.js';
14
14
  import { getLang, t } from '../../i18n.js';
15
15
  import { revealLinesOf, snapReveal } from '../smoothReveal.js';
16
16
  import { useRevealVersion } from '../../hooks/useRevealVersion.js';
17
- /** Tool display names: DSH emits lowercase tool ids (`bash`); display common
18
- * names with an initial capital and fall back to the id with its first letter
19
- * uppercased. */
17
+ /** Tool display names localize through the `tool-name-*` dictionary family
18
+ * (i18n.ts): DSH emits lowercase tool ids (`bash`), display names resolve
19
+ * per language — proper nouns (Bash, PowerShell) stay identical in zh.
20
+ * Unmapped ids (plugins, new upstream tools) fall back to the id with its
21
+ * first letter uppercased: that is a name, not copy — there is nothing to
22
+ * translate. Keys appear as literals here, so verify-i18n's dead-key scan
23
+ * sees them without a DYNAMIC_PREFIXES entry. */
24
+ const TOOL_NAME_KEYS = {
25
+ bash: 'tool-name-bash',
26
+ powershell: 'tool-name-powershell',
27
+ read: 'tool-name-read',
28
+ glob: 'tool-name-glob',
29
+ grep: 'tool-name-grep',
30
+ write: 'tool-name-write',
31
+ edit: 'tool-name-edit',
32
+ todo_write: 'tool-name-todo_write',
33
+ subagent: 'tool-name-subagent',
34
+ web_search: 'tool-name-web_search',
35
+ };
20
36
  function displayName(name) {
21
- const KNOWN = {
22
- bash: 'Bash',
23
- powershell: 'PowerShell',
24
- read: 'Read',
25
- glob: 'Glob',
26
- grep: 'Grep',
27
- write: 'Write',
28
- edit: 'Edit',
29
- todo_write: 'TodoWrite',
30
- subagent: 'Task',
31
- web_search: 'WebSearch',
32
- };
33
- const mapped = KNOWN[name];
34
- if (mapped)
35
- return mapped;
37
+ const key = TOOL_NAME_KEYS[name];
38
+ if (key !== undefined)
39
+ return t(key);
36
40
  if (name.length === 0)
37
41
  return name;
38
42
  return name[0].toUpperCase() + name.slice(1);
@@ -145,10 +149,10 @@ function viewLines(view) {
145
149
  const out = (('output' in view ? view.output : undefined) ?? '').trimEnd();
146
150
  const lines = out === '' ? [] : out.split('\n').map(plain);
147
151
  if ('exitCode' in view && view.exitCode !== undefined && view.exitCode !== 0) {
148
- lines.push({ text: `Exit code ${view.exitCode}`, tone: 'error' });
152
+ lines.push({ text: t('tool-exit-code', { code: view.exitCode }), tone: 'error' });
149
153
  }
150
154
  if ('signal' in view && view.signal !== undefined) {
151
- lines.push({ text: `Killed by signal ${view.signal}`, tone: 'error' });
155
+ lines.push({ text: t('tool-killed-signal', { name: String(view.signal) }), tone: 'error' });
152
156
  }
153
157
  return lines;
154
158
  }
@@ -160,7 +164,7 @@ function viewLines(view) {
160
164
  if (view.shape === 'paths') {
161
165
  const lines = view.paths.map(plain);
162
166
  if (view.truncated)
163
- lines.push(dim(`… (${view.total} total)`));
167
+ lines.push(dim(t('search-results-total', { n: view.total })));
164
168
  return lines;
165
169
  }
166
170
  const lines = [];
@@ -187,7 +191,7 @@ function capLines(lines, max, verbose) {
187
191
  return lines;
188
192
  return [
189
193
  ...lines.slice(0, max),
190
- { ...dim(`… +${lines.length - max} lines (ctrl+o to expand)`), revealOnHover: true },
194
+ { ...dim(t('lines-folded-expand', { n: lines.length - max })), revealOnHover: true },
191
195
  ];
192
196
  }
193
197
  /** Long-line clip for the body rows (utils/fold-long-lines.ts): the line cap
@@ -326,7 +330,7 @@ function HeaderTitle({ name, title, isTerminal, folded, displayArgs, argsLanguag
326
330
  return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), displayArgs !== '' && (_jsxs(Box, { flexWrap: "nowrap", ...headerTooltip, children: [_jsx(Text, { children: "(" }), _jsx(SyntaxText, { text: clipHeaderArgs(displayArgs), sourceText: displayArgs, language: argsLanguage }), _jsx(Text, { children: ")" })] }))] }));
327
331
  }
328
332
  if (isTerminal) {
329
- return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), _jsx(Box, { flexWrap: "nowrap", ...headerTooltip, children: folded === undefined ? (_jsxs(Text, { children: ["(", title, ")"] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["(", folded.first, ")"] }), folded.hiddenLines > 0 && (_jsx(Text, { dimColor: true, children: ` … +${folded.hiddenLines} lines (ctrl+o to expand)` }))] })) })] }));
333
+ return (_jsxs(_Fragment, { children: [_jsx(Box, { flexShrink: 0, children: _jsx(Text, { bold: true, color: nameColor, wrap: "truncate-end", children: name }) }), _jsx(Box, { flexWrap: "nowrap", ...headerTooltip, children: folded === undefined ? (_jsxs(Text, { children: ["(", title, ")"] })) : (_jsxs(_Fragment, { children: [_jsxs(Text, { children: ["(", folded.first, ")"] }), folded.hiddenLines > 0 && (_jsx(Text, { dimColor: true, children: ` ${t('lines-folded-expand', { n: folded.hiddenLines })}` }))] })) })] }));
330
334
  }
331
335
  const trimmed = title.trim();
332
336
  if (trimmed === '') {
@@ -439,7 +443,7 @@ export function AssistantToolUseMessage({ tool, marginTopOnTurn, verbose, isSele
439
443
  body = result.trimEnd().split('\n').map(plain);
440
444
  }
441
445
  if (isRunning && body.length === 0) {
442
- body = [dim(`Running… (${formatDuration(Math.max(0, Date.now() - (tool.startedAt ?? Date.now())))})`)];
446
+ body = [dim(t('tool-running-elapsed', { duration: formatDuration(Math.max(0, Date.now() - (tool.startedAt ?? Date.now()))) }))];
443
447
  }
444
448
  }
445
449
  const cap = view?.card === 'diff' ? DIFF_BODY_MAX_LINES : TEXT_BODY_MAX_LINES;
@@ -479,8 +483,8 @@ export function AssistantToolUseMessage({ tool, marginTopOnTurn, verbose, isSele
479
483
  // Hover affordance for the click-to-toggle row: the theme's tool-card blue
480
484
  // face marks the call's content area while the pointer dwells (the
481
485
  // toolBackground treatment steps up one level to the strong card face), the
482
- // collapsed `(ctrl+o to expand)` hint steps from dim to text, the elapsed
483
- // clock stops dimming, and a ▾/▴ discloses the row is a toggle.
486
+ // collapsed fold hint (lines-folded-expand) steps from dim to text, the
487
+ // elapsed clock stops dimming, and a ▾/▴ discloses the row is a toggle.
484
488
  // No layout change: the indicator is a fixed column on the header line, the
485
489
  // body never moves.
486
490
  const [hovered, setHovered] = React.useState(false);