@linxiraos/pi-tui 1.2.1 → 1.3.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 (280) hide show
  1. package/CHANGELOG.md +245 -224
  2. package/README.md +36 -0
  3. package/THIRD-PARTY-NOTICES.txt +55 -5
  4. package/dist/types/app-keybindings.d.ts +18 -8
  5. package/dist/types/apps/git/diff-pane.d.ts +5 -0
  6. package/dist/types/apps/git/git-tui.d.ts +16 -2
  7. package/dist/types/apps/git/state.d.ts +11 -0
  8. package/dist/types/autocomplete.d.ts +2 -0
  9. package/dist/types/charts/chart-svg.d.ts +7 -13
  10. package/dist/types/chat/advisor-message.d.ts +4 -1
  11. package/dist/types/chat/assistant-message.d.ts +9 -4
  12. package/dist/types/chat/figure-markdown.d.ts +7 -2
  13. package/dist/types/chat/messages.d.ts +3 -1
  14. package/dist/types/chat/read-tool-group.d.ts +2 -2
  15. package/dist/types/chat/table-chart.d.ts +11 -15
  16. package/dist/types/chat/tool-execution.d.ts +32 -20
  17. package/dist/types/chat/user-message.d.ts +3 -2
  18. package/dist/types/chrome/countdown-timer.d.ts +3 -2
  19. package/dist/types/chrome/diff.d.ts +2 -3
  20. package/dist/types/chrome/status-notice.d.ts +2 -0
  21. package/dist/types/chrome/transcript-container.d.ts +2 -0
  22. package/dist/types/components/image.d.ts +2 -0
  23. package/dist/types/components/markdown.d.ts +49 -3
  24. package/dist/types/components/text.d.ts +2 -0
  25. package/dist/types/debug-server.d.ts +2 -0
  26. package/dist/types/keybinding-matchers.d.ts +6 -0
  27. package/dist/types/multiplexers/tmux.d.ts +1 -0
  28. package/dist/types/multiplexers/types.d.ts +6 -0
  29. package/dist/types/native/backend.d.ts +5 -4
  30. package/dist/types/native/blobs.d.ts +24 -12
  31. package/dist/types/overlays/agent-hub-projection.d.ts +10 -5
  32. package/dist/types/overlays/annotation-overlay.d.ts +19 -0
  33. package/dist/types/overlays/approval-dialog.d.ts +79 -0
  34. package/dist/types/overlays/ask-dialog.d.ts +11 -6
  35. package/dist/types/overlays/btw-history-panel.d.ts +2 -6
  36. package/dist/types/overlays/hook-editor.d.ts +7 -2
  37. package/dist/types/overlays/hook-input.d.ts +12 -4
  38. package/dist/types/overlays/job-tree.d.ts +50 -0
  39. package/dist/types/overlays/jobs-panel.d.ts +25 -11
  40. package/dist/types/overlays/login-dialog.d.ts +8 -3
  41. package/dist/types/overlays/mcp-add-wizard.d.ts +9 -1
  42. package/dist/types/overlays/model-browser.d.ts +6 -1
  43. package/dist/types/overlays/model-picker.d.ts +2 -0
  44. package/dist/types/overlays/pause-screen.d.ts +5 -0
  45. package/dist/types/overlays/plan-review-overlay.d.ts +4 -0
  46. package/dist/types/overlays/report-panel.d.ts +5 -1
  47. package/dist/types/overlays/reset-usage-selector.d.ts +7 -5
  48. package/dist/types/overlays/session-account-selector.d.ts +4 -0
  49. package/dist/types/overlays/session-info-overlay.d.ts +7 -3
  50. package/dist/types/overlays/session-selector.d.ts +3 -1
  51. package/dist/types/overlays/usage-dashboard.d.ts +6 -6
  52. package/dist/types/prompt/composer.d.ts +23 -20
  53. package/dist/types/prompt/custom-editor.d.ts +20 -10
  54. package/dist/types/prompt/image-references.d.ts +22 -0
  55. package/dist/types/prompt/internal-url-autocomplete.d.ts +1 -0
  56. package/dist/types/prompt/macos-spelling.d.ts +2 -1
  57. package/dist/types/prompt/prose-gate.d.ts +3 -0
  58. package/dist/types/prompt/queued-messages.d.ts +1 -1
  59. package/dist/types/prompt/typing-word.d.ts +25 -0
  60. package/dist/types/prompt/welcome.d.ts +26 -6
  61. package/dist/types/render/hanging-wrap.d.ts +7 -0
  62. package/dist/types/render/hyperlink.d.ts +2 -0
  63. package/dist/types/render/render-utils.d.ts +9 -1
  64. package/dist/types/render/utils.d.ts +1 -1
  65. package/dist/types/setup/scenes/splash.d.ts +6 -5
  66. package/dist/types/status-line/context-usage.d.ts +4 -1
  67. package/dist/types/status-line/host.d.ts +6 -4
  68. package/dist/types/status-line/startup.d.ts +6 -6
  69. package/dist/types/status-line/types.d.ts +3 -0
  70. package/dist/types/stdin-buffer.d.ts +7 -0
  71. package/dist/types/terminal-capabilities.d.ts +16 -1
  72. package/dist/types/terminal-multiplexer.d.ts +7 -0
  73. package/dist/types/terminal.d.ts +8 -4
  74. package/dist/types/theme/symbols.d.ts +1 -1
  75. package/dist/types/theme/theme.d.ts +11 -0
  76. package/dist/types/thinking.d.ts +5 -0
  77. package/dist/types/tmux.d.ts +39 -4
  78. package/dist/types/tools/ast-edit.d.ts +2 -2
  79. package/dist/types/tools/ast-grep.d.ts +2 -2
  80. package/dist/types/tools/bash.d.ts +6 -1
  81. package/dist/types/tools/edit.d.ts +2 -0
  82. package/dist/types/tools/eval.d.ts +53 -4
  83. package/dist/types/tools/find.d.ts +14 -4
  84. package/dist/types/tools/glob.d.ts +3 -3
  85. package/dist/types/tools/grep.d.ts +2 -2
  86. package/dist/types/tools/grouped-file-output.d.ts +9 -4
  87. package/dist/types/tools/native-view.d.ts +1 -1
  88. package/dist/types/tools/read.d.ts +32 -10
  89. package/dist/types/tools/renderer.d.ts +2 -0
  90. package/dist/types/tools/task.d.ts +31 -0
  91. package/dist/types/tools/web-search-types.d.ts +1 -0
  92. package/dist/types/tools/write.d.ts +2 -0
  93. package/dist/types/tui.d.ts +20 -3
  94. package/package.json +10 -10
  95. package/src/app-keybindings.d.ts +18 -8
  96. package/src/app-keybindings.ts +20 -8
  97. package/src/apps/autoresearch-dashboard.ts +444 -27
  98. package/src/apps/debug/raw-sse.ts +2 -2
  99. package/src/apps/git/diff-pane.ts +39 -41
  100. package/src/apps/git/git-tui.ts +80 -14
  101. package/src/apps/git/sidebar.ts +10 -1
  102. package/src/apps/git/state.ts +12 -0
  103. package/src/autocomplete.d.ts +2 -0
  104. package/src/autocomplete.ts +21 -19
  105. package/src/bracketed-paste.ts +9 -2
  106. package/src/charts/chart-svg.d.ts +7 -13
  107. package/src/charts/chart-svg.ts +19 -22
  108. package/src/charts/table-data.ts +17 -6
  109. package/src/chat/advisor-message.d.ts +4 -1
  110. package/src/chat/advisor-message.ts +77 -24
  111. package/src/chat/assistant-message.d.ts +9 -4
  112. package/src/chat/assistant-message.ts +127 -31
  113. package/src/chat/bash-execution.ts +7 -2
  114. package/src/chat/figure-markdown.d.ts +7 -2
  115. package/src/chat/figure-markdown.ts +10 -3
  116. package/src/chat/image-loading.ts +9 -6
  117. package/src/chat/messages.d.ts +3 -1
  118. package/src/chat/messages.ts +4 -1
  119. package/src/chat/read-tool-group.d.ts +2 -2
  120. package/src/chat/read-tool-group.ts +97 -42
  121. package/src/chat/svg-figure.ts +3 -1
  122. package/src/chat/svg-source.ts +6 -3
  123. package/src/chat/table-chart.d.ts +11 -15
  124. package/src/chat/table-chart.ts +24 -57
  125. package/src/chat/tool-execution.d.ts +32 -20
  126. package/src/chat/tool-execution.ts +103 -36
  127. package/src/chat/user-message.d.ts +3 -2
  128. package/src/chat/user-message.ts +54 -34
  129. package/src/chrome/countdown-timer.ts +3 -3
  130. package/src/chrome/diff.d.ts +2 -3
  131. package/src/chrome/diff.ts +4 -9
  132. package/src/chrome/status-notice.ts +5 -0
  133. package/src/chrome/transcript-container.d.ts +2 -0
  134. package/src/chrome/transcript-container.ts +212 -28
  135. package/src/components/editor.ts +12 -12
  136. package/src/components/image.d.ts +2 -0
  137. package/src/components/image.ts +54 -20
  138. package/src/components/markdown.d.ts +49 -3
  139. package/src/components/markdown.ts +482 -12
  140. package/src/components/settings-list.ts +2 -4
  141. package/src/components/text.d.ts +2 -0
  142. package/src/components/text.ts +11 -1
  143. package/src/debug-server.d.ts +2 -0
  144. package/src/debug-server.ts +51 -69
  145. package/src/hotkeys-markdown.ts +2 -0
  146. package/src/keybinding-matchers.d.ts +6 -0
  147. package/src/keybinding-matchers.ts +19 -0
  148. package/src/multiplexers/tmux.d.ts +1 -0
  149. package/src/multiplexers/tmux.ts +3 -0
  150. package/src/multiplexers/types.d.ts +6 -0
  151. package/src/multiplexers/types.ts +6 -0
  152. package/src/native/backend.d.ts +5 -4
  153. package/src/native/backend.ts +13 -5
  154. package/src/native/blobs.d.ts +24 -12
  155. package/src/native/blobs.ts +56 -7
  156. package/src/native/reconcile.ts +43 -21
  157. package/src/overlays/advisor-config.ts +259 -8
  158. package/src/overlays/agent-hub-projection.d.ts +10 -5
  159. package/src/overlays/agent-hub-projection.ts +63 -12
  160. package/src/overlays/annotation-overlay.ts +422 -168
  161. package/src/overlays/approval-dialog.ts +389 -0
  162. package/src/overlays/ask-dialog.ts +244 -105
  163. package/src/overlays/btw-history-panel.ts +208 -147
  164. package/src/overlays/extensions/extension-list.ts +4 -6
  165. package/src/overlays/hook-editor.ts +90 -32
  166. package/src/overlays/hook-input.ts +105 -19
  167. package/src/overlays/job-tree.ts +177 -0
  168. package/src/overlays/jobs-panel.ts +222 -142
  169. package/src/overlays/login-dialog.ts +201 -91
  170. package/src/overlays/mcp-add-wizard.ts +562 -279
  171. package/src/overlays/model-browser.ts +32 -7
  172. package/src/overlays/model-hub.ts +4 -10
  173. package/src/overlays/model-picker.ts +58 -21
  174. package/src/overlays/pause-screen.ts +81 -23
  175. package/src/overlays/plan-review-overlay.ts +16 -0
  176. package/src/overlays/plugin-selector.ts +17 -1
  177. package/src/overlays/queue-mode-selector.ts +10 -0
  178. package/src/overlays/report-panel.ts +6 -2
  179. package/src/overlays/reset-usage-selector.ts +130 -21
  180. package/src/overlays/session-account-selector.ts +36 -0
  181. package/src/overlays/session-info-overlay.ts +199 -76
  182. package/src/overlays/session-selector.ts +15 -10
  183. package/src/overlays/settings-selector.ts +3 -0
  184. package/src/overlays/show-images-selector.ts +1 -0
  185. package/src/overlays/snapcompact-shape-preview.ts +13 -6
  186. package/src/overlays/theme-selector.ts +6 -1
  187. package/src/overlays/thinking-selector.ts +8 -4
  188. package/src/overlays/tree-selector.ts +117 -2
  189. package/src/overlays/usage-dashboard.ts +498 -144
  190. package/src/prompt/composer-cache.ts +4 -1
  191. package/src/prompt/composer.ts +200 -74
  192. package/src/prompt/custom-editor.ts +168 -65
  193. package/src/prompt/image-references.d.ts +22 -0
  194. package/src/prompt/image-references.ts +79 -2
  195. package/src/prompt/internal-url-autocomplete.ts +1 -0
  196. package/src/prompt/macos-spelling.ts +28 -5
  197. package/src/prompt/prose-gate.ts +3 -0
  198. package/src/prompt/queued-messages.ts +9 -2
  199. package/src/prompt/tips.txt +1 -2
  200. package/src/prompt/typing-word.ts +59 -0
  201. package/src/prompt/welcome.ts +97 -45
  202. package/src/render/code-cell.ts +18 -5
  203. package/src/render/hanging-wrap.d.ts +7 -0
  204. package/src/render/hanging-wrap.ts +58 -0
  205. package/src/render/hyperlink.d.ts +2 -0
  206. package/src/render/hyperlink.ts +5 -0
  207. package/src/render/render-utils.d.ts +9 -1
  208. package/src/render/render-utils.ts +16 -2
  209. package/src/render/utils.d.ts +1 -1
  210. package/src/render/utils.ts +1 -1
  211. package/src/render/width-aware-text.ts +6 -5
  212. package/src/setup/scenes/splash.ts +22 -8
  213. package/src/status-line/component.ts +79 -11
  214. package/src/status-line/context-usage.ts +176 -73
  215. package/src/status-line/footer.ts +18 -35
  216. package/src/status-line/host.ts +6 -1
  217. package/src/status-line/segments.ts +16 -5
  218. package/src/status-line/startup.ts +6 -6
  219. package/src/status-line/types.ts +1 -0
  220. package/src/stdin-buffer.d.ts +7 -0
  221. package/src/stdin-buffer.ts +300 -62
  222. package/src/terminal-capabilities.d.ts +16 -1
  223. package/src/terminal-capabilities.ts +66 -28
  224. package/src/terminal-multiplexer.d.ts +7 -0
  225. package/src/terminal-multiplexer.ts +14 -0
  226. package/src/terminal.d.ts +8 -4
  227. package/src/terminal.ts +190 -63
  228. package/src/theme/symbols.d.ts +1 -1
  229. package/src/theme/symbols.ts +1 -0
  230. package/src/theme/theme-class.ts +1 -0
  231. package/src/theme/theme.d.ts +11 -0
  232. package/src/theme/theme.ts +46 -14
  233. package/src/thinking.d.ts +5 -0
  234. package/src/thinking.ts +9 -1
  235. package/src/tmux.d.ts +39 -4
  236. package/src/tmux.ts +110 -16
  237. package/src/tools/ask.ts +20 -9
  238. package/src/tools/ast-edit.d.ts +2 -2
  239. package/src/tools/ast-edit.ts +30 -7
  240. package/src/tools/ast-grep.d.ts +2 -2
  241. package/src/tools/ast-grep.ts +39 -35
  242. package/src/tools/bash.d.ts +6 -1
  243. package/src/tools/bash.ts +15 -9
  244. package/src/tools/debug.ts +2 -1
  245. package/src/tools/default-renderer.ts +3 -2
  246. package/src/tools/edit.d.ts +2 -0
  247. package/src/tools/edit.ts +100 -21
  248. package/src/tools/eval.d.ts +53 -4
  249. package/src/tools/eval.ts +173 -137
  250. package/src/tools/find.d.ts +14 -4
  251. package/src/tools/find.ts +71 -18
  252. package/src/tools/glob.d.ts +3 -3
  253. package/src/tools/glob.ts +51 -17
  254. package/src/tools/grep.d.ts +2 -2
  255. package/src/tools/grep.ts +99 -22
  256. package/src/tools/grouped-file-output.d.ts +9 -4
  257. package/src/tools/grouped-file-output.ts +21 -6
  258. package/src/tools/lsp.ts +30 -18
  259. package/src/tools/mcp.ts +2 -2
  260. package/src/tools/native-view.d.ts +1 -1
  261. package/src/tools/native-view.ts +2 -2
  262. package/src/tools/read.d.ts +32 -10
  263. package/src/tools/read.ts +89 -43
  264. package/src/tools/renderer.d.ts +2 -0
  265. package/src/tools/renderer.ts +2 -0
  266. package/src/tools/task.d.ts +31 -0
  267. package/src/tools/task.ts +40 -7
  268. package/src/tools/todo.ts +3 -2
  269. package/src/tools/wait.ts +16 -30
  270. package/src/tools/web-search-types.d.ts +1 -0
  271. package/src/tools/web-search-types.ts +1 -0
  272. package/src/tools/web-search.ts +2 -1
  273. package/src/tools/write.d.ts +2 -0
  274. package/src/tools/write.ts +36 -12
  275. package/src/tools/yield.ts +2 -2
  276. package/src/tui.d.ts +20 -3
  277. package/src/tui.ts +160 -43
  278. package/src/utils.ts +5 -3
  279. package/dist/types/tools/eval-format/code-mode-declarations.d.ts +0 -5
  280. package/src/tools/eval-format/code-mode-declarations.ts +0 -61
package/src/tools/task.ts CHANGED
@@ -29,6 +29,8 @@ import {
29
29
  isFeedModelBadgeEnabled,
30
30
  pluralize,
31
31
  previewLine,
32
+ clipCollapsed,
33
+ flattenLine,
32
34
  previewWindowRows,
33
35
  replaceTabs,
34
36
  shortenEmbeddedPaths,
@@ -347,7 +349,7 @@ function renderOutputSection(
347
349
  lines.push(
348
350
  `${continuePrefix} ${theme.fg("warning", theme.status.warning)} ${theme.fg(
349
351
  "dim",
350
- truncateToWidth(sanitizeText(warning), 80),
352
+ clipCollapsed(sanitizeText(warning), 80, expanded),
351
353
  )}`,
352
354
  );
353
355
 
@@ -391,7 +393,7 @@ function renderOutputSection(
391
393
  const outputLines = trimmedOutput.split("\n");
392
394
  const previewCount = expanded ? maxExpanded : maxCollapsed;
393
395
  for (const line of outputLines.slice(0, previewCount)) {
394
- lines.push(`${continuePrefix} ${theme.fg("dim", truncateToWidth(replaceTabs(line), 70))}`);
396
+ lines.push(`${continuePrefix} ${theme.fg("dim", clipCollapsed(replaceTabs(line), 70, expanded))}`);
395
397
  }
396
398
 
397
399
  if (outputLines.length > previewCount) {
@@ -444,7 +446,7 @@ function renderOutputSection(
444
446
  const outputLines = trimmedOutput.split("\n");
445
447
  const previewCount = expanded ? maxExpanded : maxCollapsed;
446
448
  for (const line of outputLines.slice(0, previewCount)) {
447
- lines.push(`${continuePrefix} ${theme.fg("dim", truncateToWidth(replaceTabs(line), 70))}`);
449
+ lines.push(`${continuePrefix} ${theme.fg("dim", clipCollapsed(replaceTabs(line), 70, expanded))}`);
448
450
  }
449
451
 
450
452
  if (outputLines.length > previewCount) {
@@ -468,7 +470,7 @@ function renderTaskSection(
468
470
  lines.push(`${continuePrefix}${theme.fg("dim", "Task")}`);
469
471
  const taskLines = trimmed.split("\n");
470
472
  for (const line of taskLines.slice(0, maxExpanded)) {
471
- lines.push(`${continuePrefix} ${theme.fg("dim", truncateToWidth(replaceTabs(line), 70))}`);
473
+ lines.push(`${continuePrefix} ${theme.fg("dim", replaceTabs(line))}`);
472
474
  }
473
475
  if (taskLines.length > maxExpanded) {
474
476
  lines.push(`${continuePrefix} ${theme.fg("dim", formatMoreItems(taskLines.length - maxExpanded, "line"))}`);
@@ -1119,7 +1121,7 @@ function renderAgentResult(
1119
1121
  lines.push(
1120
1122
  `${continuePrefix}${theme.fg("error", theme.status.aborted)} ${theme.fg(
1121
1123
  "dim",
1122
- previewLine(sanitizeText(result.abortReason), 80),
1124
+ clipCollapsed(flattenLine(sanitizeText(result.abortReason)), 80, expanded),
1123
1125
  )}`,
1124
1126
  );
1125
1127
  }
@@ -1211,7 +1213,7 @@ function renderAgentResult(
1211
1213
  lines.push(
1212
1214
  `${continuePrefix}${theme.fg("warning", theme.status.warning)} ${theme.fg(
1213
1215
  "dim",
1214
- truncateToWidth(sanitizeText(missingCompleteWarning), 80),
1216
+ clipCollapsed(sanitizeText(missingCompleteWarning), 80, expanded),
1215
1217
  )}`,
1216
1218
  );
1217
1219
  }
@@ -1251,7 +1253,7 @@ function renderAgentResult(
1251
1253
  // Error message
1252
1254
  if (result.error && (!success || mergeFailed) && (!aborted || result.error !== result.abortReason)) {
1253
1255
  lines.push(
1254
- `${continuePrefix}${theme.fg(mergeFailed ? "warning" : "error", previewLine(sanitizeText(result.error), 70))}`,
1256
+ `${continuePrefix}${theme.fg(mergeFailed ? "warning" : "error", clipCollapsed(flattenLine(sanitizeText(result.error)), 70, expanded))}`,
1255
1257
  );
1256
1258
  }
1257
1259
 
@@ -2200,6 +2202,10 @@ export interface TaskItem {
2200
2202
  name?: string;
2201
2203
  /** Agent type to run this item (e.g. "scout"). Defaults to the spawn policy's default agent. */
2202
2204
  agent?: string;
2205
+ /** Historical source session selected by the bundled consult agent. */
2206
+ sourceSession?: string;
2207
+ /** Optional model selector override for the historical source session. */
2208
+ model?: string | string[];
2203
2209
  /** The work; required by the schema. */
2204
2210
  task?: string;
2205
2211
  /** How open-ended the work is; required by the schema and the child's sole `auto` thinking classification input. */
@@ -2227,6 +2233,10 @@ export interface TaskParams {
2227
2233
  name?: string;
2228
2234
  /** Agent type to spawn (flat form); omitted values resolve from the session spawn policy. */
2229
2235
  agent?: string;
2236
+ /** Historical source session selected by the bundled consult agent. */
2237
+ sourceSession?: string;
2238
+ /** Optional model selector override for the historical source session. */
2239
+ model?: string | string[];
2230
2240
  /** The work (flat form). */
2231
2241
  task?: string;
2232
2242
  /** How open-ended the work is (flat form); see {@link TaskItem.solutionSpace}. */
@@ -2497,6 +2507,29 @@ export interface SingleResult {
2497
2507
  attempt: number;
2498
2508
  errorMessage: string;
2499
2509
  };
2510
+ /**
2511
+ * Coarse category of a non-zero exit — `usage-limit`, `auth`,
2512
+ * `account-policy`, `context-overflow`, `transient`, `aborted`, or
2513
+ * `unknown`. A parent agent reads this to tell "swap the model" (the first
2514
+ * three) from "fix the request" instead of parsing provider prose.
2515
+ */
2516
+ failureClass?: "usage-limit" | "auth" | "account-policy" | "context-overflow" | "transient" | "aborted" | "unknown";
2517
+ /**
2518
+ * Other models this session could run instead (selectors accepted by the
2519
+ * task `model` parameter, including `@role` aliases). Empty when nothing
2520
+ * else is configured or every alternative is already in cooldown.
2521
+ */
2522
+ fallbackCandidates?: string[];
2523
+ /**
2524
+ * Set when a caller-supplied `model` replaced the model this run would
2525
+ * otherwise have used — the audit trail of an agent-driven switch.
2526
+ */
2527
+ modelChange?: {
2528
+ source: "spawn-param" | "wake-param";
2529
+ /** The model the run would have used without the override, when resolvable. */
2530
+ from?: string;
2531
+ to: string;
2532
+ };
2500
2533
  /** Output metadata for agent:// URL integration */
2501
2534
  outputMeta?: { lineCount: number; charCount: number };
2502
2535
  }
package/src/tools/todo.ts CHANGED
@@ -8,6 +8,7 @@ import type { RenderResultOptions } from "./renderer";
8
8
  import type { Theme } from "../theme/theme";
9
9
 
10
10
  import { renderStatusLine, renderTreeList } from "../render";
11
+ import { wrapTreeLine } from "../render/hanging-wrap";
11
12
  import { framedToolCard } from "../render/tool-card";
12
13
 
13
14
  import { formatErrorDetail, formatMoreItems, PREVIEW_LIMITS, pluralize, replaceTabs } from "../render/render-utils";
@@ -657,7 +658,7 @@ export const todoToolRenderer = {
657
658
  matchByContent.clear();
658
659
  };
659
660
 
660
- return framedToolCard(uiTheme, () => {
661
+ return framedToolCard(uiTheme, ({ contentWidth }) => {
661
662
  const { expanded, spinnerFrame } = options;
662
663
  const multiPhase = phases.length > 1;
663
664
  const indent = multiPhase ? " " : "";
@@ -738,7 +739,7 @@ export const todoToolRenderer = {
738
739
  );
739
740
  })();
740
741
  for (const line of treeLines) {
741
- bodyLines.push(`${indent}${line}`);
742
+ bodyLines.push(...(wrapTreeLine(`${indent}${line}`, contentWidth, uiTheme) ?? [`${indent}${line}`]));
742
743
  }
743
744
  }
744
745
  while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
package/src/tools/wait.ts CHANGED
@@ -589,7 +589,11 @@ export function createIrcMessageCard(
589
589
  return Object.assign(component, { describe: () => described });
590
590
  }
591
591
 
592
- /** One job row: type badge, id + label, terminal-clocked duration (live while running), preview below. */
592
+ /**
593
+ * One job row on one line: type badge, id + label, terminal-clocked duration
594
+ * (live while running). The job's output and errors fill the card of the call
595
+ * that started it (`parksAsBackground`), so the row carries no preview.
596
+ */
593
597
  function describeJob(job: JobSnapshot, isPartial: boolean): NativeNode {
594
598
  const running = job.status === "running" && isPartial;
595
599
  const label = job.label.trim() !== job.id ? plainText(job.label.split(/\r?\n/)[0] ?? "") : "";
@@ -606,39 +610,21 @@ function describeJob(job: JobSnapshot, isPartial: boolean): NativeNode {
606
610
  : job.status === "cancelled"
607
611
  ? "warning"
608
612
  : "accent";
609
- const artifactError = job.meta?.artifactError ?? job.artifactError;
610
- const preview = flattenStructuredPreview(
611
- stripTaskResultEnvelope(
612
- stripOutputNotice(job.errorText?.trim() || job.resultText?.trim() || "", job.meta).trim(),
613
- ),
614
- );
615
613
  return node(
616
- "col",
617
- { role: "zeta.wait.job", tone },
614
+ "row",
615
+ { role: "zeta.wait.job", tone, gap: "sm" },
618
616
  compact([
619
- row(
620
- compact([
621
- node("badge", { text: job.type, tone }),
622
- text(spans, { truncate: "end", grow: 1 }),
623
- job.status === "running" && job.completionPercent !== undefined
624
- ? node("progress", {
625
- value: job.completionPercent / 100,
626
- label: `${job.completionPercent}%`,
627
- tone: "accent",
628
- title: "Agent's own completion estimate",
629
- })
630
- : undefined,
631
- elapsed(job.durationMs, !running),
632
- ]),
633
- { gap: "sm" },
634
- ),
635
- artifactError && text([span(formatArtifactErrorNotice(artifactError), "warning")], { wrap: "word" }),
636
- preview
637
- ? text([span(plainText(preview), job.errorText ? "error" : "dim")], {
638
- wrap: "word",
639
- lines: PREVIEW_LINES_EXPANDED,
617
+ node("badge", { text: job.type, tone }),
618
+ text(spans, { truncate: "end", grow: 1 }),
619
+ job.status === "running" && job.completionPercent !== undefined
620
+ ? node("progress", {
621
+ value: job.completionPercent / 100,
622
+ label: `${job.completionPercent}%`,
623
+ tone: "accent",
624
+ title: "Agent's own completion estimate",
640
625
  })
641
626
  : undefined,
627
+ elapsed(job.durationMs, !running),
642
628
  ]),
643
629
  job.id,
644
630
  );
@@ -25,6 +25,7 @@ export declare const SEARCH_PROVIDER_LABELS: {
25
25
  readonly tavily: "Tavily";
26
26
  readonly firecrawl: "Firecrawl";
27
27
  readonly brave: "Brave";
28
+ readonly deepseek: "DeepSeek";
28
29
  readonly kimi: "Kimi";
29
30
  readonly synthetic: "Synthetic";
30
31
  readonly ollama: "Ollama";
@@ -26,6 +26,7 @@ export const SEARCH_PROVIDER_LABELS = {
26
26
  tavily: "Tavily",
27
27
  firecrawl: "Firecrawl",
28
28
  brave: "Brave",
29
+ deepseek: "DeepSeek",
29
30
  kimi: "Kimi",
30
31
  synthetic: "Synthetic",
31
32
  ollama: "Ollama",
@@ -19,6 +19,7 @@ import {
19
19
  PREVIEW_LIMITS,
20
20
  replaceTabs,
21
21
  truncateToWidth,
22
+ clipCollapsed,
22
23
  } from "../render/render-utils";
23
24
  import { tuiText, tuiTextFmt } from "../i18n";
24
25
  import { renderStatusLine, renderTreeList, urlHyperlink } from "../render";
@@ -35,7 +36,7 @@ const MAX_COLLAPSED_ITEMS = PREVIEW_LIMITS.COLLAPSED_ITEMS;
35
36
  function renderFallbackText(contentText: string, expanded: boolean, theme: Theme): Component {
36
37
  const lines = contentText.split("\n").filter(line => line.trim());
37
38
  const maxLines = expanded ? lines.length : 6;
38
- const displayLines = lines.slice(0, maxLines).map(line => truncateToWidth(line.trim(), 110));
39
+ const displayLines = lines.slice(0, maxLines).map(line => clipCollapsed(line.trim(), 110, expanded));
39
40
  const remaining = lines.length - displayLines.length;
40
41
 
41
42
  const headerIcon = formatStatusIcon("warning", theme);
@@ -73,6 +73,8 @@ interface WriteStreamingPreviewStateCarrier {
73
73
  /** Render context for the write tool: resolves an `xd://`-mounted tool so its live renderer drives device dispatch previews. */
74
74
  export interface WriteRenderContext {
75
75
  resolveXdevMounted?: (name: string) => XdevMountedRenderer | undefined;
76
+ /** Original result metadata used only for filesystem link targets. */
77
+ rawDetails?: unknown;
76
78
  }
77
79
  /** Render file writes and delegated tool-device calls. */
78
80
  export declare const writeToolRenderer: {
@@ -1,4 +1,5 @@
1
1
  import * as path from "node:path";
2
+ import { sanitizeText } from "@linxiraos/pi-utils";
2
3
  import type { HighlightStream } from "@linxiraos/pi-natives";
3
4
  import type { Component } from "../tui";
4
5
  import { fencedCode } from "../components/markdown";
@@ -7,6 +8,7 @@ import { getLanguageFromPath } from "../lang-from-path";
7
8
  import { createHighlightStream, highlightCode, type Theme } from "../theme/theme";
8
9
  import { fileHyperlink, renderStatusLine } from "../render";
9
10
  import { framedToolCard } from "../render/tool-card";
11
+ import { wrapPrefixedLine } from "../render/hanging-wrap";
10
12
  import {
11
13
  cachedRenderedString,
12
14
  createRenderedStringCache,
@@ -162,10 +164,12 @@ function describeFigure(figure: ToolFigure | undefined): NativeNode | undefined
162
164
  return { ...md(text, { role: "zeta.tool.write.figure", stream: !figure.closed }), key: "figure" };
163
165
  }
164
166
 
165
- /** The written content as numbered code, keyed so it keeps its node as the figure above it comes and goes. */
167
+ /**
168
+ * The written content as numbered code, highlighted by Tern from the file's path,
169
+ * keyed so it keeps its node as the figure above it comes and goes.
170
+ */
166
171
  function describeSource(rawPath: string, content: string): NativeNode {
167
- const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
168
- return { ...code(content, { lang, numbers: true }), key: "source" };
172
+ return { ...code(content, { path: rawPath || undefined, numbers: true, wrap: true }), key: "source" };
169
173
  }
170
174
 
171
175
  function countLines(text: string): number {
@@ -388,6 +392,15 @@ function formatStreamingContent(
388
392
  return `${bodyText}${spinner}${uiTheme.fg("dim", `… (streaming)`)}`;
389
393
  }
390
394
 
395
+ function wrapWritePreviewRows(lines: string[], width: number): string[] {
396
+ return lines.flatMap(line => {
397
+ const gutter = /^(\x1b\[[0-9;]*m)( *\d+ )(\x1b\[[0-9;]*m)(.*)$/.exec(line);
398
+ if (!gutter) return line;
399
+ const prefix = `${gutter[1]}${gutter[2]}${gutter[3]}`;
400
+ return wrapPrefixedLine(prefix, " ".repeat(Bun.stringWidth(gutter[2]!)), gutter[4] ?? "", width);
401
+ });
402
+ }
403
+
391
404
  function renderContentPreview(
392
405
  content: string,
393
406
  expanded: boolean,
@@ -424,6 +437,8 @@ function renderContentPreview(
424
437
  /** Render context for the write tool: resolves an `xd://`-mounted tool so its live renderer drives device dispatch previews. */
425
438
  export interface WriteRenderContext {
426
439
  resolveXdevMounted?: (name: string) => XdevMountedRenderer | undefined;
440
+ /** Original result metadata used only for filesystem link targets. */
441
+ rawDetails?: unknown;
427
442
  }
428
443
 
429
444
  /** Process id and operation named by the text after `proc://`. */
@@ -540,6 +555,15 @@ function describeWriteUrl(
540
555
  }
541
556
  }
542
557
 
558
+ function writeResolvedPath(rawDetails: unknown, fallback: string | undefined): string | undefined {
559
+ return rawDetails !== null &&
560
+ typeof rawDetails === "object" &&
561
+ "resolvedPath" in rawDetails &&
562
+ typeof rawDetails.resolvedPath === "string"
563
+ ? rawDetails.resolvedPath
564
+ : fallback;
565
+ }
566
+
543
567
  /** Native write head shared by the call and result: `Write · path · 22 lines`, executable/diagnostics chips. */
544
568
  function writeToolHead(
545
569
  rawPath: string,
@@ -553,7 +577,7 @@ function writeToolHead(
553
577
  if (diagnostics) badges.push(diagnostics);
554
578
  return {
555
579
  title: "Write",
556
- target: rawPath ? displayPath(rawPath) : undefined,
580
+ target: rawPath ? sanitizeText(displayPath(rawPath)) : undefined,
557
581
  targetKind: "path",
558
582
  href: fileHref(details?.resolvedPath),
559
583
  meta: lineCount > 0 ? [`${lineCount} line${lineCount === 1 ? "" : "s"}`] : undefined,
@@ -611,7 +635,7 @@ export const writeToolRenderer = {
611
635
  if (xdev.name === REPORT_ISSUE_DEVICE_NAME) return renderReportIssueDeviceCall(args.content, uiTheme);
612
636
  return renderXdevCall(xdev.name, args.content, options, uiTheme, options.renderContext?.resolveXdevMounted);
613
637
  }
614
- const filePath = shortenPath(rawPath);
638
+ const filePath = sanitizeText(shortenPath(rawPath));
615
639
  const lang = rawPath ? (getLanguageFromPath(rawPath) ?? "text") : "text";
616
640
  const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
617
641
  const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
@@ -639,7 +663,7 @@ export const writeToolRenderer = {
639
663
  const streamingCache = createRenderedStringCache();
640
664
  return framedToolCard(
641
665
  uiTheme,
642
- () => {
666
+ ({ contentWidth }) => {
643
667
  const body = content
644
668
  ? formatStreamingContent(
645
669
  content,
@@ -660,7 +684,7 @@ export const writeToolRenderer = {
660
684
  while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
661
685
  return {
662
686
  header,
663
- sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
687
+ sections: bodyLines.length > 0 ? [{ content: wrapWritePreviewRows(bodyLines, contentWidth) }] : [],
664
688
  phase: "pending",
665
689
  borderColor: "borderMuted",
666
690
  };
@@ -695,14 +719,14 @@ export const writeToolRenderer = {
695
719
  }
696
720
  const rawPath =
697
721
  typeof args?.file_path === "string" ? args.file_path : typeof args?.path === "string" ? args.path : "";
698
- const filePath = shortenPath(rawPath);
722
+ const filePath = sanitizeText(shortenPath(rawPath));
699
723
  const fileContent = normalizeDisplayText(args?.content);
700
724
  const lang = rawPath ? getLanguageFromPath(rawPath) : undefined;
701
725
  const langIcon = uiTheme.fg("muted", uiTheme.getLangIcon(lang));
702
726
  // The header shows the cwd-relative path but links to the absolute path the
703
727
  // write resolved to (args.path may be relative, which would yield a broken
704
728
  // `file://` URI). Falls back to plain text when the result lacks a path.
705
- const linkTarget = result.details?.resolvedPath;
729
+ const linkTarget = writeResolvedPath(options.renderContext?.rawDetails, result.details?.resolvedPath);
706
730
  const styledPath = filePath ? uiTheme.fg("accent", filePath) : uiTheme.fg("toolOutput", "…");
707
731
  const pathDisplay = filePath && linkTarget ? fileHyperlink(linkTarget, styledPath) : styledPath;
708
732
 
@@ -743,7 +767,7 @@ export const writeToolRenderer = {
743
767
  const previewCache = createRenderedStringCache();
744
768
  return framedToolCard(
745
769
  uiTheme,
746
- () => {
770
+ ({ contentWidth }) => {
747
771
  const { expanded } = options;
748
772
  let body = renderContentPreview(fileContent, expanded, lang, uiTheme, previewCache);
749
773
  if (isPartial && progressText) {
@@ -768,7 +792,7 @@ export const writeToolRenderer = {
768
792
  while (bodyLines.length > 0 && bodyLines[0].trim() === "") bodyLines.shift();
769
793
  return {
770
794
  header,
771
- sections: bodyLines.length > 0 ? [{ content: bodyLines }] : [],
795
+ sections: bodyLines.length > 0 ? [{ content: wrapWritePreviewRows(bodyLines, contentWidth) }] : [],
772
796
  phase: isPartial ? "partial" : "success",
773
797
  borderColor: "borderMuted",
774
798
  };
@@ -825,7 +849,7 @@ export const writeToolRenderer = {
825
849
  const diagnostics = isPartial ? undefined : result.details?.diagnostics;
826
850
  const tool = writeToolHead(rawPath, fileContent, {
827
851
  madeExecutable: !isPartial && result.details?.madeExecutable === true,
828
- resolvedPath: result.details?.resolvedPath,
852
+ resolvedPath: writeResolvedPath(options.renderContext?.rawDetails, result.details?.resolvedPath),
829
853
  diagnostics,
830
854
  });
831
855
  if (result.isError) return { tool, tone: "error", body: [errorText(resultText(result))] };
@@ -9,7 +9,7 @@ import { compact, md } from "../native/describe";
9
9
  import { OwnerMemo } from "../native/memo";
10
10
  import type { NativeChild } from "../native/node";
11
11
  import { formatOutputPaneLines, styleToolOutputLine } from "../render/output-pane";
12
- import { formatExpandHint, sanitizeDisplayLines, truncateToWidth } from "../render/render-utils";
12
+ import { clipCollapsed, formatExpandHint, sanitizeDisplayLines } from "../render/render-utils";
13
13
  import type { StatusLineOptions } from "../render/status-line";
14
14
  import { plainToolCard, type ToolCardPhase } from "../render/tool-card";
15
15
  import type { Theme } from "../theme/theme";
@@ -134,7 +134,7 @@ function dataLines(data: unknown, expanded: boolean, width: number, theme: Theme
134
134
  lines: sanitizeText(data).trimEnd().split("\n"),
135
135
  expanded,
136
136
  collapsedMaxLines: TEXT_ROWS_COLLAPSED,
137
- styleLine: line => truncateToWidth(styleToolOutputLine(line, theme), width),
137
+ styleLine: line => clipCollapsed(styleToolOutputLine(line, theme), width, expanded),
138
138
  },
139
139
  theme,
140
140
  ).lines,
package/src/tui.d.ts CHANGED
@@ -115,9 +115,6 @@ export interface Component {
115
115
  * last time.
116
116
  */
117
117
  render(width: number): readonly string[];
118
- /** Inline decision panels must keep displaced settled transcript rows in
119
- * native scrollback even when mounted inside a transient editor container. */
120
- readonly retireDisplacedTranscript?: boolean;
121
118
  /**
122
119
  * Describe the component semantically for a Tern Surface Protocol
123
120
  * terminal (see `native/node.ts`). Called instead of `render()` when the
@@ -351,6 +348,7 @@ export declare class TUI extends Container {
351
348
  overlayStack: {
352
349
  component: Component;
353
350
  options?: OverlayOptions;
351
+ /** Focus restored after closing; tracks live focus changes beneath an input-less key holder. */
354
352
  preFocus: Component | null;
355
353
  hidden: boolean;
356
354
  /**
@@ -458,6 +456,16 @@ export declare class TUI extends Container {
458
456
  top: number;
459
457
  length: number;
460
458
  };
459
+ /**
460
+ * Rows of native history the last paint left on screen above a mutable
461
+ * viewport that reached the bottom row, or `undefined` when that paint left
462
+ * rows free below it (or none happened, or a destructive reset will repaint
463
+ * from row zero). Rows above a bottom-reaching viewport may already sit in
464
+ * native scrollback, which a later shrink cannot pull back down.
465
+ */
466
+ getFilledHistoryRows(): number | undefined;
467
+ /** Whether native history holds rows the frame provider committed since the last destructive reset. */
468
+ hasProviderHistory(): boolean;
461
469
  /**
462
470
  * Probe for opt-in normal-buffer click capture. The provider is read every
463
471
  * frame; while it returns true (and no fullscreen overlay owns the
@@ -507,6 +515,13 @@ export declare class TUI extends Container {
507
515
  * overtaking them. No-op unless `previous` owns the held keys.
508
516
  */
509
517
  replaceHeldFocus(previous: Component, next: Component): void;
518
+ /**
519
+ * Register a hook that re-enables terminal modes on every start(). A hook
520
+ * added while the TUI is already running (a prepaint composer starts it
521
+ * before the interactive mode wires its controllers) missed that start, so
522
+ * it runs immediately instead of waiting for a stop/start cycle that may
523
+ * never come.
524
+ */
510
525
  addStartListener(listener: StartListener): () => void;
511
526
  addInputListener(listener: InputListener): () => void;
512
527
  removeInputListener(listener: InputListener): void;
@@ -519,6 +534,8 @@ export declare class TUI extends Container {
519
534
  */
520
535
  resetDisplay(): void;
521
536
  requestRender(force?: boolean, options?: RenderRequestOptions): void;
537
+ /** Run `callback` after `delayMs` on the render scheduler's clock, unless the TUI has stopped. */
538
+ scheduleAfter(delayMs: number, callback: () => void): RenderTimer;
522
539
  /**
523
540
  * Paint a forced frame synchronously when startup must hand off an already
524
541
  * visible component tree before further async initialization. Same as