@deepseek-harness-tui/dsh-tui 0.8.1 → 0.8.3

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 (241) hide show
  1. package/README.md +30 -15
  2. package/bin/dsh-tui.js +43 -7
  3. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
  5. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
  7. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
  8. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
  9. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
  11. package/dsh-ecosystem-spec/registry/README.md +14 -0
  12. package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
  13. package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
  14. package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
  15. package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
  16. package/{ecosystem-spec → dsh-ecosystem-spec}/registry/permissions-0.1.json +1 -1
  17. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
  18. package/lib/types/command-trees.d.ts +2 -1
  19. package/lib/types/command-trees.d.ts.map +1 -1
  20. package/lib/types/command-trees.js +1 -1
  21. package/lib/types/components/LoadedContextPanel.d.ts +10 -5
  22. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  23. package/lib/types/components/LoadedContextPanel.js +18 -7
  24. package/lib/types/components/LogoV2.d.ts.map +1 -1
  25. package/lib/types/components/LogoV2.js +16 -7
  26. package/lib/types/components/Markdown.d.ts +13 -1
  27. package/lib/types/components/Markdown.d.ts.map +1 -1
  28. package/lib/types/components/Markdown.js +15 -1
  29. package/lib/types/components/MessageList.d.ts +3 -1
  30. package/lib/types/components/MessageList.d.ts.map +1 -1
  31. package/lib/types/components/MessageList.js +90 -9
  32. package/lib/types/components/PromptInput.d.ts.map +1 -1
  33. package/lib/types/components/PromptInput.js +26 -12
  34. package/lib/types/components/StreamingMarkdown.d.ts +2 -8
  35. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  36. package/lib/types/components/StreamingMarkdown.js +51 -9
  37. package/lib/types/components/activityFrames.d.ts +1 -1
  38. package/lib/types/components/activityFrames.d.ts.map +1 -1
  39. package/lib/types/components/activityFrames.js +4 -1
  40. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
  41. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  42. package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
  43. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  44. package/lib/types/components/messages/AssistantToolUseMessage.js +12 -1
  45. package/lib/types/dsh-adapter/channel.d.ts +31 -4
  46. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  47. package/lib/types/dsh-adapter/channel.js +267 -102
  48. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  49. package/lib/types/dsh-adapter/childStderr.js +10 -0
  50. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -1
  51. package/lib/types/dsh-adapter/command-attribution.js +2 -2
  52. package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
  53. package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
  54. package/lib/types/dsh-adapter/command-trees.js +70 -20
  55. package/lib/types/dsh-adapter/component-identity.d.ts +5 -0
  56. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -1
  57. package/lib/types/dsh-adapter/component-identity.js +14 -14
  58. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -1
  59. package/lib/types/dsh-adapter/decision-guard.js +10 -16
  60. package/lib/types/dsh-adapter/dialogs.d.ts +2 -3
  61. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -1
  62. package/lib/types/dsh-adapter/dialogs.js +114 -60
  63. package/lib/types/dsh-adapter/effect-ledger.d.ts +0 -8
  64. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
  65. package/lib/types/dsh-adapter/effect-ledger.js +73 -53
  66. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -1
  67. package/lib/types/dsh-adapter/extension-events.js +39 -1
  68. package/lib/types/dsh-adapter/grants.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/grants.js +43 -14
  70. package/lib/types/dsh-adapter/host-access.d.ts +37 -0
  71. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
  72. package/lib/types/dsh-adapter/host-access.js +652 -0
  73. package/lib/types/dsh-adapter/host-descriptor.d.ts +2 -2
  74. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -1
  75. package/lib/types/dsh-adapter/host-descriptor.js +31 -8
  76. package/lib/types/dsh-adapter/index.d.ts +4 -0
  77. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/index.js +1 -0
  79. package/lib/types/dsh-adapter/message-observer.d.ts +10 -15
  80. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -1
  81. package/lib/types/dsh-adapter/message-observer.js +114 -70
  82. package/lib/types/dsh-adapter/plugin-host.d.ts +39 -6
  83. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -1
  84. package/lib/types/dsh-adapter/plugin-host.js +139 -44
  85. package/lib/types/dsh-adapter/plugin-storage.d.ts +0 -5
  86. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -1
  87. package/lib/types/dsh-adapter/plugin-storage.js +48 -36
  88. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  89. package/lib/types/dsh-adapter/plugin.js +97 -16
  90. package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
  91. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  92. package/lib/types/dsh-adapter/promptDebug.js +13 -4
  93. package/lib/types/dsh-adapter/renderers.d.ts +6 -14
  94. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -1
  95. package/lib/types/dsh-adapter/renderers.js +81 -46
  96. package/lib/types/dsh-adapter/scenes.d.ts +8 -4
  97. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  98. package/lib/types/dsh-adapter/scenes.js +136 -37
  99. package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
  100. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  101. package/lib/types/dsh-adapter/settings-sections.js +106 -26
  102. package/lib/types/dsh-adapter/shortcuts.d.ts +8 -10
  103. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -1
  104. package/lib/types/dsh-adapter/shortcuts.js +108 -36
  105. package/lib/types/dsh-adapter/status.d.ts +6 -7
  106. package/lib/types/dsh-adapter/status.d.ts.map +1 -1
  107. package/lib/types/dsh-adapter/status.js +88 -22
  108. package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
  109. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/workspaces.js +287 -170
  111. package/lib/types/extensions.d.ts +10 -5
  112. package/lib/types/extensions.d.ts.map +1 -1
  113. package/lib/types/extensions.js +11 -5
  114. package/lib/types/force-production-react.d.ts +1 -0
  115. package/lib/types/force-production-react.d.ts.map +1 -0
  116. package/lib/types/force-production-react.js +12 -0
  117. package/lib/types/i18n.d.ts +22 -6
  118. package/lib/types/i18n.d.ts.map +1 -1
  119. package/lib/types/i18n.js +14 -2
  120. package/lib/types/index.d.ts +1 -0
  121. package/lib/types/index.d.ts.map +1 -1
  122. package/lib/types/index.js +1 -0
  123. package/lib/types/ink/components/App.d.ts +2 -0
  124. package/lib/types/ink/components/App.d.ts.map +1 -1
  125. package/lib/types/ink/components/App.js +4 -0
  126. package/lib/types/ink/ink.d.ts +20 -3
  127. package/lib/types/ink/ink.d.ts.map +1 -1
  128. package/lib/types/ink/ink.js +57 -5
  129. package/lib/types/ink/log-update.d.ts.map +1 -1
  130. package/lib/types/ink/log-update.js +128 -9
  131. package/lib/types/ink/output.d.ts +28 -2
  132. package/lib/types/ink/output.d.ts.map +1 -1
  133. package/lib/types/ink/output.js +158 -12
  134. package/lib/types/ink/screen.d.ts +39 -0
  135. package/lib/types/ink/screen.d.ts.map +1 -1
  136. package/lib/types/ink/screen.js +117 -0
  137. package/lib/types/ink/terminal.d.ts.map +1 -1
  138. package/lib/types/ink/terminal.js +10 -2
  139. package/lib/types/ink/wrapAnsi.d.ts +1 -1
  140. package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
  141. package/lib/types/ink/wrapAnsi.js +29 -2
  142. package/lib/types/plugin-host.d.ts +13 -11
  143. package/lib/types/plugin-host.d.ts.map +1 -1
  144. package/lib/types/plugin-host.js +16 -16
  145. package/lib/types/plugin-spec/registry.d.ts +2 -2
  146. package/lib/types/plugin-spec/registry.d.ts.map +1 -1
  147. package/lib/types/plugin-spec/registry.js +113 -18
  148. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -1
  149. package/lib/types/plugin-spec/tui-extension.js +2 -0
  150. package/lib/types/plugin-spec/validate.d.ts.map +1 -1
  151. package/lib/types/plugin-spec/validate.js +20 -1
  152. package/lib/types/scenes.d.ts +2 -1
  153. package/lib/types/scenes.d.ts.map +1 -1
  154. package/lib/types/scenes.js +1 -1
  155. package/lib/types/screens/Chat.d.ts +3 -3
  156. package/lib/types/screens/Chat.d.ts.map +1 -1
  157. package/lib/types/screens/Chat.js +38 -17
  158. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  159. package/lib/types/screens/SessionBrowser.js +10 -3
  160. package/lib/types/sessions/view.d.ts +3 -2
  161. package/lib/types/sessions/view.d.ts.map +1 -1
  162. package/lib/types/sessions/view.js +18 -0
  163. package/lib/types/settings-sections.d.ts +2 -1
  164. package/lib/types/settings-sections.d.ts.map +1 -1
  165. package/lib/types/settings-sections.js +1 -1
  166. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
  167. package/lib/types/utils/execFileNoThrow.js +3 -0
  168. package/lib/types/workspaces.d.ts +2 -1
  169. package/lib/types/workspaces.d.ts.map +1 -1
  170. package/lib/types/workspaces.js +1 -1
  171. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
  172. package/node_modules/@dsh-std/command/package.json +1 -1
  173. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  174. package/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  175. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  176. package/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  177. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  178. package/node_modules/@dsh-std/connection/package.json +1 -1
  179. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  180. package/node_modules/@dsh-std/core/package.json +1 -1
  181. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  182. package/node_modules/@dsh-std/manifest/package.json +1 -1
  183. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
  184. package/node_modules/@dsh-std/messages/package.json +1 -1
  185. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  186. package/node_modules/@dsh-std/presentation/README.md +2 -1
  187. package/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  188. package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  189. package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  190. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  191. package/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  192. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  193. package/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  194. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  195. package/node_modules/@dsh-std/presentation/package.json +7 -3
  196. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
  197. package/node_modules/@dsh-std/storage/package.json +1 -1
  198. package/package.json +10 -5
  199. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  200. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  201. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  202. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  203. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  204. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +1 -1
  205. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  206. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +1 -1
  207. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  208. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +1 -1
  209. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  210. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +2 -1
  211. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +2 -1
  212. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  213. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  214. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  215. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +44 -1
  216. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -1
  217. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +6 -3
  218. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -1
  219. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +7 -3
  220. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  221. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +1 -1
  222. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  223. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +1 -1
  224. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  225. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +1 -1
  226. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  227. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +1 -1
  228. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -1
  229. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +1 -0
  230. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -1
  231. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +1 -1
  232. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  233. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +1 -1
  234. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  235. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +1 -1
  236. package/ecosystem-spec/registry/registry-0.15.json +0 -57
  237. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/contracts/decision-events-v1alpha1.json +0 -0
  238. /package/{ecosystem-spec → dsh-ecosystem-spec}/registry/host-descriptor.tui.example.json +0 -0
  239. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/conformance-claim.schema.json +0 -0
  240. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/effect-ledger-record.schema.json +0 -0
  241. /package/{ecosystem-spec → dsh-ecosystem-spec}/schemas/host-descriptor.schema.json +0 -0
@@ -4,7 +4,7 @@ import { getGraphemeSegmenter } from '../utils/intl.js';
4
4
  import sliceAnsi from '../utils/sliceAnsi.js';
5
5
  import { reorderBidi } from './bidi.js';
6
6
  import { unionRect } from './layout/geometry.js';
7
- import { blitRegion, extractHyperlinkFromStyles, filterOutHyperlinkStyles, markNoSelectRegion, OSC8_PREFIX, resetScreen, setCellAt, shiftRows, } from './screen.js';
7
+ import { blitRegion, cellRunSpan, createCellRun, extractHyperlinkFromStyles, filterOutHyperlinkStyles, markNoSelectRegion, OSC8_PREFIX, recordCellRunEntry, recordSpacerRunEntry, replayCellRun, resetScreen, setCellAt, shiftRows, } from './screen.js';
8
8
  import { stringWidth } from './stringWidth.js';
9
9
  import { widestLine } from './widest-line.js';
10
10
  /**
@@ -29,6 +29,11 @@ import { widestLine } from './widest-line.js';
29
29
  const MAX_CACHEABLE_LINE = 500;
30
30
  const CHAR_CACHE_MAX_ENTRIES = 16384;
31
31
  const CHAR_CACHE_MAX_CHARS = 100_000;
32
+ /** Packed-run line cap: runs are three integers per cell (no per-char
33
+ * objects like charCache entries), so long ANSI-art lines (the whale
34
+ * logo, ~700 chars) are worth caching — they would otherwise
35
+ * re-tokenize and re-cluster every frame. */
36
+ const PACKED_MAX_LINE = 4000;
32
37
  /** Copy a string into a fresh flat string with no parent references. */
33
38
  function detachString(s) {
34
39
  // Buffer round-trip guarantees a newly allocated flat string; cheaper
@@ -41,28 +46,58 @@ function detachString(s) {
41
46
  * no caller can accidentally grow it unboundedly.
42
47
  */
43
48
  export class CharCache {
49
+ /**
50
+ * LRU of line → clustered characters. A full clear on overflow
51
+ * (the original behavior) thrashes during long-session streaming: the
52
+ * mounted window's static lines alone approach the char budget, so the
53
+ * streaming row's per-frame new keys tip it over every frame or two,
54
+ * nuking the cache and forcing ~1800 line re-tokenizations per frame
55
+ * (measured ~100ms/frame at a 175-row session). Evicting oldest-first
56
+ * keeps the hot mounted lines resident while streaming churn falls out
57
+ * the back. Recency is refreshed on hit (delete + re-insert) so lines
58
+ * read every frame are never evicted by one-shot churn.
59
+ */
44
60
  map = new Map();
45
61
  chars = 0;
46
62
  /**
47
- * Return the cached clustered characters for a line, if present.
63
+ * Return the cached clustered characters for a line, if present, and
64
+ * refresh the entry's recency.
48
65
  * @param line - the line to look up.
49
66
  * @returns the cached characters, or undefined on a miss.
50
67
  */
51
68
  get(line) {
52
- return this.map.get(line);
69
+ const hit = this.map.get(line);
70
+ if (hit !== undefined) {
71
+ this.map.delete(line);
72
+ this.map.set(line, hit);
73
+ }
74
+ return hit;
53
75
  }
54
76
  /**
55
- * Cache clustered characters for a line, enforcing the cache bounds.
77
+ * Cache clustered characters for a line, enforcing the cache bounds by
78
+ * evicting least-recently-used entries (never a full clear).
56
79
  * @param line - the line key.
57
80
  * @param characters - the clustered characters to cache.
58
81
  */
59
82
  set(line, characters) {
60
83
  if (line.length > MAX_CACHEABLE_LINE)
61
84
  return;
62
- if (this.map.size >= CHAR_CACHE_MAX_ENTRIES ||
85
+ if (this.map.has(line)) {
86
+ this.map.delete(line);
87
+ this.chars -= line.length;
88
+ }
89
+ // Evict oldest entries until the new one fits. A single line longer
90
+ // than the whole budget cannot fit even empty — skip caching it.
91
+ if (line.length > CHAR_CACHE_MAX_CHARS)
92
+ return;
93
+ while (this.map.size >= CHAR_CACHE_MAX_ENTRIES ||
63
94
  this.chars + line.length > CHAR_CACHE_MAX_CHARS) {
64
- this.map.clear();
65
- this.chars = 0;
95
+ const oldest = this.map.keys().next();
96
+ if (oldest.done === true)
97
+ break;
98
+ const oldestKey = oldest.value;
99
+ this.chars -= oldestKey.length;
100
+ this.map.delete(oldestKey);
66
101
  }
67
102
  this.map.set(detachString(line), characters);
68
103
  this.chars += line.length;
@@ -106,6 +141,13 @@ function minDefined(a, b) {
106
141
  return a;
107
142
  return Math.min(a, b);
108
143
  }
144
+ /**
145
+ * Slot-reuse ceiling: the clustered array is a second full memory image of
146
+ * the line, and pathological single-line output (base64 blobs, minified
147
+ * code) would pin it across frames on top of the string itself. Past this
148
+ * length every frame takes the full path — transient, GC-able.
149
+ */
150
+ const STREAMING_SLOT_MAX_LINE = 65_536;
109
151
  /**
110
152
  * Classify the tail of the line relative to its LAST ESC character.
111
153
  * - 'none': no ESC at all
@@ -172,6 +214,7 @@ function hasRtlChars(text) {
172
214
  * The slot is only kept when the line can safely be extended next frame. */
173
215
  function updateStreamingSlot(slot, line, clustered, chars) {
174
216
  if (line.length <= MAX_CACHEABLE_LINE ||
217
+ line.length > STREAMING_SLOT_MAX_LINE ||
175
218
  endsWithOpenGrapheme(line) ||
176
219
  hasRtlChars(line)) {
177
220
  // Short lines are served by CharCache; complex tails always take the
@@ -234,6 +277,41 @@ export default class Output {
234
277
  screen;
235
278
  operations = [];
236
279
  charCache = new CharCache();
280
+ /**
281
+ * LRU of line → recorded packed cell run, mirroring charCache's bounds.
282
+ * The main-screen architecture repaints every settled line each frame;
283
+ * the recorded run replays as raw two-word stores (no interning, no
284
+ * packing, no guard branches) — the long-session streaming fix. Runs are
285
+ * validated against the screen they were interned on and die with it.
286
+ */
287
+ packedLines = new Map();
288
+ packedChars = 0;
289
+ /** Stable owner handle passed to writeLineToScreen (no per-call closure). */
290
+ packedOwner = {
291
+ lines: this.packedLines,
292
+ commit: (line, run) => {
293
+ this.setPackedLine(line, run);
294
+ },
295
+ };
296
+ /** Insert into the packed-lines LRU, evicting oldest entries on budget. */
297
+ setPackedLine(line, run) {
298
+ if (line.length > PACKED_MAX_LINE)
299
+ return;
300
+ if (this.packedLines.has(line)) {
301
+ this.packedLines.delete(line);
302
+ this.packedChars -= line.length;
303
+ }
304
+ while (this.packedLines.size >= CHAR_CACHE_MAX_ENTRIES ||
305
+ this.packedChars + line.length > CHAR_CACHE_MAX_CHARS) {
306
+ const oldest = this.packedLines.keys().next();
307
+ if (oldest.done === true)
308
+ break;
309
+ this.packedChars -= oldest.value.length;
310
+ this.packedLines.delete(oldest.value);
311
+ }
312
+ this.packedLines.set(detachString(line), run);
313
+ this.packedChars += line.length;
314
+ }
237
315
  /**
238
316
  * Streaming-tail slot, shared by every writeLineToScreen call of this
239
317
  * Output (only one line grows per frame during streaming). Survives reset()
@@ -482,6 +560,12 @@ export default class Output {
482
560
  const from = x < clip.x1 ? clip.x1 - x : 0;
483
561
  const width = stringWidth(line);
484
562
  const to = x + width > clip.x2 ? clip.x2 - x : width;
563
+ // Fast path: the line sits entirely inside the clip — no
564
+ // slice needed. sliceAnsi re-tokenizes the line (the
565
+ // dominant per-frame cost of long sessions otherwise:
566
+ // every settled line, every frame).
567
+ if (from === 0 && to === width)
568
+ return line;
485
569
  let sliced = sliceAnsi(line, from, to);
486
570
  // Wide chars (CJK, emoji) occupy 2 cells. When `to` lands
487
571
  // on the first cell of a wide char, sliceAnsi includes the
@@ -524,7 +608,7 @@ export default class Output {
524
608
  if (lineY >= screenHeight) {
525
609
  break;
526
610
  }
527
- const contentEnd = writeLineToScreen(screen, line, x, lineY, screenWidth, this.stylePool, this.charCache, this.streamingSlot);
611
+ const contentEnd = writeLineToScreen(screen, line, x, lineY, screenWidth, this.stylePool, this.charCache, this.streamingSlot, this.packedOwner);
528
612
  writeCells += contentEnd - x;
529
613
  // See Screen.softWrap docstring for the encoding. contentEnd
530
614
  // from writeLineToScreen is tab-expansion-aware, unlike
@@ -641,12 +725,45 @@ function flushBuffer(buffer, styles, stylePool, out) {
641
725
  * the caller can record it in screen.softWrap without re-walking the
642
726
  * line via stringWidth(). Caller computes the debug cell-count as end-x.
643
727
  */
644
- function writeLineToScreen(screen, line, x, y, screenWidth, stylePool, charCache, streamingSlot) {
728
+ function writeLineToScreen(screen, line, x, y, screenWidth, stylePool, charCache, streamingSlot, packed) {
729
+ // Fast path: replay the recorded packed run for this exact line — two
730
+ // typed-array stores per cell, no interning/packing/guard work. The run
731
+ // dies with its screen; a width transition needs the slow path's guards
732
+ // and replayCellRun reports that.
733
+ if (packed !== undefined) {
734
+ const run = packed.lines.get(line);
735
+ if (run !== undefined &&
736
+ run.charPool === screen.charPool &&
737
+ x >= 0 &&
738
+ x + cellRunSpan(run) <= screenWidth) {
739
+ if (replayCellRun(screen, x, y, run)) {
740
+ // LRU refresh (chars budget unchanged: same key length).
741
+ packed.lines.delete(line);
742
+ packed.lines.set(line, run);
743
+ return x + cellRunSpan(run);
744
+ }
745
+ // fall through to the slow path for this line this frame
746
+ }
747
+ }
645
748
  let characters = charCache.get(line);
646
749
  if (!characters) {
647
750
  characters = buildClusteredChars(line, stylePool, streamingSlot);
648
751
  charCache.set(line, characters);
649
752
  }
753
+ // Recording for the packed fast path: only plain single/wide char cells
754
+ // are recordable — control chars (tab expansion is x-relative), edge
755
+ // SpacerHead substitution, and y/x off-screen abort it. The packed-run
756
+ // limit is far above the charCache's MAX_CACHEABLE_LINE: a run costs
757
+ // three small integers per cell (no per-char objects), so even the
758
+ // ~700-char ANSI-art whale lines belong — without this they
759
+ // re-tokenize and re-cluster EVERY frame.
760
+ const recordable = packed !== undefined &&
761
+ line.length <= PACKED_MAX_LINE &&
762
+ x >= 0 &&
763
+ y >= 0 &&
764
+ y < screen.height;
765
+ let run = recordable ? createCellRun(screen) : undefined;
766
+ let runAlive = run !== undefined;
650
767
  let offsetX = x;
651
768
  for (let charIdx = 0; charIdx < characters.length; charIdx++) {
652
769
  const character = characters[charIdx];
@@ -655,6 +772,8 @@ function writeLineToScreen(screen, line, x, y, screenWidth, stylePool, charCache
655
772
  // mismatches. stringWidth treats these as width 0, but terminals may
656
773
  // move the cursor differently.
657
774
  if (codePoint !== undefined && codePoint <= 0x1f) {
775
+ // Not recordable: tab expansion is x-relative, ESC handling varies.
776
+ runAlive = false;
658
777
  // Tab (0x09): expand to spaces to reach next tab stop
659
778
  if (codePoint === 0x09) {
660
779
  const tabWidth = 8;
@@ -757,9 +876,10 @@ function writeLineToScreen(screen, line, x, y, screenWidth, stylePool, charCache
757
876
  }
758
877
  const isWideCharacter = charWidth >= 2;
759
878
  // Wide char at last column can't fit — terminal would wrap it to
760
- // the next line, desyncing our cursor model. Place a SpacerHead
761
- // to mark the blank column, matching terminal behavior.
879
+ // the next line, desyncing our cursor model. Place a SpacerHead to
880
+ // mark the blank column, matching terminal behavior.
762
881
  if (isWideCharacter && offsetX + 2 > screenWidth) {
882
+ runAlive = false;
763
883
  setCellAt(screen, offsetX, y, {
764
884
  char: ' ',
765
885
  styleId: stylePool.none,
@@ -772,13 +892,39 @@ function writeLineToScreen(screen, line, x, y, screenWidth, stylePool, charCache
772
892
  // styleId + hyperlink were precomputed during clustering (once per
773
893
  // style run, cached via charCache). Hot loop is now just property
774
894
  // reads — no intern, no extract, no filter per frame.
895
+ const cellWidth = isWideCharacter ? 1 /* CellWidth.Wide */ : 0 /* CellWidth.Narrow */;
775
896
  setCellAt(screen, offsetX, y, {
776
897
  char: character.value,
777
898
  styleId: character.styleId,
778
- width: isWideCharacter ? 1 /* CellWidth.Wide */ : 0 /* CellWidth.Narrow */,
899
+ width: cellWidth,
779
900
  hyperlink: character.hyperlink,
780
901
  });
902
+ if (runAlive && run !== undefined) {
903
+ recordCellRunEntry(run, screen, offsetX, {
904
+ char: character.value,
905
+ styleId: character.styleId,
906
+ width: cellWidth,
907
+ hyperlink: character.hyperlink,
908
+ });
909
+ // setCellAt also writes a SpacerTail after a wide char (when it
910
+ // fits); record that deterministic write too, or abort at the edge.
911
+ if (isWideCharacter) {
912
+ if (offsetX + 1 < screenWidth) {
913
+ recordSpacerRunEntry(run, offsetX + 1);
914
+ }
915
+ else {
916
+ runAlive = false;
917
+ }
918
+ }
919
+ }
781
920
  offsetX += isWideCharacter ? 2 : 1;
782
921
  }
922
+ // Commit the recording for the packed fast path. Complete NON-EMPTY
923
+ // runs only: an empty line ('' from split('\n'), zero-width-only lines)
924
+ // records zero entries and would commit a degenerate run whose replay
925
+ // writes NaN damage (gaps[0] undefined) and poisons the whole frame diff.
926
+ if (runAlive && run !== undefined && packed !== undefined && run.gaps.length > 0) {
927
+ packed.commit(line, run);
928
+ }
783
929
  return offsetX;
784
930
  }
@@ -339,6 +339,45 @@ export declare function charInCellAt(screen: Screen, x: number, y: number): stri
339
339
  * @param cell - the cell data to write.
340
340
  */
341
341
  export declare function setCellAt(screen: Screen, x: number, y: number, cell: Cell): void;
342
+ /** A recorded run of packed cell writes for one screen line. */
343
+ export interface CellRun {
344
+ /** Char pool the words were interned against. Screens ping-pong
345
+ * (front/back double buffering) but SHARE their pools for a whole
346
+ * session, so pool identity — not screen identity — is the validation
347
+ * token; runs survive the screen swap and die only with the pools
348
+ * (renderer recreation/resize). */
349
+ charPool: CharPool;
350
+ /** The shared stylePool.none — the spacer cells' style word. */
351
+ emptyStyleId: number;
352
+ /** Column gap from the previous written cell; the first entry's gap is
353
+ * 0 (its absolute column is the run's base). */
354
+ gaps: number[];
355
+ charIdxs: number[];
356
+ word1s: number[];
357
+ /** Absolute column of the first written cell (record time). */
358
+ baseX: number;
359
+ /** Absolute column of the last written cell (record time). */
360
+ lastX: number;
361
+ }
362
+ /** Begin recording a run for the given screen. */
363
+ export declare function createCellRun(screen: Screen): CellRun;
364
+ /** Record the cell the slow path is about to write at column x. */
365
+ export declare function recordCellRunEntry(run: CellRun, screen: Screen, x: number, cell: Cell): void;
366
+ /** Record the deterministic SpacerTail that setCellAt writes after a wide
367
+ * char at column x (only when x < screen.width - 1). */
368
+ export declare function recordSpacerRunEntry(run: CellRun, x: number): void;
369
+ /**
370
+ * Replay a recorded run at (x, y): plain two-word stores, with the damage
371
+ * rect extended over the written span. Mirrors setCellAt's guard-fire
372
+ * conditions exactly — a replay is only valid when none of the three
373
+ * state-dependent fixups (wide-overwrite spacer cleanup, spacer-tail
374
+ * orphan cleanup, wide's next-cell orphan cleanup) would trigger; any
375
+ * other width transition is a plain overwrite. Returns false so the
376
+ * caller falls back to the slow path when a guard WOULD fire.
377
+ */
378
+ export declare function replayCellRun(screen: Screen, x: number, y: number, run: CellRun): boolean;
379
+ /** Column just past the last written cell — the run's content end. */
380
+ export declare function cellRunSpan(run: CellRun): number;
342
381
  /**
343
382
  * Replace the styleId of a cell in-place without disturbing char, width,
344
383
  * or hyperlink. Preserves empty cells as-is (char stays ' '). Tracks damage
@@ -1 +1 @@
1
- {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../../src/ink/screen.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"screen.d.ts","sourceRoot":"","sources":["../../../src/ink/screen.ts"],"names":[],"mappings":"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"}
@@ -721,6 +721,123 @@ export function setCellAt(screen, x, y, cell) {
721
721
  }
722
722
  }
723
723
  }
724
+ /** Begin recording a run for the given screen. */
725
+ export function createCellRun(screen) {
726
+ return {
727
+ charPool: screen.charPool,
728
+ emptyStyleId: screen.emptyStyleId,
729
+ gaps: [],
730
+ charIdxs: [],
731
+ word1s: [],
732
+ baseX: -1,
733
+ lastX: -1,
734
+ };
735
+ }
736
+ /** Record the cell the slow path is about to write at column x. */
737
+ export function recordCellRunEntry(run, screen, x, cell) {
738
+ if (run.baseX < 0)
739
+ run.baseX = x;
740
+ run.gaps.push(run.lastX < 0 ? 0 : x - run.lastX);
741
+ run.charIdxs.push(internCharString(screen, cell.char));
742
+ run.word1s.push(packWord1(cell.styleId, internHyperlink(screen, cell.hyperlink), cell.width));
743
+ run.lastX = x;
744
+ }
745
+ /** Record the deterministic SpacerTail that setCellAt writes after a wide
746
+ * char at column x (only when x < screen.width - 1). */
747
+ export function recordSpacerRunEntry(run, x) {
748
+ run.gaps.push(x - run.lastX);
749
+ run.charIdxs.push(SPACER_CHAR_INDEX);
750
+ run.word1s.push(packWord1(run.emptyStyleId, 0, 2 /* CellWidth.SpacerTail */));
751
+ run.lastX = x;
752
+ }
753
+ /**
754
+ * Replay a recorded run at (x, y): plain two-word stores, with the damage
755
+ * rect extended over the written span. Mirrors setCellAt's guard-fire
756
+ * conditions exactly — a replay is only valid when none of the three
757
+ * state-dependent fixups (wide-overwrite spacer cleanup, spacer-tail
758
+ * orphan cleanup, wide's next-cell orphan cleanup) would trigger; any
759
+ * other width transition is a plain overwrite. Returns false so the
760
+ * caller falls back to the slow path when a guard WOULD fire.
761
+ */
762
+ export function replayCellRun(screen, x, y, run) {
763
+ if (x < 0 || y < 0 || y >= screen.height || x >= screen.width)
764
+ return false;
765
+ // Defensive: an empty run has no cells and no span — its damage math
766
+ // would be NaN. Callers never cache empty runs, but a future one might.
767
+ if (run.gaps.length === 0)
768
+ return false;
769
+ const cells = screen.cells;
770
+ const width = screen.width;
771
+ let ox = x;
772
+ const gaps = run.gaps;
773
+ for (let i = 0; i < gaps.length; i++) {
774
+ ox += gaps[i];
775
+ if (ox >= width)
776
+ return false;
777
+ const ci = ((y * width) + ox) << 1;
778
+ const w1 = run.word1s[i];
779
+ const newW = w1 & WIDTH_MASK;
780
+ const prevW = cells[ci + 1] & WIDTH_MASK;
781
+ if (newW === 0 /* CellWidth.Narrow */) {
782
+ // G1 (prev Wide → narrow overwrite cleans the spacer at x+1) and
783
+ // G2 (prev SpacerTail → clears the orphan wide at x-1) would fire.
784
+ if (prevW !== 0 /* CellWidth.Narrow */)
785
+ return false;
786
+ }
787
+ else if (newW === 1 /* CellWidth.Wide */) {
788
+ // G2: a SpacerTail under a Wide write clears the orphan at x-1.
789
+ if (prevW === 2 /* CellWidth.SpacerTail */)
790
+ return false;
791
+ // G3: the spacer this write puts at x+1 would stomp a Wide cell
792
+ // and needs its orphan cleanup.
793
+ if (ox + 1 < width && (cells[ci + 3] & WIDTH_MASK) === 1 /* CellWidth.Wide */) {
794
+ return false;
795
+ }
796
+ }
797
+ else {
798
+ // SpacerTail: G1 fires when it overwrites a Wide cell; G2's
799
+ // SpacerHead variant (prev SpacerTail under a non-SpacerTail write)
800
+ // can't occur in a recorded run — SpacerHead is never recorded — but
801
+ // keep the guard honest for that day soft-wrap starts recording it.
802
+ if (prevW === 1 /* CellWidth.Wide */ ||
803
+ (newW !== 2 /* CellWidth.SpacerTail */ && prevW === 2 /* CellWidth.SpacerTail */)) {
804
+ return false;
805
+ }
806
+ }
807
+ cells[ci] = run.charIdxs[i];
808
+ cells[ci + 1] = w1;
809
+ }
810
+ // Damage: the full written span, matching what per-cell updates in
811
+ // setCellAt would have accumulated.
812
+ const firstX = x + gaps[0];
813
+ const endX = ox + 1;
814
+ const damage = screen.damage;
815
+ if (damage !== undefined) {
816
+ const right = damage.x + damage.width;
817
+ if (firstX < damage.x) {
818
+ damage.width += damage.x - firstX;
819
+ damage.x = firstX;
820
+ }
821
+ else if (endX > right) {
822
+ damage.width += endX - right;
823
+ }
824
+ if (y < damage.y) {
825
+ damage.height += damage.y - y;
826
+ damage.y = y;
827
+ }
828
+ else if (y >= damage.y + damage.height) {
829
+ damage.height = y - damage.y + 1;
830
+ }
831
+ }
832
+ else {
833
+ screen.damage = { x: firstX, y, width: endX - firstX, height: 1 };
834
+ }
835
+ return true;
836
+ }
837
+ /** Column just past the last written cell — the run's content end. */
838
+ export function cellRunSpan(run) {
839
+ return run.lastX - run.baseX + 1;
840
+ }
724
841
  /**
725
842
  * Replace the styleId of a cell in-place without disturbing char, width,
726
843
  * or hyperlink. Preserves empty cells as-is (char stays ' '). Tracks damage
@@ -1 +1 @@
1
- {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../../src/ink/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAKtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAatC;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,CAAA;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,OAAO,CAuCtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAwDvD;AAcD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAyBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,WAAW,GAAE,MAAM,GAAG,SAAoC,EAC1D,kBAAkB,GAAE,MAAM,GAAG,SAA4C,GACxE,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,SAAkC,CAAA;AAEpE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAuBD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,QAAQ,CAAA;CACjB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,eAAe,UAAQ,GACtB,IAAI,CAgFN"}
1
+ {"version":3,"file":"terminal.d.ts","sourceRoot":"","sources":["../../../src/ink/terminal.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAA;AAKtC,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAYtC;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,KAAK,EAAE,SAAS,GAAG,WAAW,GAAG,OAAO,GAAG,eAAe,CAAA;IAC1D,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED;;;;;;;;;GASG;AACH,wBAAgB,4BAA4B,IAAI,OAAO,CAuCtD;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sBAAsB,IAAI,OAAO,CAEhD;AAED;;;;GAIG;AACH,wBAAgB,6BAA6B,IAAI,OAAO,CAwDvD;AAcD;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAEnD;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,IAAI,OAAO,CAGnC;AAyBD;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,IAAI,OAAO,CAG9C;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,sBAAsB,CACpC,QAAQ,GAAE,MAAM,CAAC,QAA2B,EAC5C,WAAW,GAAE,MAAM,GAAG,SAAoC,EAC1D,kBAAkB,GAAE,MAAM,GAAG,SAA4C,GACxE,OAAO,CAOT;AAED;;;;;;;;GAQG;AACH,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,SAAkC,CAAA;AAEpE;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,IAAI,OAAO,CAEvC;AAuBD;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,EAAE,QAAQ,CAAA;CACjB,CAAA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE,IAAI,EACV,eAAe,UAAQ,GACtB,IAAI,CAwFN"}
@@ -2,7 +2,7 @@ import { coerce } from 'semver';
2
2
  import { appendFileSync } from 'node:fs';
3
3
  import { env } from '../utils/env.js';
4
4
  import { gte } from '../utils/semver.js';
5
- import { CURSOR_HOME, cursorMove, cursorTo, ERASE_SCREEN, ERASE_SCROLLBACK, eraseLines, SGR_RESET, } from './termio/csi.js';
5
+ import { CURSOR_HOME, cursorMove, cursorTo, ERASE_SCREEN, eraseLines, SGR_RESET, } from './termio/csi.js';
6
6
  import { BSU, ESU, HIDE_CURSOR, SHOW_CURSOR } from './termio/dec.js';
7
7
  import { link } from './termio/osc.js';
8
8
  /**
@@ -321,11 +321,19 @@ export function writeDiffToTerminal(terminal, diff, skipSyncMarkers = false) {
321
321
  // scrollback snapshots (the duplicated whale-logo class of bugs);
322
322
  // the old soft clear (CSI n S) PUSHED the live viewport into the
323
323
  // scrollback instead, depositing a fresh full-UI copy per reset.
324
+ // Screen-only hard clear: 2J + home, NO 3J. Erasing the scrollback
325
+ // here destroyed the user's entire visible history on every settle
326
+ // shrink (the "context lost / cannot scroll" reports) — the inline
327
+ // transcript IS the scrollback; wiping it to avoid duplicate
328
+ // snapshots is never an acceptable trade. 2J clears the screen for
329
+ // the repaint while everything above the viewport survives.
330
+ // Executed OUTSIDE the DEC 2026 sync block (split begin/end): WT
331
+ // yanks the viewport to top when 2J runs inside a synchronized
332
+ // update (claude-code#35580).
324
333
  buffer +=
325
334
  (useSync ? ESU : '') +
326
335
  SGR_RESET +
327
336
  ERASE_SCREEN +
328
- ERASE_SCROLLBACK +
329
337
  CURSOR_HOME +
330
338
  (useSync ? BSU : '');
331
339
  break;
@@ -7,7 +7,7 @@ type WrapAnsiOptions = {
7
7
  * Wrap a string to a maximum column width, preserving ANSI escape sequences.
8
8
  *
9
9
  * Uses Bun.wrapAnsi when available; otherwise falls back to the wrap-ansi
10
- * package.
10
+ * package (with the incremental fast path for growing streaming inputs).
11
11
  * @param input - the string to wrap.
12
12
  * @param columns - the maximum width in columns.
13
13
  * @param options - wrap options: hard breaks long words, wordWrap splits on word boundaries, trim strips trailing whitespace.
@@ -1 +1 @@
1
- {"version":3,"file":"wrapAnsi.d.ts","sourceRoot":"","sources":["../../../src/ink/wrapAnsi.ts"],"names":[],"mappings":"AAEA,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAOD;;;;;;;;;GASG;AACH,QAAA,MAAM,QAAQ,EAAE,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAAmC,CAAA;AAExC,OAAO,EAAE,QAAQ,EAAE,CAAA"}
1
+ {"version":3,"file":"wrapAnsi.d.ts","sourceRoot":"","sources":["../../../src/ink/wrapAnsi.ts"],"names":[],"mappings":"AAEA,KAAK,eAAe,GAAG;IACrB,IAAI,CAAC,EAAE,OAAO,CAAA;IACd,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,IAAI,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AA0DD;;;;;;;;;GASG;AACH,QAAA,MAAM,QAAQ,EAAE,CACd,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAEmB,CAAA;AAExB,OAAO,EAAE,QAAQ,EAAE,CAAA"}
@@ -2,15 +2,42 @@ import wrapAnsiNpm from 'wrap-ansi';
2
2
  const wrapAnsiBun = typeof Bun !== 'undefined' && typeof Bun.wrapAnsi === 'function'
3
3
  ? Bun.wrapAnsi
4
4
  : null;
5
+ const SLOT_MIN_LENGTH = 2048;
6
+ let slot = null;
7
+ function slotOptionsApply(options) {
8
+ return options?.hard === true && (options?.trim ?? false) === false;
9
+ }
10
+ function wrapAnsiNpmIncremental(input, columns, options) {
11
+ const slottable = input.length > SLOT_MIN_LENGTH && slotOptionsApply(options);
12
+ if (slottable && slot !== null && slot.columns === columns && input.startsWith(slot.input)) {
13
+ const suffix = input.slice(slot.input.length);
14
+ if (!suffix.includes('\x1b') && !slot.remainder.includes('\x1b')) {
15
+ const tail = wrapAnsiNpm(slot.remainder + suffix, columns, options).split('\n');
16
+ const rows = [...slot.headRows, ...tail];
17
+ slot = { columns, input, headRows: rows.slice(0, -1), remainder: rows[rows.length - 1] ?? '' };
18
+ return rows.join('\n');
19
+ }
20
+ }
21
+ const result = wrapAnsiNpm(input, columns, options);
22
+ if (slottable && !input.includes('\x1b')) {
23
+ const rows = result.split('\n');
24
+ slot = { columns, input, headRows: rows.slice(0, -1), remainder: rows[rows.length - 1] ?? '' };
25
+ }
26
+ else {
27
+ slot = null;
28
+ }
29
+ return result;
30
+ }
5
31
  /**
6
32
  * Wrap a string to a maximum column width, preserving ANSI escape sequences.
7
33
  *
8
34
  * Uses Bun.wrapAnsi when available; otherwise falls back to the wrap-ansi
9
- * package.
35
+ * package (with the incremental fast path for growing streaming inputs).
10
36
  * @param input - the string to wrap.
11
37
  * @param columns - the maximum width in columns.
12
38
  * @param options - wrap options: hard breaks long words, wordWrap splits on word boundaries, trim strips trailing whitespace.
13
39
  * @returns the wrapped string.
14
40
  */
15
- const wrapAnsi = wrapAnsiBun ?? wrapAnsiNpm;
41
+ const wrapAnsi = wrapAnsiBun ??
42
+ wrapAnsiNpmIncremental;
16
43
  export { wrapAnsi };
@@ -1,13 +1,15 @@
1
- export * from './dsh-adapter/plugin-host.js';
2
- export * from './dsh-adapter/grants.js';
3
- export * from './dsh-adapter/host-descriptor.js';
4
- export * from './dsh-adapter/plugin-storage.js';
5
- export * from './dsh-adapter/message-observer.js';
6
- export * from './dsh-adapter/effect-ledger.js';
7
- export * from './dsh-adapter/command-errors.js';
8
- export { commandOwner, fiberNameOf, stampCommandOwner, unstampCommandOwner } from './dsh-adapter/command-attribution.js';
9
- export { DECISION_EVENT_PERMISSIONS, decisionHandlersOf, registerDecisionHandler, withDecisionRegistration, } from './dsh-adapter/decision-guard.js';
10
- export { DECISION_HANDLER_TIMEOUT_MS, DECISION_TOTAL_TIMEOUT_MS, dispatchTuiNotification, } from './dsh-adapter/extension-events.js';
1
+ export { name, apply } from './dsh-adapter/plugin-host.js';
2
+ export type { TuiPluginHost } from './dsh-adapter/plugin-host.js';
3
+ export type { GrantPrincipal, GrantStore } from './dsh-adapter/grants.js';
4
+ export type { HostContract, HostDescriptor, ContractCoordinate, ContractRef, NegotiationDecision, PermissionEntry, PermissionRegistry } from './plugin-spec/types.js';
5
+ export type { TuiPluginStorage, PluginStorageErrorCode, TuiPluginStorageRuntime } from './dsh-adapter/plugin-storage.js';
6
+ export { PluginStorageError, STORAGE_KEY_MAX_LENGTH, STORAGE_MAX_BYTES, STORAGE_MAX_KEYS } from './dsh-adapter/plugin-storage.js';
7
+ export { OBSERVE_CALLBACK_QUEUE_LIMIT, OBSERVE_CALLBACK_TIMEOUT_MS, OBSERVE_CONTENT_MAX_CHARS, OBSERVE_ID_MAX_CHARS, OBSERVE_IMAGE_BASE64_MAX_CHARS, OBSERVE_IMAGE_MAX_BYTES, OBSERVE_SCOPE_MAX_CHARS, OBSERVE_SUMMARY_CELLS, } from './dsh-adapter/message-observer.js';
8
+ export type { MessagesObserveContentBlock, MessagesObserveEnvelope, MessagesObserveListener, MessagesObservePayload, TuiMessageObserverRuntime, } from './dsh-adapter/message-observer.js';
9
+ export type { LedgerEntry, LedgerOperation, LedgerResult, TuiEffectLedgerRuntime } from './dsh-adapter/effect-ledger.js';
10
+ export { COMMAND_ERROR_CODES, hasCommandErrorCode, mapCommandError, withCommandErrorMapping, } from './dsh-adapter/command-errors.js';
11
+ export type { CommandErrorCode, CodedCommandError } from './dsh-adapter/command-errors.js';
12
+ export { DECISION_EVENT_PERMISSIONS, } from './dsh-adapter/decision-guard.js';
13
+ export { DECISION_HANDLER_TIMEOUT_MS, DECISION_TOTAL_TIMEOUT_MS, } from './dsh-adapter/extension-events.js';
11
14
  export { DECISION_EVENTS_COORDINATE, TUI_EXTENSION_API_VERSION, TUI_DECISION_EVENT_NAMES, TUI_EXTENSION_PERMISSION_NAMES, createAdmissionCatalog, } from './plugin-spec/tui-extension.js';
12
- export type { ContractCoordinate, ContractRef, HostContract, HostDescriptor, NegotiationDecision, PermissionEntry, PermissionRegistry, } from './plugin-spec/types.js';
13
15
  //# sourceMappingURL=plugin-host.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../../src/plugin-host.ts"],"names":[],"mappings":"AAMA,cAAc,8BAA8B,CAAA;AAC5C,cAAc,yBAAyB,CAAA;AACvC,cAAc,kCAAkC,CAAA;AAChD,cAAc,iCAAiC,CAAA;AAC/C,cAAc,mCAAmC,CAAA;AACjD,cAAc,gCAAgC,CAAA;AAC9C,cAAc,iCAAiC,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,sCAAsC,CAAA;AACxH,OAAO,EACL,0BAA0B,EAC1B,kBAAkB,EAClB,uBAAuB,EACvB,wBAAwB,GACzB,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,EACzB,uBAAuB,GACxB,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,gCAAgC,CAAA;AAEvC,YAAY,EACV,kBAAkB,EAClB,WAAW,EACX,YAAY,EACZ,cAAc,EACd,mBAAmB,EACnB,eAAe,EACf,kBAAkB,GACnB,MAAM,wBAAwB,CAAA"}
1
+ {"version":3,"file":"plugin-host.d.ts","sourceRoot":"","sources":["../../src/plugin-host.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,8BAA8B,CAAA;AAC1D,YAAY,EAAE,aAAa,EAAE,MAAM,8BAA8B,CAAA;AACjE,YAAY,EAAE,cAAc,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzE,YAAY,EAAE,YAAY,EAAE,cAAc,EAAE,kBAAkB,EAAE,WAAW,EAAE,mBAAmB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AACrK,YAAY,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAA;AACxH,OAAO,EAAE,kBAAkB,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAA;AAIjI,OAAO,EACL,4BAA4B,EAC5B,2BAA2B,EAC3B,yBAAyB,EACzB,oBAAoB,EACpB,8BAA8B,EAC9B,uBAAuB,EACvB,uBAAuB,EACvB,qBAAqB,GACtB,MAAM,mCAAmC,CAAA;AAC1C,YAAY,EACV,2BAA2B,EAC3B,uBAAuB,EACvB,uBAAuB,EACvB,sBAAsB,EACtB,yBAAyB,GAC1B,MAAM,mCAAmC,CAAA;AAE1C,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,gCAAgC,CAAA;AACxH,OAAO,EACL,mBAAmB,EACnB,mBAAmB,EACnB,eAAe,EACf,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AACxC,YAAY,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,iCAAiC,CAAA;AAK1F,OAAO,EACL,0BAA0B,GAC3B,MAAM,iCAAiC,CAAA;AACxC,OAAO,EACL,2BAA2B,EAC3B,yBAAyB,GAC1B,MAAM,mCAAmC,CAAA;AAC1C,OAAO,EACL,0BAA0B,EAC1B,yBAAyB,EACzB,wBAAwB,EACxB,8BAA8B,EAC9B,sBAAsB,GACvB,MAAM,gCAAgC,CAAA"}