@linxiraos/pi-tui 1.1.26 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/CHANGELOG.md +51 -491
  2. package/README.md +13 -7
  3. package/dist/types/active-terminal.d.ts +30 -0
  4. package/dist/types/app-keybindings.d.ts +7 -2
  5. package/dist/types/apps/cleanse-board.d.ts +1 -1
  6. package/dist/types/apps/debug/log-viewer.d.ts +4 -0
  7. package/dist/types/apps/debug/raw-sse-buffer.d.ts +2 -0
  8. package/dist/types/apps/git/git-tui.d.ts +5 -4
  9. package/dist/types/apps/git/help.d.ts +3 -1
  10. package/dist/types/apps/if-bench-board.d.ts +2 -2
  11. package/dist/types/apps/setup-model-picker.d.ts +1 -1
  12. package/dist/types/autocomplete.d.ts +5 -2
  13. package/dist/types/charts/chart-plan.d.ts +674 -0
  14. package/dist/types/charts/chart-svg.d.ts +59 -0
  15. package/dist/types/charts/table-data.d.ts +175 -0
  16. package/dist/types/chat/assistant-message.d.ts +11 -1
  17. package/dist/types/chat/chat-transcript-builder.d.ts +3 -0
  18. package/dist/types/chat/compaction-summary-message.d.ts +1 -0
  19. package/dist/types/chat/execution-shared.d.ts +6 -5
  20. package/dist/types/chat/fence-figure.d.ts +42 -0
  21. package/dist/types/chat/figure-markdown.d.ts +42 -0
  22. package/dist/types/chat/messages.d.ts +1 -1
  23. package/dist/types/chat/svg-figure.d.ts +33 -0
  24. package/dist/types/chat/svg-source.d.ts +49 -0
  25. package/dist/types/chat/table-chart.d.ts +89 -0
  26. package/dist/types/chat/tool-execution.d.ts +2 -0
  27. package/dist/types/chat/user-message.d.ts +17 -4
  28. package/dist/types/chrome/shared.d.ts +9 -0
  29. package/dist/types/chrome/status-notice.d.ts +15 -3
  30. package/dist/types/chrome/transcript-container.d.ts +6 -0
  31. package/dist/types/components/box.d.ts +1 -0
  32. package/dist/types/components/disclosure.d.ts +1 -0
  33. package/dist/types/components/editor.d.ts +23 -5
  34. package/dist/types/components/form.d.ts +1 -0
  35. package/dist/types/components/image.d.ts +26 -44
  36. package/dist/types/components/input.d.ts +2 -1
  37. package/dist/types/components/key-value-list.d.ts +1 -0
  38. package/dist/types/components/layout/row.d.ts +1 -0
  39. package/dist/types/components/layout/stack.d.ts +1 -0
  40. package/dist/types/components/loader.d.ts +5 -6
  41. package/dist/types/components/markdown.d.ts +47 -5
  42. package/dist/types/components/scroll-view.d.ts +1 -0
  43. package/dist/types/components/section.d.ts +1 -0
  44. package/dist/types/components/table.d.ts +1 -0
  45. package/dist/types/components/text.d.ts +1 -0
  46. package/dist/types/components/tree-view.d.ts +7 -0
  47. package/dist/types/components/truncated-text.d.ts +1 -0
  48. package/dist/types/fuzzy.d.ts +29 -1
  49. package/dist/types/key-hint-format.d.ts +9 -1
  50. package/dist/types/keybinding-matchers.d.ts +3 -0
  51. package/dist/types/keybindings.d.ts +1 -1
  52. package/dist/types/kitty-graphics.d.ts +2 -2
  53. package/dist/types/loop-watchdog.d.ts +8 -7
  54. package/dist/types/multiplexers/cmux.d.ts +16 -0
  55. package/dist/types/multiplexers/herdr.d.ts +26 -0
  56. package/dist/types/multiplexers/notify.d.ts +13 -0
  57. package/dist/types/multiplexers/orca.d.ts +12 -0
  58. package/dist/types/multiplexers/screen.d.ts +9 -0
  59. package/dist/types/multiplexers/tmux.d.ts +29 -0
  60. package/dist/types/multiplexers/types.d.ts +100 -0
  61. package/dist/types/multiplexers/wmux.d.ts +12 -0
  62. package/dist/types/multiplexers/zellij.d.ts +12 -0
  63. package/dist/types/native/apply.d.ts +4 -3
  64. package/dist/types/native/backend.d.ts +5 -4
  65. package/dist/types/native/blobs.d.ts +7 -3
  66. package/dist/types/native/encode.d.ts +8 -6
  67. package/dist/types/native/node.d.ts +36 -5
  68. package/dist/types/native/overlay.d.ts +8 -3
  69. package/dist/types/native/settle.d.ts +4 -3
  70. package/dist/types/overlays/agent-hub-projection.d.ts +6 -5
  71. package/dist/types/overlays/agent-hub-types.d.ts +9 -0
  72. package/dist/types/overlays/agent-hub.d.ts +3 -0
  73. package/dist/types/overlays/agent-transcript-viewer.d.ts +12 -2
  74. package/dist/types/overlays/agents-hub.d.ts +9 -0
  75. package/dist/types/overlays/annotation-overlay.d.ts +19 -2
  76. package/dist/types/overlays/annotation-types.d.ts +2 -0
  77. package/dist/types/overlays/ask-dialog.d.ts +5 -2
  78. package/dist/types/overlays/btw-history-panel.d.ts +13 -1
  79. package/dist/types/overlays/copy-selector.d.ts +1 -0
  80. package/dist/types/overlays/history-search.d.ts +10 -0
  81. package/dist/types/overlays/hook-selector.d.ts +9 -4
  82. package/dist/types/overlays/hub-frame.d.ts +4 -1
  83. package/dist/types/overlays/jobs-panel.d.ts +1 -7
  84. package/dist/types/overlays/model-browser.d.ts +54 -7
  85. package/dist/types/overlays/model-hub.d.ts +37 -0
  86. package/dist/types/overlays/oauth-selector.d.ts +0 -1
  87. package/dist/types/overlays/plan-review-overlay.d.ts +3 -2
  88. package/dist/types/overlays/report-panel.d.ts +68 -0
  89. package/dist/types/overlays/rewind-selector.d.ts +1 -0
  90. package/dist/types/overlays/session-observer-registry.d.ts +2 -0
  91. package/dist/types/overlays/session-selector.d.ts +6 -2
  92. package/dist/types/overlays/settings-defs.d.ts +1 -0
  93. package/dist/types/overlays/thinking-selector.d.ts +2 -2
  94. package/dist/types/overlays/usage-dashboard.d.ts +12 -8
  95. package/dist/types/overlays/usage-display.d.ts +3 -1
  96. package/dist/types/prompt/composer-cache.d.ts +0 -16
  97. package/dist/types/prompt/composer.d.ts +18 -3
  98. package/dist/types/prompt/custom-editor.d.ts +25 -13
  99. package/dist/types/prompt/image-source.d.ts +2 -1
  100. package/dist/types/prompt/interactive-context-helpers.d.ts +5 -1
  101. package/dist/types/prompt/model-mention-autocomplete.d.ts +1 -1
  102. package/dist/types/prompt/welcome.d.ts +7 -2
  103. package/dist/types/render/composite.d.ts +2 -0
  104. package/dist/types/render/output-pane.d.ts +1 -0
  105. package/dist/types/render/render-utils.d.ts +1 -0
  106. package/dist/types/render/tool-card.d.ts +6 -0
  107. package/dist/types/render/width-aware-text.d.ts +2 -0
  108. package/dist/types/setup/scenes/types.d.ts +1 -0
  109. package/dist/types/space-hold.d.ts +40 -25
  110. package/dist/types/status-line/component.d.ts +3 -0
  111. package/dist/types/status-line/context-usage.d.ts +7 -5
  112. package/dist/types/status-line/host.d.ts +4 -1
  113. package/dist/types/status-line/segments.d.ts +2 -0
  114. package/dist/types/status-line/types.d.ts +14 -3
  115. package/dist/types/terminal-capabilities.d.ts +41 -36
  116. package/dist/types/terminal-multiplexer.d.ts +127 -14
  117. package/dist/types/terminal.d.ts +23 -16
  118. package/dist/types/theme/active-symbols.d.ts +1 -1
  119. package/dist/types/theme/schema.d.ts +11 -2
  120. package/dist/types/theme/symbols.d.ts +1 -1
  121. package/dist/types/theme/theme-class.d.ts +7 -0
  122. package/dist/types/theme/theme.d.ts +1 -1
  123. package/dist/types/theme/tui-adapters.d.ts +3 -0
  124. package/dist/types/thinking.d.ts +1 -1
  125. package/dist/types/tmux.d.ts +11 -4
  126. package/dist/types/tools/bash.d.ts +7 -2
  127. package/dist/types/tools/daemon.d.ts +1 -1
  128. package/dist/types/tools/eval.d.ts +22 -3
  129. package/dist/types/tools/glob.d.ts +4 -0
  130. package/dist/types/tools/native-view.d.ts +47 -0
  131. package/dist/types/tools/output-meta.d.ts +11 -1
  132. package/dist/types/tools/renderer.d.ts +37 -2
  133. package/dist/types/tools/streaming-output.d.ts +20 -1
  134. package/dist/types/tools/task.d.ts +2 -3
  135. package/dist/types/tools/write.d.ts +9 -2
  136. package/dist/types/tools/yield.d.ts +22 -0
  137. package/dist/types/tui.d.ts +36 -0
  138. package/dist/types/utils.d.ts +6 -0
  139. package/dist/types/vim.d.ts +19 -5
  140. package/dist/types/windows-input-mode.d.ts +33 -1
  141. package/package.json +13 -9
  142. package/src/active-terminal.d.ts +30 -0
  143. package/src/active-terminal.ts +80 -0
  144. package/src/app-keybindings.d.ts +7 -2
  145. package/src/app-keybindings.ts +7 -2
  146. package/src/apps/autoresearch-dashboard.ts +7 -3
  147. package/src/apps/cleanse-board.ts +2 -2
  148. package/src/apps/cleanse-picker.ts +1 -1
  149. package/src/apps/debug/log-viewer.ts +188 -72
  150. package/src/apps/debug/raw-sse-buffer.ts +8 -0
  151. package/src/apps/debug/raw-sse.ts +39 -5
  152. package/src/apps/git/diff-pane.ts +96 -23
  153. package/src/apps/git/git-tui.ts +23 -16
  154. package/src/apps/git/help.ts +29 -20
  155. package/src/apps/git/sidebar.ts +8 -3
  156. package/src/apps/if-bench-board.ts +2 -2
  157. package/src/apps/ps-top.ts +2 -2
  158. package/src/apps/setup-model-picker.ts +1 -1
  159. package/src/autocomplete.d.ts +5 -2
  160. package/src/autocomplete.ts +67 -43
  161. package/src/bracketed-paste.ts +10 -3
  162. package/src/charts/chart-plan.d.ts +674 -0
  163. package/src/charts/chart-plan.ts +5081 -0
  164. package/src/charts/chart-svg.d.ts +59 -0
  165. package/src/charts/chart-svg.ts +4657 -0
  166. package/src/charts/table-data.d.ts +175 -0
  167. package/src/charts/table-data.ts +1478 -0
  168. package/src/chat/advisor-message.ts +11 -0
  169. package/src/chat/assistant-message.d.ts +11 -1
  170. package/src/chat/assistant-message.ts +245 -54
  171. package/src/chat/chat-transcript-builder.d.ts +3 -0
  172. package/src/chat/chat-transcript-builder.ts +9 -3
  173. package/src/chat/compaction-summary-message.d.ts +1 -0
  174. package/src/chat/compaction-summary-message.ts +8 -0
  175. package/src/chat/execution-shared.d.ts +6 -5
  176. package/src/chat/execution-shared.ts +42 -25
  177. package/src/chat/fence-figure.d.ts +42 -0
  178. package/src/chat/fence-figure.ts +80 -0
  179. package/src/chat/figure-markdown.d.ts +42 -0
  180. package/src/chat/figure-markdown.ts +174 -0
  181. package/src/chat/messages.d.ts +1 -1
  182. package/src/chat/messages.ts +1 -1
  183. package/src/chat/read-tool-group.ts +6 -4
  184. package/src/chat/skill-message.ts +6 -0
  185. package/src/chat/svg-figure.d.ts +33 -0
  186. package/src/chat/svg-figure.ts +399 -0
  187. package/src/chat/svg-source.d.ts +49 -0
  188. package/src/chat/svg-source.ts +188 -0
  189. package/src/chat/table-chart.d.ts +89 -0
  190. package/src/chat/table-chart.ts +294 -0
  191. package/src/chat/tool-execution.d.ts +2 -0
  192. package/src/chat/tool-execution.ts +86 -7
  193. package/src/chat/user-message.d.ts +17 -4
  194. package/src/chat/user-message.ts +71 -8
  195. package/src/chrome/shared.ts +53 -0
  196. package/src/chrome/status-notice.ts +25 -7
  197. package/src/chrome/transcript-container.d.ts +6 -0
  198. package/src/chrome/transcript-container.ts +162 -19
  199. package/src/components/box.d.ts +1 -0
  200. package/src/components/box.ts +5 -0
  201. package/src/components/disclosure.d.ts +1 -0
  202. package/src/components/disclosure.ts +8 -0
  203. package/src/components/editor.d.ts +23 -5
  204. package/src/components/editor.ts +316 -47
  205. package/src/components/form.d.ts +1 -0
  206. package/src/components/form.ts +8 -0
  207. package/src/components/image.d.ts +26 -44
  208. package/src/components/image.ts +115 -152
  209. package/src/components/input.d.ts +2 -1
  210. package/src/components/input.ts +17 -7
  211. package/src/components/key-value-list.d.ts +1 -0
  212. package/src/components/key-value-list.ts +4 -0
  213. package/src/components/layout/row.d.ts +1 -0
  214. package/src/components/layout/row.ts +7 -0
  215. package/src/components/layout/stack.d.ts +1 -0
  216. package/src/components/layout/stack.ts +7 -0
  217. package/src/components/loader.d.ts +5 -6
  218. package/src/components/loader.ts +10 -17
  219. package/src/components/markdown.d.ts +47 -5
  220. package/src/components/markdown.ts +786 -303
  221. package/src/components/scroll-view.d.ts +1 -0
  222. package/src/components/scroll-view.ts +5 -0
  223. package/src/components/section.d.ts +1 -0
  224. package/src/components/section.ts +5 -0
  225. package/src/components/settings-list.ts +2 -0
  226. package/src/components/table.d.ts +1 -0
  227. package/src/components/table.ts +4 -0
  228. package/src/components/text.d.ts +1 -0
  229. package/src/components/text.ts +7 -0
  230. package/src/components/tree-view.d.ts +7 -0
  231. package/src/components/tree-view.ts +38 -8
  232. package/src/components/truncated-text.d.ts +1 -0
  233. package/src/components/truncated-text.ts +5 -0
  234. package/src/desktop-notify.ts +3 -2
  235. package/src/fuzzy.d.ts +29 -1
  236. package/src/fuzzy.ts +115 -30
  237. package/src/hotkeys-markdown.ts +9 -2
  238. package/src/key-hint-format.d.ts +9 -1
  239. package/src/key-hint-format.ts +26 -11
  240. package/src/keybinding-matchers.d.ts +3 -0
  241. package/src/keybinding-matchers.ts +6 -2
  242. package/src/keybindings.d.ts +1 -1
  243. package/src/keybindings.ts +1 -1
  244. package/src/kitty-graphics.d.ts +2 -2
  245. package/src/kitty-graphics.ts +11 -5
  246. package/src/loop-watchdog.d.ts +8 -7
  247. package/src/loop-watchdog.ts +18 -13
  248. package/src/multiplexers/cmux.d.ts +16 -0
  249. package/src/multiplexers/cmux.ts +29 -0
  250. package/src/multiplexers/herdr.d.ts +26 -0
  251. package/src/multiplexers/herdr.ts +64 -0
  252. package/src/multiplexers/notify.d.ts +13 -0
  253. package/src/multiplexers/notify.ts +24 -0
  254. package/src/multiplexers/orca.d.ts +12 -0
  255. package/src/multiplexers/orca.ts +16 -0
  256. package/src/multiplexers/screen.d.ts +9 -0
  257. package/src/multiplexers/screen.ts +15 -0
  258. package/src/multiplexers/tmux.d.ts +29 -0
  259. package/src/multiplexers/tmux.ts +40 -0
  260. package/src/multiplexers/types.d.ts +100 -0
  261. package/src/multiplexers/types.ts +110 -0
  262. package/src/multiplexers/wmux.d.ts +12 -0
  263. package/src/multiplexers/wmux.ts +16 -0
  264. package/src/multiplexers/zellij.d.ts +12 -0
  265. package/src/multiplexers/zellij.ts +24 -0
  266. package/src/native/apply.d.ts +4 -3
  267. package/src/native/apply.ts +4 -3
  268. package/src/native/backend.d.ts +5 -4
  269. package/src/native/backend.ts +199 -22
  270. package/src/native/blobs.d.ts +7 -3
  271. package/src/native/blobs.ts +47 -12
  272. package/src/native/encode.d.ts +8 -6
  273. package/src/native/encode.ts +76 -34
  274. package/src/native/node.d.ts +36 -5
  275. package/src/native/node.ts +31 -5
  276. package/src/native/overlay.d.ts +8 -3
  277. package/src/native/overlay.ts +17 -4
  278. package/src/native/reconcile.ts +16 -4
  279. package/src/native/settle.d.ts +4 -3
  280. package/src/native/settle.ts +4 -3
  281. package/src/overlays/agent-hub-projection.d.ts +6 -5
  282. package/src/overlays/agent-hub-projection.ts +124 -13
  283. package/src/overlays/agent-hub-types.d.ts +9 -0
  284. package/src/overlays/agent-hub-types.ts +6 -0
  285. package/src/overlays/agent-hub.ts +48 -29
  286. package/src/overlays/agent-transcript-viewer.ts +93 -23
  287. package/src/overlays/agents-hub.ts +28 -8
  288. package/src/overlays/annotation-overlay.ts +293 -90
  289. package/src/overlays/annotation-types.ts +2 -0
  290. package/src/overlays/ask-dialog.ts +10 -17
  291. package/src/overlays/btw-history-panel.ts +136 -39
  292. package/src/overlays/copy-selector.d.ts +1 -0
  293. package/src/overlays/copy-selector.ts +32 -8
  294. package/src/overlays/extensions/extension-dashboard.ts +4 -4
  295. package/src/overlays/extensions/state-manager.ts +52 -12
  296. package/src/overlays/history-search.ts +100 -7
  297. package/src/overlays/hook-selector.ts +12 -5
  298. package/src/overlays/hub-frame.ts +19 -1
  299. package/src/overlays/jobs-panel.ts +33 -52
  300. package/src/overlays/model-browser.ts +367 -122
  301. package/src/overlays/model-hub.ts +435 -59
  302. package/src/overlays/move-overlay.ts +28 -14
  303. package/src/overlays/oauth-selector.ts +54 -70
  304. package/src/overlays/pause-screen.ts +2 -2
  305. package/src/overlays/plan-review-overlay.ts +208 -70
  306. package/src/overlays/plugin-settings.ts +3 -3
  307. package/src/overlays/report-panel.ts +191 -0
  308. package/src/overlays/rewind-selector.ts +88 -17
  309. package/src/overlays/session-observer-registry.d.ts +2 -0
  310. package/src/overlays/session-observer-registry.ts +10 -2
  311. package/src/overlays/session-selector.ts +64 -26
  312. package/src/overlays/settings-defs.ts +5 -4
  313. package/src/overlays/settings-selector.ts +33 -8
  314. package/src/overlays/snapcompact-shape-preview.ts +2 -3
  315. package/src/overlays/stats-notice.ts +1 -1
  316. package/src/overlays/thinking-selector.ts +11 -10
  317. package/src/overlays/tree-selector.ts +41 -20
  318. package/src/overlays/usage-dashboard.ts +117 -58
  319. package/src/overlays/usage-display.ts +10 -4
  320. package/src/overlays/usage-row.ts +22 -8
  321. package/src/prompt/attachment-chips.ts +15 -11
  322. package/src/prompt/composer-attachments.ts +6 -4
  323. package/src/prompt/composer-cache.ts +7 -86
  324. package/src/prompt/composer.ts +37 -26
  325. package/src/prompt/custom-editor.ts +282 -105
  326. package/src/prompt/image-source.ts +2 -1
  327. package/src/prompt/interactive-context-helpers.ts +32 -18
  328. package/src/prompt/macos-spelling.ts +7 -0
  329. package/src/prompt/magic-keywords.ts +11 -2
  330. package/src/prompt/model-mention-autocomplete.ts +14 -14
  331. package/src/prompt/queued-messages.ts +6 -4
  332. package/src/prompt/tips.txt +13 -9
  333. package/src/prompt/welcome.ts +64 -46
  334. package/src/prompt/word-completion.ts +38 -5
  335. package/src/render/composite.d.ts +2 -0
  336. package/src/render/composite.ts +64 -0
  337. package/src/render/output-pane.d.ts +1 -0
  338. package/src/render/output-pane.ts +6 -0
  339. package/src/render/render-utils.d.ts +1 -0
  340. package/src/render/render-utils.ts +16 -1
  341. package/src/render/tool-card.d.ts +6 -0
  342. package/src/render/tool-card.ts +15 -0
  343. package/src/render/width-aware-text.d.ts +2 -0
  344. package/src/render/width-aware-text.ts +5 -0
  345. package/src/setup/scenes/model.ts +2 -1
  346. package/src/setup/scenes/types.ts +1 -0
  347. package/src/space-hold.d.ts +40 -25
  348. package/src/space-hold.ts +156 -52
  349. package/src/status-line/component.ts +292 -78
  350. package/src/status-line/context-usage.ts +21 -46
  351. package/src/status-line/host.ts +4 -1
  352. package/src/status-line/segments.ts +40 -22
  353. package/src/status-line/types.ts +15 -3
  354. package/src/terminal-capabilities.d.ts +41 -36
  355. package/src/terminal-capabilities.ts +137 -213
  356. package/src/terminal-multiplexer.d.ts +127 -14
  357. package/src/terminal-multiplexer.ts +112 -33
  358. package/src/terminal.d.ts +23 -16
  359. package/src/terminal.ts +203 -99
  360. package/src/theme/active-symbols.d.ts +1 -1
  361. package/src/theme/active-symbols.ts +1 -1
  362. package/src/theme/schema-validation.ts +1 -0
  363. package/src/theme/schema.d.ts +11 -2
  364. package/src/theme/schema.ts +14 -1
  365. package/src/theme/session-color.ts +1 -7
  366. package/src/theme/symbols.d.ts +1 -1
  367. package/src/theme/symbols.ts +11 -4
  368. package/src/theme/theme-class.d.ts +7 -0
  369. package/src/theme/theme-class.ts +26 -1
  370. package/src/theme/theme-schema.json +5 -1
  371. package/src/theme/theme.d.ts +1 -1
  372. package/src/theme/theme.ts +8 -4
  373. package/src/theme/tui-adapters.d.ts +3 -0
  374. package/src/theme/tui-adapters.ts +68 -0
  375. package/src/thinking.d.ts +1 -1
  376. package/src/thinking.ts +1 -1
  377. package/src/tmux.d.ts +11 -4
  378. package/src/tmux.ts +47 -11
  379. package/src/tools/ast-edit.ts +59 -35
  380. package/src/tools/ast-grep.ts +35 -16
  381. package/src/tools/bash.d.ts +7 -2
  382. package/src/tools/bash.ts +116 -47
  383. package/src/tools/daemon.d.ts +1 -1
  384. package/src/tools/daemon.ts +1 -1
  385. package/src/tools/default-renderer.ts +35 -19
  386. package/src/tools/edit.ts +142 -120
  387. package/src/tools/eval-format/index.ts +28 -2
  388. package/src/tools/eval-format/javascript.ts +7 -5
  389. package/src/tools/eval.d.ts +22 -3
  390. package/src/tools/eval.ts +364 -208
  391. package/src/tools/fetch.ts +5 -6
  392. package/src/tools/glob.d.ts +4 -0
  393. package/src/tools/glob.ts +33 -65
  394. package/src/tools/grep.ts +81 -48
  395. package/src/tools/index.ts +2 -0
  396. package/src/tools/lsp.ts +81 -59
  397. package/src/tools/mcp.ts +111 -68
  398. package/src/tools/native-view.d.ts +47 -0
  399. package/src/tools/native-view.ts +102 -1
  400. package/src/tools/output-meta.d.ts +11 -1
  401. package/src/tools/output-meta.ts +23 -3
  402. package/src/tools/read.ts +7 -5
  403. package/src/tools/renderer.d.ts +37 -2
  404. package/src/tools/renderer.ts +42 -2
  405. package/src/tools/streaming-output.d.ts +20 -1
  406. package/src/tools/streaming-output.ts +154 -50
  407. package/src/tools/task.d.ts +2 -3
  408. package/src/tools/task.ts +115 -62
  409. package/src/tools/todo.ts +119 -30
  410. package/src/tools/wait.ts +34 -24
  411. package/src/tools/web-search.ts +1 -1
  412. package/src/tools/write.d.ts +9 -2
  413. package/src/tools/write.ts +147 -95
  414. package/src/tools/yield.d.ts +22 -0
  415. package/src/tools/yield.ts +225 -0
  416. package/src/tui.d.ts +36 -0
  417. package/src/tui.ts +666 -217
  418. package/src/utils.d.ts +6 -0
  419. package/src/utils.ts +19 -0
  420. package/src/vim.d.ts +19 -5
  421. package/src/vim.ts +333 -18
  422. package/src/windows-input-mode.d.ts +33 -1
  423. package/src/windows-input-mode.ts +212 -6
@@ -53,11 +53,15 @@ export function matchesSelectPageDown(data: string): boolean {
53
53
  return getKeybindings().matches(data, "tui.select.pageDown");
54
54
  }
55
55
 
56
+ /** Effective external-editor key: the first bound key, or Ctrl+G when unbound. */
57
+ export function appExternalEditorKey(): KeyId {
58
+ return getKeybindings().getKeys("app.editor.external")[0] ?? "ctrl+g";
59
+ }
60
+
56
61
  /** Match the external-editor keybinding, falling back to Ctrl+G when unbound. */
57
62
  export function matchesAppExternalEditor(data: string): boolean {
58
63
  const keybindings = getKeybindings();
59
- const externalEditorKeys = keybindings.getKeys("app.editor.external");
60
- if (externalEditorKeys.length > 0) {
64
+ if (keybindings.getKeys("app.editor.external").length > 0) {
61
65
  return keybindings.matches(data, "app.editor.external");
62
66
  }
63
67
  return matchesKey(data, "ctrl+g");
@@ -107,7 +107,7 @@ export declare const TUI_KEYBINDINGS: {
107
107
  readonly description: "Delete word backward";
108
108
  };
109
109
  readonly "tui.editor.deleteWordForward": {
110
- readonly defaultKeys: ["alt+delete", "alt+d", "super+alt+delete", "super+alt+d"];
110
+ readonly defaultKeys: ["alt+delete", "alt+d", "ctrl+delete", "super+alt+delete", "super+alt+d"];
111
111
  readonly description: "Delete word forward";
112
112
  };
113
113
  readonly "tui.editor.deleteToLineStart": {
@@ -105,7 +105,7 @@ export const TUI_KEYBINDINGS = {
105
105
  description: "Delete word backward",
106
106
  },
107
107
  "tui.editor.deleteWordForward": {
108
- defaultKeys: ["alt+delete", "alt+d", "super+alt+delete", "super+alt+d"],
108
+ defaultKeys: ["alt+delete", "alt+d", "ctrl+delete", "super+alt+delete", "super+alt+d"],
109
109
  description: "Delete word forward",
110
110
  },
111
111
  "tui.editor.deleteToLineStart": {
@@ -30,8 +30,8 @@ export interface KittyGraphicsFeatures {
30
30
  * cannot use cursor-positioned placements because the outer terminal does not
31
31
  * know pane scroll/reflow state. An explicit `PI_FORCE_IMAGE_PROTOCOL=kitty`
32
32
  * opts into placeholders under any multiplexer — matching `timg -pk`.
33
- * Automatic Herdr fallback stays off because its pane marker does not prove
34
- * that the experimental Kitty renderer is enabled.
33
+ * Automatic fallback stays off inside multiplexers whose pane marker does not
34
+ * prove that a Kitty renderer is enabled (Herdr's is experimental).
35
35
  *
36
36
  * `PI_NO_KITTY_PLACEHOLDERS=1` and `PI_KITTY_PLACEHOLDERS=0` remain hard
37
37
  * opt-outs; `PI_KITTY_PLACEHOLDERS=1` explicitly opts in anywhere else.
@@ -15,7 +15,7 @@
15
15
  * forms. Protocol gating (`imageProtocol === Kitty`) lives in the caller.
16
16
  */
17
17
 
18
- import { isInsideHerdr, isInsideTerminalMultiplexer } from "./terminal-multiplexer";
18
+ import { isInsideTerminalMultiplexer, terminalMultiplexerSessions } from "./terminal-multiplexer";
19
19
  import { wrapTmuxPassthroughIfNeeded } from "./tmux";
20
20
 
21
21
  /** Kitty Unicode placeholder base character (U+10EEEE, Plane 16 PUA). */
@@ -67,8 +67,8 @@ export interface KittyGraphicsFeatures {
67
67
  * cannot use cursor-positioned placements because the outer terminal does not
68
68
  * know pane scroll/reflow state. An explicit `PI_FORCE_IMAGE_PROTOCOL=kitty`
69
69
  * opts into placeholders under any multiplexer — matching `timg -pk`.
70
- * Automatic Herdr fallback stays off because its pane marker does not prove
71
- * that the experimental Kitty renderer is enabled.
70
+ * Automatic fallback stays off inside multiplexers whose pane marker does not
71
+ * prove that a Kitty renderer is enabled (Herdr's is experimental).
72
72
  *
73
73
  * `PI_NO_KITTY_PLACEHOLDERS=1` and `PI_KITTY_PLACEHOLDERS=0` remain hard
74
74
  * opt-outs; `PI_KITTY_PLACEHOLDERS=1` explicitly opts in anywhere else.
@@ -81,8 +81,14 @@ export function detectKittyUnicodePlaceholdersSupport(terminalId: string, env: N
81
81
  if (force === "0" || force === "false" || force === "off" || force === "no" || force === "n") return false;
82
82
  const insideMultiplexer = isInsideTerminalMultiplexer(env);
83
83
  if (insideMultiplexer && env.PI_FORCE_IMAGE_PROTOCOL?.trim().toLowerCase() === "kitty") return true;
84
- if (isInsideHerdr(env)) return false;
85
- return terminalId === "kitty" || terminalId === "ghostty" || terminalId === "otty" || terminalId === "rio";
84
+ if (terminalMultiplexerSessions(env).some(multiplexer => multiplexer.imagesRequireOverride)) return false;
85
+ return (
86
+ terminalId === "kitty" ||
87
+ terminalId === "ghostty" ||
88
+ terminalId === "monstar" ||
89
+ terminalId === "otty" ||
90
+ terminalId === "rio"
91
+ );
86
92
  }
87
93
 
88
94
  let features: KittyGraphicsFeatures = {
@@ -21,13 +21,14 @@ interface LoopWatchdogTimer {
21
21
  cancel?(): void;
22
22
  }
23
23
  /**
24
- * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead of
25
- * a recorded deadline; a tick that fires `thresholdMs` past its deadline means
26
- * the loop was blocked that long. The overshoot is logged once on the rising
27
- * edge (one block ⇒ one line, deduped via `#wasBlocked`), tagged with the phase
28
- * active during the elapsed interval via {@link takeRecentLoopPhase} — which
29
- * survives the synchronous push/pop the instrumented hot paths do before this
30
- * delayed tick can run — so the stall names its cause instead of "unknown".
24
+ * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead,
25
+ * with that target recorded as its deadline; a tick that fires `thresholdMs`
26
+ * past its deadline means the loop was blocked that long. The overshoot is
27
+ * logged once on the rising edge (one block ⇒ one line, deduped via
28
+ * `#wasBlocked`). Attribution via
29
+ * {@link takeLoopPhaseAttribution} totals each innermost phase only after the tick's
30
+ * deadline. A phase is named, with `phaseMs`, only when its total outweighs all
31
+ * unlabeled time in that late window; otherwise the block remains "unknown".
31
32
  *
32
33
  * The handle is `unref`'d so the probe never keeps the process alive, and stop()
33
34
  * cancels the armed timer when the handle exposes `cancel` (the default
@@ -1,5 +1,5 @@
1
1
  import { performance } from "node:perf_hooks";
2
- import { logger, takeRecentLoopPhase } from "@linxiraos/pi-utils";
2
+ import { logger, resetLoopPhaseWindow, takeLoopPhaseAttribution } from "@linxiraos/pi-utils";
3
3
 
4
4
  export interface LoopWatchdogOptions {
5
5
  /** How far ahead each probe tick is scheduled, in ms. Default 250. */
@@ -35,13 +35,14 @@ interface LoopWatchdogTimer {
35
35
  const CPU_BUSY_RATIO = 0.01;
36
36
 
37
37
  /**
38
- * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead of
39
- * a recorded deadline; a tick that fires `thresholdMs` past its deadline means
40
- * the loop was blocked that long. The overshoot is logged once on the rising
41
- * edge (one block ⇒ one line, deduped via `#wasBlocked`), tagged with the phase
42
- * active during the elapsed interval via {@link takeRecentLoopPhase} — which
43
- * survives the synchronous push/pop the instrumented hot paths do before this
44
- * delayed tick can run — so the stall names its cause instead of "unknown".
38
+ * Always-on event-loop lag probe. Each tick is scheduled `intervalMs` ahead,
39
+ * with that target recorded as its deadline; a tick that fires `thresholdMs`
40
+ * past its deadline means the loop was blocked that long. The overshoot is
41
+ * logged once on the rising edge (one block ⇒ one line, deduped via
42
+ * `#wasBlocked`). Attribution via
43
+ * {@link takeLoopPhaseAttribution} totals each innermost phase only after the tick's
44
+ * deadline. A phase is named, with `phaseMs`, only when its total outweighs all
45
+ * unlabeled time in that late window; otherwise the block remains "unknown".
45
46
  *
46
47
  * The handle is `unref`'d so the probe never keeps the process alive, and stop()
47
48
  * cancels the armed timer when the handle exposes `cancel` (the default
@@ -101,12 +102,15 @@ export class LoopWatchdog {
101
102
  }
102
103
 
103
104
  stop(): void {
105
+ // Attribution state is process-global: a watchdog that is not running must not disarm another's window.
106
+ if (!this.#running) return;
104
107
  this.#running = false;
105
108
  this.#wasBlocked = false;
106
109
  this.#stallEndedAt = Number.NEGATIVE_INFINITY;
107
110
  this.#generation++;
108
111
  this.#handle?.cancel?.();
109
112
  this.#handle = undefined;
113
+ resetLoopPhaseWindow();
110
114
  }
111
115
 
112
116
  /**
@@ -133,6 +137,7 @@ export class LoopWatchdog {
133
137
  #armTick(): void {
134
138
  const generation = this.#generation;
135
139
  this.#expected = this.#now() + this.#intervalMs;
140
+ resetLoopPhaseWindow(this.#expected, this.#now);
136
141
  this.#expectedCpu = this.#cpuNow();
137
142
  this.#handle = this.#schedule(() => this.#tick(generation), this.#intervalMs);
138
143
  this.#handle.unref?.();
@@ -143,10 +148,9 @@ export class LoopWatchdog {
143
148
  const now = this.#now();
144
149
  const blockedMs = now - this.#expected;
145
150
  const cpuMs = this.#cpuNow() - this.#expectedCpu;
146
- // Consume the recent phase every tick (block or not) so attribution is
147
- // scoped to the just-elapsed interval and never carries a stale phase
148
- // forward to a later, phase-less block.
149
- const phase = takeRecentLoopPhase();
151
+ // Consume every valid tick, even without a warning, so no phase carries
152
+ // forward into a later, phase-less block.
153
+ const phase = takeLoopPhaseAttribution(now);
150
154
  if (blockedMs > this.#thresholdMs) {
151
155
  if (this.#isSuspension(blockedMs, cpuMs)) {
152
156
  this.#wasBlocked = false;
@@ -157,7 +161,8 @@ export class LoopWatchdog {
157
161
  logger.warn("ui.loop-blocked", {
158
162
  blockedMs: Math.round(blockedMs),
159
163
  cpuMs: Math.round(cpuMs),
160
- phase: phase ?? "unknown",
164
+ phase: phase?.label ?? "unknown",
165
+ ...(phase ? { phaseMs: Math.round(phase.ms) } : {}),
161
166
  });
162
167
  }
163
168
  }
@@ -0,0 +1,16 @@
1
+ export declare const cmuxMultiplexer: {
2
+ readonly id: "cmux";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["CMUX_WORKSPACE_ID", "CMUX_SURFACE_ID", "CMUX_REMOTE_TRANSPORT"];
5
+ readonly ownsScreenGrid: true;
6
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
7
+ /**
8
+ * Workspace/socket state alone is not enough: only the injected surface UUID
9
+ * identifies the pane that should receive the notification. Without a valid
10
+ * surface, or without the binary, delivery falls through unchanged.
11
+ */
12
+ readonly notifier: {
13
+ readonly tier: "surface";
14
+ readonly send: ({ message, env }: import("./types").TerminalMultiplexerNotificationRequest) => boolean;
15
+ };
16
+ };
@@ -0,0 +1,29 @@
1
+ import { notificationTitleAndBody, spawnNotifier } from "./notify";
2
+ import type { TerminalMultiplexerModule } from "./types";
3
+
4
+ const CMUX_SURFACE_ID_PATTERN = /^[0-9a-f]{8}-(?:[0-9a-f]{4}-){3}[0-9a-f]{12}$/iu;
5
+
6
+ export const cmuxMultiplexer = {
7
+ id: "cmux",
8
+ precedence: "session",
9
+ // CMUX_SOCKET_PATH is a CLI socket override and can be set outside a CMUX terminal.
10
+ sessionEnvKeys: ["CMUX_WORKSPACE_ID", "CMUX_SURFACE_ID", "CMUX_REMOTE_TRANSPORT"],
11
+ ownsScreenGrid: true,
12
+ isInside(env: NodeJS.ProcessEnv = Bun.env): boolean {
13
+ return Boolean(env.CMUX_WORKSPACE_ID || env.CMUX_SURFACE_ID || env.CMUX_REMOTE_TRANSPORT);
14
+ },
15
+ /**
16
+ * Workspace/socket state alone is not enough: only the injected surface UUID
17
+ * identifies the pane that should receive the notification. Without a valid
18
+ * surface, or without the binary, delivery falls through unchanged.
19
+ */
20
+ notifier: {
21
+ tier: "surface",
22
+ send({ message, env }) {
23
+ const surfaceId = env.CMUX_SURFACE_ID?.trim();
24
+ if (!surfaceId || !CMUX_SURFACE_ID_PATTERN.test(surfaceId)) return false;
25
+ const { title, body } = notificationTitleAndBody(message);
26
+ return spawnNotifier(["cmux", "notify", "--surface", surfaceId, "--title", title, "--body", body]);
27
+ },
28
+ },
29
+ } as const satisfies TerminalMultiplexerModule;
@@ -0,0 +1,26 @@
1
+ export declare const herdrMultiplexer: {
2
+ readonly id: "herdr";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["HERDR_ENV", "HERDR_PANE_ID", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID"];
5
+ readonly ownsScreenGrid: true;
6
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
7
+ /**
8
+ * Herdr multiplexes panes like tmux but swallows bare OSC 9 / OSC 99 and has
9
+ * no DCS passthrough envelope, and its bell relay does not flag a
10
+ * backgrounded tab — so without its CLI a backgrounded pane gets no signal
11
+ * at all that the agent finished or is waiting for input.
12
+ *
13
+ * `sound` maps the notification kind onto what Herdr offers: a question
14
+ * waiting on the user and a turn that stopped with an error both need the
15
+ * human and ring `request`, a settled turn rings `done`, anything else stays
16
+ * silent. The exact pane ID addresses the CLI call; without a valid one, or
17
+ * without the binary, delivery falls through.
18
+ */
19
+ readonly notifier: {
20
+ readonly tier: "pane";
21
+ readonly send: ({ message, env }: import("./types").TerminalMultiplexerNotificationRequest) => boolean;
22
+ };
23
+ readonly hyperlinks: "render";
24
+ readonly honorsSynchronizedOutput: true;
25
+ readonly imagesRequireOverride: true;
26
+ };
@@ -0,0 +1,64 @@
1
+ import { DEFAULT_NOTIFICATION_TITLE, notificationTitleAndBody, spawnNotifier } from "./notify";
2
+ import type { TerminalMultiplexerModule } from "./types";
3
+
4
+ const HERDR_PANE_ID_PATTERN = /^[0-9A-Za-z:_-]{1,64}$/u;
5
+ /**
6
+ * `herdr notification show` takes the title as its first positional and reads
7
+ * exactly these three values there as a help request; it has no `--`
8
+ * terminator. Any other text, including one starting with `-`, is a title.
9
+ */
10
+ const HERDR_USAGE_TOKENS: Record<string, true> = { help: true, "--help": true, "-h": true };
11
+
12
+ export const herdrMultiplexer = {
13
+ id: "herdr",
14
+ precedence: "session",
15
+ sessionEnvKeys: ["HERDR_ENV", "HERDR_PANE_ID", "HERDR_TAB_ID", "HERDR_WORKSPACE_ID"],
16
+ ownsScreenGrid: true,
17
+ isInside(env: NodeJS.ProcessEnv = Bun.env): boolean {
18
+ // Identity vars survive env-sanitizing launchers that drop HERDR_ENV.
19
+ // Client-only socket, binary, session, and config overrides are not proof
20
+ // that this process runs inside a pane.
21
+ return env.HERDR_ENV === "1" || Boolean(env.HERDR_PANE_ID || env.HERDR_TAB_ID || env.HERDR_WORKSPACE_ID);
22
+ },
23
+ /**
24
+ * Herdr multiplexes panes like tmux but swallows bare OSC 9 / OSC 99 and has
25
+ * no DCS passthrough envelope, and its bell relay does not flag a
26
+ * backgrounded tab — so without its CLI a backgrounded pane gets no signal
27
+ * at all that the agent finished or is waiting for input.
28
+ *
29
+ * `sound` maps the notification kind onto what Herdr offers: a question
30
+ * waiting on the user and a turn that stopped with an error both need the
31
+ * human and ring `request`, a settled turn rings `done`, anything else stays
32
+ * silent. The exact pane ID addresses the CLI call; without a valid one, or
33
+ * without the binary, delivery falls through.
34
+ */
35
+ notifier: {
36
+ tier: "pane",
37
+ send({ message, env }) {
38
+ const paneId = env.HERDR_PANE_ID?.trim();
39
+ if (!paneId || !HERDR_PANE_ID_PATTERN.test(paneId)) return false;
40
+
41
+ const parsed = notificationTitleAndBody(message);
42
+ const title = Object.hasOwn(HERDR_USAGE_TOKENS, parsed.title) ? DEFAULT_NOTIFICATION_TITLE : parsed.title;
43
+ const kinds = typeof message === "string" ? [] : [message.type ?? []].flat();
44
+ const sound =
45
+ kinds.includes("ask") || kinds.includes("error")
46
+ ? "request"
47
+ : kinds.includes("completion")
48
+ ? "done"
49
+ : "none";
50
+ return spawnNotifier(["herdr", "notification", "show", title, "--body", parsed.body, "--sound", sound]);
51
+ },
52
+ },
53
+ // Herdr hides the outer terminal (`TERM=xterm-256color`, no `TERM_PROGRAM`),
54
+ // but renders OSC 8 in its own grid and opens links itself on Ctrl+click.
55
+ hyperlinks: "render",
56
+ // The pane VTE is libghostty and suppresses compositing while DEC 2026 is
57
+ // set. Without sync, CUP-diff paints and split write(2) chunks composite as
58
+ // dirty-row patches: the live viewport tears, its top frozen while only the
59
+ // bottom refreshes.
60
+ honorsSynchronizedOutput: true,
61
+ // The pane marker does not prove the client enabled Herdr's experimental
62
+ // Kitty renderer, and leaked outer-terminal identities cannot either.
63
+ imagesRequireOverride: true,
64
+ } as const satisfies TerminalMultiplexerModule;
@@ -0,0 +1,13 @@
1
+ import type { TerminalNotification } from "../terminal-capabilities";
2
+ /** Title for out-of-band multiplexer notifications that carry none of their own. */
3
+ export declare const DEFAULT_NOTIFICATION_TITLE = "omp";
4
+ /** Title and body for an out-of-band multiplexer notification (cmux, Herdr). */
5
+ export declare function notificationTitleAndBody(message: string | TerminalNotification): {
6
+ title: string;
7
+ body: string;
8
+ };
9
+ /**
10
+ * Start a notifier CLI without waiting for or pinning it. Returns false when
11
+ * the binary cannot be spawned, which leaves delivery to the terminal fallback.
12
+ */
13
+ export declare function spawnNotifier(cmd: string[]): boolean;
@@ -0,0 +1,24 @@
1
+ import type { TerminalNotification } from "../terminal-capabilities";
2
+
3
+ /** Title for out-of-band multiplexer notifications that carry none of their own. */
4
+ export const DEFAULT_NOTIFICATION_TITLE = "omp";
5
+
6
+ /** Title and body for an out-of-band multiplexer notification (cmux, Herdr). */
7
+ export function notificationTitleAndBody(message: string | TerminalNotification): { title: string; body: string } {
8
+ if (typeof message === "string") return { title: DEFAULT_NOTIFICATION_TITLE, body: message };
9
+ return { title: message.title?.trim() || DEFAULT_NOTIFICATION_TITLE, body: message.body ?? "" };
10
+ }
11
+
12
+ /**
13
+ * Start a notifier CLI without waiting for or pinning it. Returns false when
14
+ * the binary cannot be spawned, which leaves delivery to the terminal fallback.
15
+ */
16
+ export function spawnNotifier(cmd: string[]): boolean {
17
+ try {
18
+ const child = Bun.spawn({ cmd, stdin: "ignore", stdout: "ignore", stderr: "ignore" });
19
+ child.unref();
20
+ } catch {
21
+ return false;
22
+ }
23
+ return true;
24
+ }
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Orca is an xterm.js-based IDE whose terminals expose paired pane/worktree IDs.
3
+ * It is recognized for launches, but it ranks below nested multiplexers and keeps
4
+ * the direct-terminal render path it used before it was recognized.
5
+ */
6
+ export declare const orcaMultiplexer: {
7
+ readonly id: "orca";
8
+ readonly precedence: "outerApp";
9
+ readonly sessionEnvKeys: readonly ["ORCA_PANE_KEY", "ORCA_WORKTREE_ID"];
10
+ readonly ownsScreenGrid: false;
11
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
12
+ };
@@ -0,0 +1,16 @@
1
+ import type { TerminalMultiplexerModule } from "./types";
2
+
3
+ /**
4
+ * Orca is an xterm.js-based IDE whose terminals expose paired pane/worktree IDs.
5
+ * It is recognized for launches, but it ranks below nested multiplexers and keeps
6
+ * the direct-terminal render path it used before it was recognized.
7
+ */
8
+ export const orcaMultiplexer = {
9
+ id: "orca",
10
+ precedence: "outerApp",
11
+ sessionEnvKeys: ["ORCA_PANE_KEY", "ORCA_WORKTREE_ID"],
12
+ ownsScreenGrid: false,
13
+ isInside(env: NodeJS.ProcessEnv = Bun.env): boolean {
14
+ return Boolean(env.ORCA_PANE_KEY?.trim() && env.ORCA_WORKTREE_ID?.trim());
15
+ },
16
+ } as const satisfies TerminalMultiplexerModule;
@@ -0,0 +1,9 @@
1
+ export declare const screenMultiplexer: {
2
+ readonly id: "screen";
3
+ readonly precedence: "session";
4
+ readonly sessionEnvKeys: readonly ["STY"];
5
+ readonly termPrefix: "screen";
6
+ readonly ownsScreenGrid: true;
7
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
8
+ readonly hyperlinks: "drop";
9
+ };
@@ -0,0 +1,15 @@
1
+ import type { TerminalMultiplexerModule } from "./types";
2
+
3
+ export const screenMultiplexer = {
4
+ id: "screen",
5
+ precedence: "session",
6
+ sessionEnvKeys: ["STY"],
7
+ termPrefix: "screen",
8
+ ownsScreenGrid: true,
9
+ isInside(env: NodeJS.ProcessEnv = Bun.env): boolean {
10
+ return Boolean(env.STY);
11
+ },
12
+ // GNU screen never gained OSC 8 support, so a screen layer anywhere in the
13
+ // path vetoes hyperlinks, even under a nested tmux.
14
+ hyperlinks: "drop",
15
+ } as const satisfies TerminalMultiplexerModule;
@@ -0,0 +1,29 @@
1
+ /** Wrap a control sequence in tmux's DCS passthrough envelope. */
2
+ export declare function wrapTmuxPassthrough(payload: string): string;
3
+ export declare const tmuxMultiplexer: {
4
+ readonly id: "tmux";
5
+ readonly precedence: "session";
6
+ readonly sessionEnvKeys: readonly ["TMUX"];
7
+ readonly termPrefix: "tmux";
8
+ readonly ownsScreenGrid: true;
9
+ readonly isInside: (env?: NodeJS.ProcessEnv) => boolean;
10
+ readonly notifier: {
11
+ readonly tier: "inBand";
12
+ readonly send: ({ sequence, write }: import("./types").TerminalMultiplexerNotificationRequest) => boolean;
13
+ };
14
+ readonly hyperlinks: {
15
+ readonly forward: {
16
+ readonly termProgram: "tmux";
17
+ readonly major: 3;
18
+ readonly minor: 4;
19
+ };
20
+ };
21
+ readonly backgroundColorCache: {
22
+ readonly passthrough: typeof wrapTmuxPassthrough;
23
+ readonly settleMs: 100;
24
+ };
25
+ readonly expandsRepPadding: true;
26
+ readonly expiresSynchronizedOutput: true;
27
+ readonly altRestoreEndsSynchronizedOutput: true;
28
+ readonly growsBeforeSigwinch: true;
29
+ };
@@ -0,0 +1,40 @@
1
+ import type { TerminalMultiplexerModule } from "./types";
2
+
3
+ /** Wrap a control sequence in tmux's DCS passthrough envelope. */
4
+ export function wrapTmuxPassthrough(payload: string): string {
5
+ return `\x1bPtmux;${payload.replaceAll("\x1b", "\x1b\x1b")}\x1b\\`;
6
+ }
7
+
8
+ export const tmuxMultiplexer = {
9
+ id: "tmux",
10
+ precedence: "session",
11
+ sessionEnvKeys: ["TMUX"],
12
+ termPrefix: "tmux",
13
+ ownsScreenGrid: true,
14
+ isInside(env: NodeJS.ProcessEnv = Bun.env): boolean {
15
+ return Boolean(env.TMUX);
16
+ },
17
+ notifier: {
18
+ tier: "inBand",
19
+ // tmux swallows bare OSCs; passthrough preserves the toast and BEL flags
20
+ // the pane. Bell notifications already provide that signal. One write
21
+ // keeps a concurrent renderer from interleaving between the two.
22
+ send({ sequence, write }) {
23
+ if (sequence === null) return false;
24
+ write(`${wrapTmuxPassthrough(sequence)}\x07`);
25
+ return true;
26
+ },
27
+ },
28
+ // tmux 3.4 stores OSC 8 as a cell attribute and forwards it to outer
29
+ // terminals whose `terminal-features` include `hyperlinks`; it self-reports
30
+ // via TERM_PROGRAM/TERM_PROGRAM_VERSION since 3.2a.
31
+ hyperlinks: { forward: { termProgram: "tmux", major: 3, minor: 4 } },
32
+ // An explicit appearance refresh passes OSC 11 to the outer terminal, then
33
+ // re-reads tmux's cache once it has consumed the reply.
34
+ backgroundColorCache: { passthrough: wrapTmuxPassthrough, settleMs: 100 },
35
+ expandsRepPadding: true,
36
+ // tmux expires synchronized output after one second.
37
+ expiresSynchronizedOutput: true,
38
+ altRestoreEndsSynchronizedOutput: true,
39
+ growsBeforeSigwinch: true,
40
+ } as const satisfies TerminalMultiplexerModule;
@@ -0,0 +1,100 @@
1
+ import type { TerminalNotification } from "../terminal-capabilities";
2
+ /**
3
+ * Classification tier. `session` markers outrank the TERM fallback; `outerApp`
4
+ * hosts rank below it so a multiplexer nested inside them still wins.
5
+ */
6
+ export type TerminalMultiplexerPrecedence = "session" | "outerApp";
7
+ /**
8
+ * Notification delivery tier, tried in this order so the innermost target wins:
9
+ * a `pane` notifier addresses the exact pane through the multiplexer's CLI, a
10
+ * `surface` notifier addresses the containing app surface, and an `inBand`
11
+ * notifier rewrites the terminal's own notification sequence.
12
+ */
13
+ export type TerminalMultiplexerNotificationTier = "pane" | "surface" | "inBand";
14
+ /** One notification offered to a multiplexer notifier. */
15
+ export interface TerminalMultiplexerNotificationRequest {
16
+ readonly message: string | TerminalNotification;
17
+ readonly env: NodeJS.ProcessEnv;
18
+ /** The host terminal's formatted OSC notification, or `null` when it notifies with a bare BEL. */
19
+ readonly sequence: string | null;
20
+ /** Write a sequence through the active terminal. */
21
+ write(data: string): void;
22
+ }
23
+ export interface TerminalMultiplexerNotifier {
24
+ readonly tier: TerminalMultiplexerNotificationTier;
25
+ /** Deliver the notification. `false` leaves it to the next notifier and then the terminal fallback. */
26
+ send(request: TerminalMultiplexerNotificationRequest): boolean;
27
+ }
28
+ /**
29
+ * OSC 8 handling inside a multiplexer session:
30
+ * - `render`: the multiplexer draws and opens links itself, so links work
31
+ * whatever the outer terminal supports, unless another session with a
32
+ * hyperlink policy sits in the path.
33
+ * - `drop`: never forwards OSC 8; its session anywhere in the path disables links.
34
+ * - `forward`: forwards OSC 8 once it self-reports (`TERM_PROGRAM`/`TERM_PROGRAM_VERSION`)
35
+ * at least `major.minor`; older or unreported versions stay off.
36
+ */
37
+ export type TerminalMultiplexerHyperlinks = "render" | "drop" | {
38
+ readonly forward: {
39
+ /** Lowercase `TERM_PROGRAM` the multiplexer sets inside its panes. */
40
+ readonly termProgram: string;
41
+ readonly major: number;
42
+ readonly minor: number;
43
+ };
44
+ };
45
+ /** A multiplexer that answers OSC 11 from a cached copy of the outer terminal's background color. */
46
+ export interface TerminalMultiplexerBackgroundColorCache {
47
+ /** Wrap a query so it reaches the outer terminal and refreshes the cache. */
48
+ passthrough(query: string): string;
49
+ /** How long the multiplexer needs to store the outer reply before the cache is re-read. */
50
+ readonly settleMs: number;
51
+ }
52
+ /** Everything omp needs to recognize one terminal multiplexer and adapt to it. */
53
+ export interface TerminalMultiplexerModule<Id extends string = string> {
54
+ readonly id: Id;
55
+ readonly precedence: TerminalMultiplexerPrecedence;
56
+ /** Every environment variable `isInside()` reads. */
57
+ readonly sessionEnvKeys: readonly string[];
58
+ /** Lowercase TERM prefix that identifies the multiplexer when its session markers were stripped. */
59
+ readonly termPrefix?: string;
60
+ /**
61
+ * Whether the multiplexer owns the screen grid. When true, rendering takes the
62
+ * multiplexer path (no scrollback rebuild, anchored resize, skipped probes).
63
+ */
64
+ readonly ownsScreenGrid: boolean;
65
+ isInside(env?: NodeJS.ProcessEnv): boolean;
66
+ /** Routes notifications the multiplexer would otherwise swallow. */
67
+ readonly notifier?: TerminalMultiplexerNotifier;
68
+ /** OSC 8 policy inside a session; unset defers to the outer terminal. */
69
+ readonly hyperlinks?: TerminalMultiplexerHyperlinks;
70
+ /**
71
+ * The pane's VTE honors DEC 2026 even when DECRQM is unanswered or reports
72
+ * the mode unrecognized (status 0): synchronized output starts on inside a
73
+ * session, and an unrecognized report does not turn it off.
74
+ */
75
+ readonly honorsSynchronizedOutput?: boolean;
76
+ /**
77
+ * The session does not reveal whether its client renders graphics while
78
+ * outer-terminal identity variables leak in, so images and Kitty
79
+ * placeholders stay off unless the user forces them.
80
+ */
81
+ readonly imagesRequireOverride?: boolean;
82
+ /** Platforms where the multiplexer breaks OSC 11 passthrough, so host appearance is used instead. */
83
+ readonly backgroundQueryBrokenOn?: readonly NodeJS.Platform[];
84
+ /** Present when OSC 11 replies come from a cache an explicit refresh must update first. */
85
+ readonly backgroundColorCache?: TerminalMultiplexerBackgroundColorCache;
86
+ /** Expands REP into identical styled cells, so destructive replays REP-encode their padding. */
87
+ readonly expandsRepPadding?: boolean;
88
+ /** Expires a synchronized update after a deadline, so large rebuild replays renew DEC 2026 as they stream. */
89
+ readonly expiresSynchronizedOutput?: boolean;
90
+ /**
91
+ * Restoring the alternate buffer schedules a redraw that ends a synchronized
92
+ * update early, so a synchronized resize rebuild stays on the main buffer.
93
+ */
94
+ readonly altRestoreEndsSynchronizedOutput?: boolean;
95
+ /**
96
+ * May grow its grid before SIGWINCH reaches the app, so a rebuild-mode grow
97
+ * whose full viewport no longer reaches the bottom replays history.
98
+ */
99
+ readonly growsBeforeSigwinch?: boolean;
100
+ }