@deepseek-harness-tui/dsh-tui 0.11.2 → 0.13.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 (786) hide show
  1. package/README.md +60 -8
  2. package/assets/deepy/README.md +60 -0
  3. package/assets/deepy/frames.json +1 -0
  4. package/assets/whale-girl/whale-girl-happy.png +0 -0
  5. package/assets/whaleGirl/README.md +7 -0
  6. package/assets/whaleGirl/frames.json +1 -0
  7. package/assets/whaleGirl/img/building/00.png +0 -0
  8. package/assets/whaleGirl/img/building/01.png +0 -0
  9. package/assets/whaleGirl/img/building/02.png +0 -0
  10. package/assets/whaleGirl/img/building/03.png +0 -0
  11. package/assets/whaleGirl/img/building/04.png +0 -0
  12. package/assets/whaleGirl/img/building/05.png +0 -0
  13. package/assets/whaleGirl/img/building/06.png +0 -0
  14. package/assets/whaleGirl/img/building/07.png +0 -0
  15. package/assets/whaleGirl/img/building/08.png +0 -0
  16. package/assets/whaleGirl/img/building/09.png +0 -0
  17. package/assets/whaleGirl/img/building/10.png +0 -0
  18. package/assets/whaleGirl/img/building/11.png +0 -0
  19. package/assets/whaleGirl/img/building/12.png +0 -0
  20. package/assets/whaleGirl/img/building/13.png +0 -0
  21. package/assets/whaleGirl/img/building/14.png +0 -0
  22. package/assets/whaleGirl/img/building/15.png +0 -0
  23. package/assets/whaleGirl/img/building/16.png +0 -0
  24. package/assets/whaleGirl/img/building/17.png +0 -0
  25. package/assets/whaleGirl/img/building/18.png +0 -0
  26. package/assets/whaleGirl/img/building/19.png +0 -0
  27. package/assets/whaleGirl/img/building/20.png +0 -0
  28. package/assets/whaleGirl/img/building/21.png +0 -0
  29. package/assets/whaleGirl/img/building/22.png +0 -0
  30. package/assets/whaleGirl/img/building/23.png +0 -0
  31. package/assets/whaleGirl/img/carrying/00.png +0 -0
  32. package/assets/whaleGirl/img/carrying/01.png +0 -0
  33. package/assets/whaleGirl/img/carrying/02.png +0 -0
  34. package/assets/whaleGirl/img/carrying/03.png +0 -0
  35. package/assets/whaleGirl/img/carrying/04.png +0 -0
  36. package/assets/whaleGirl/img/carrying/05.png +0 -0
  37. package/assets/whaleGirl/img/carrying/06.png +0 -0
  38. package/assets/whaleGirl/img/carrying/07.png +0 -0
  39. package/assets/whaleGirl/img/compacting/00.png +0 -0
  40. package/assets/whaleGirl/img/compacting/01.png +0 -0
  41. package/assets/whaleGirl/img/compacting/02.png +0 -0
  42. package/assets/whaleGirl/img/compacting/03.png +0 -0
  43. package/assets/whaleGirl/img/compacting/04.png +0 -0
  44. package/assets/whaleGirl/img/compacting/05.png +0 -0
  45. package/assets/whaleGirl/img/compacting/06.png +0 -0
  46. package/assets/whaleGirl/img/compacting/07.png +0 -0
  47. package/assets/whaleGirl/img/compacting/08.png +0 -0
  48. package/assets/whaleGirl/img/compacting/09.png +0 -0
  49. package/assets/whaleGirl/img/conducting/00.png +0 -0
  50. package/assets/whaleGirl/img/conducting/01.png +0 -0
  51. package/assets/whaleGirl/img/conducting/02.png +0 -0
  52. package/assets/whaleGirl/img/conducting/03.png +0 -0
  53. package/assets/whaleGirl/img/conducting/04.png +0 -0
  54. package/assets/whaleGirl/img/conducting/05.png +0 -0
  55. package/assets/whaleGirl/img/conducting/06.png +0 -0
  56. package/assets/whaleGirl/img/conducting/07.png +0 -0
  57. package/assets/whaleGirl/img/conducting/08.png +0 -0
  58. package/assets/whaleGirl/img/conducting/09.png +0 -0
  59. package/assets/whaleGirl/img/conducting/10.png +0 -0
  60. package/assets/whaleGirl/img/conducting/11.png +0 -0
  61. package/assets/whaleGirl/img/drag/00.png +0 -0
  62. package/assets/whaleGirl/img/drag/01.png +0 -0
  63. package/assets/whaleGirl/img/drag/02.png +0 -0
  64. package/assets/whaleGirl/img/drag/03.png +0 -0
  65. package/assets/whaleGirl/img/drag/04.png +0 -0
  66. package/assets/whaleGirl/img/drag/05.png +0 -0
  67. package/assets/whaleGirl/img/drag/06.png +0 -0
  68. package/assets/whaleGirl/img/drag/07.png +0 -0
  69. package/assets/whaleGirl/img/drag/08.png +0 -0
  70. package/assets/whaleGirl/img/drag/09.png +0 -0
  71. package/assets/whaleGirl/img/error/00.png +0 -0
  72. package/assets/whaleGirl/img/error/01.png +0 -0
  73. package/assets/whaleGirl/img/error/02.png +0 -0
  74. package/assets/whaleGirl/img/error/03.png +0 -0
  75. package/assets/whaleGirl/img/error/04.png +0 -0
  76. package/assets/whaleGirl/img/error/05.png +0 -0
  77. package/assets/whaleGirl/img/error/06.png +0 -0
  78. package/assets/whaleGirl/img/error/07.png +0 -0
  79. package/assets/whaleGirl/img/error/08.png +0 -0
  80. package/assets/whaleGirl/img/error/09.png +0 -0
  81. package/assets/whaleGirl/img/happy/00.png +0 -0
  82. package/assets/whaleGirl/img/happy/01.png +0 -0
  83. package/assets/whaleGirl/img/happy/02.png +0 -0
  84. package/assets/whaleGirl/img/happy/03.png +0 -0
  85. package/assets/whaleGirl/img/happy/04.png +0 -0
  86. package/assets/whaleGirl/img/happy/05.png +0 -0
  87. package/assets/whaleGirl/img/happy/06.png +0 -0
  88. package/assets/whaleGirl/img/happy/07.png +0 -0
  89. package/assets/whaleGirl/img/happy/08.png +0 -0
  90. package/assets/whaleGirl/img/happy/09.png +0 -0
  91. package/assets/whaleGirl/img/happy/10.png +0 -0
  92. package/assets/whaleGirl/img/idle/00.png +0 -0
  93. package/assets/whaleGirl/img/idle/01.png +0 -0
  94. package/assets/whaleGirl/img/idle/02.png +0 -0
  95. package/assets/whaleGirl/img/idle/03.png +0 -0
  96. package/assets/whaleGirl/img/idle/04.png +0 -0
  97. package/assets/whaleGirl/img/idle/05.png +0 -0
  98. package/assets/whaleGirl/img/idle/06.png +0 -0
  99. package/assets/whaleGirl/img/idle/07.png +0 -0
  100. package/assets/whaleGirl/img/idle/08.png +0 -0
  101. package/assets/whaleGirl/img/idle/09.png +0 -0
  102. package/assets/whaleGirl/img/idle/10.png +0 -0
  103. package/assets/whaleGirl/img/idle/11.png +0 -0
  104. package/assets/whaleGirl/img/idle/12.png +0 -0
  105. package/assets/whaleGirl/img/idle/13.png +0 -0
  106. package/assets/whaleGirl/img/idle/14.png +0 -0
  107. package/assets/whaleGirl/img/idle/15.png +0 -0
  108. package/assets/whaleGirl/img/idle/16.png +0 -0
  109. package/assets/whaleGirl/img/idle/17.png +0 -0
  110. package/assets/whaleGirl/img/idle/18.png +0 -0
  111. package/assets/whaleGirl/img/idle/19.png +0 -0
  112. package/assets/whaleGirl/img/idle/20.png +0 -0
  113. package/assets/whaleGirl/img/idle-look/00.png +0 -0
  114. package/assets/whaleGirl/img/idle-look/01.png +0 -0
  115. package/assets/whaleGirl/img/idle-look/02.png +0 -0
  116. package/assets/whaleGirl/img/idle-look/03.png +0 -0
  117. package/assets/whaleGirl/img/idle-look/04.png +0 -0
  118. package/assets/whaleGirl/img/idle-look/05.png +0 -0
  119. package/assets/whaleGirl/img/idle-look/06.png +0 -0
  120. package/assets/whaleGirl/img/idle-look/07.png +0 -0
  121. package/assets/whaleGirl/img/idle-look/08.png +0 -0
  122. package/assets/whaleGirl/img/idle-look/09.png +0 -0
  123. package/assets/whaleGirl/img/idle-look/10.png +0 -0
  124. package/assets/whaleGirl/img/idle-look/11.png +0 -0
  125. package/assets/whaleGirl/img/idle-look/12.png +0 -0
  126. package/assets/whaleGirl/img/idle-spout/00.png +0 -0
  127. package/assets/whaleGirl/img/idle-spout/01.png +0 -0
  128. package/assets/whaleGirl/img/idle-spout/02.png +0 -0
  129. package/assets/whaleGirl/img/idle-spout/03.png +0 -0
  130. package/assets/whaleGirl/img/idle-spout/04.png +0 -0
  131. package/assets/whaleGirl/img/idle-spout/05.png +0 -0
  132. package/assets/whaleGirl/img/idle-spout/06.png +0 -0
  133. package/assets/whaleGirl/img/idle-spout/07.png +0 -0
  134. package/assets/whaleGirl/img/idle-spout/08.png +0 -0
  135. package/assets/whaleGirl/img/idle-spout/09.png +0 -0
  136. package/assets/whaleGirl/img/idle-spout/10.png +0 -0
  137. package/assets/whaleGirl/img/music/00.png +0 -0
  138. package/assets/whaleGirl/img/music/01.png +0 -0
  139. package/assets/whaleGirl/img/music/02.png +0 -0
  140. package/assets/whaleGirl/img/music/03.png +0 -0
  141. package/assets/whaleGirl/img/music/04.png +0 -0
  142. package/assets/whaleGirl/img/music/05.png +0 -0
  143. package/assets/whaleGirl/img/music/06.png +0 -0
  144. package/assets/whaleGirl/img/music/07.png +0 -0
  145. package/assets/whaleGirl/img/notification/00.png +0 -0
  146. package/assets/whaleGirl/img/notification/01.png +0 -0
  147. package/assets/whaleGirl/img/notification/02.png +0 -0
  148. package/assets/whaleGirl/img/notification/03.png +0 -0
  149. package/assets/whaleGirl/img/notification/04.png +0 -0
  150. package/assets/whaleGirl/img/notification/05.png +0 -0
  151. package/assets/whaleGirl/img/notification/06.png +0 -0
  152. package/assets/whaleGirl/img/notification/07.png +0 -0
  153. package/assets/whaleGirl/img/poke-left/00.png +0 -0
  154. package/assets/whaleGirl/img/poke-left/01.png +0 -0
  155. package/assets/whaleGirl/img/poke-left/02.png +0 -0
  156. package/assets/whaleGirl/img/poke-left/03.png +0 -0
  157. package/assets/whaleGirl/img/poke-left/04.png +0 -0
  158. package/assets/whaleGirl/img/poke-left/05.png +0 -0
  159. package/assets/whaleGirl/img/poke-left/06.png +0 -0
  160. package/assets/whaleGirl/img/poke-left/07.png +0 -0
  161. package/assets/whaleGirl/img/poke-left/08.png +0 -0
  162. package/assets/whaleGirl/img/poke-left/09.png +0 -0
  163. package/assets/whaleGirl/img/poke-left/10.png +0 -0
  164. package/assets/whaleGirl/img/poke-right/00.png +0 -0
  165. package/assets/whaleGirl/img/poke-right/01.png +0 -0
  166. package/assets/whaleGirl/img/poke-right/02.png +0 -0
  167. package/assets/whaleGirl/img/poke-right/03.png +0 -0
  168. package/assets/whaleGirl/img/poke-right/04.png +0 -0
  169. package/assets/whaleGirl/img/poke-right/05.png +0 -0
  170. package/assets/whaleGirl/img/poke-right/06.png +0 -0
  171. package/assets/whaleGirl/img/poke-right/07.png +0 -0
  172. package/assets/whaleGirl/img/poke-right/08.png +0 -0
  173. package/assets/whaleGirl/img/poke-right/09.png +0 -0
  174. package/assets/whaleGirl/img/poke-right/10.png +0 -0
  175. package/assets/whaleGirl/img/sleeping/00.png +0 -0
  176. package/assets/whaleGirl/img/sleeping/01.png +0 -0
  177. package/assets/whaleGirl/img/sleeping/02.png +0 -0
  178. package/assets/whaleGirl/img/sleeping/03.png +0 -0
  179. package/assets/whaleGirl/img/sleeping/04.png +0 -0
  180. package/assets/whaleGirl/img/sleeping/05.png +0 -0
  181. package/assets/whaleGirl/img/smile-hearts/00.png +0 -0
  182. package/assets/whaleGirl/img/smile-hearts/01.png +0 -0
  183. package/assets/whaleGirl/img/smile-hearts/02.png +0 -0
  184. package/assets/whaleGirl/img/smile-hearts/03.png +0 -0
  185. package/assets/whaleGirl/img/smile-hearts/04.png +0 -0
  186. package/assets/whaleGirl/img/smile-hearts/05.png +0 -0
  187. package/assets/whaleGirl/img/smile-hearts/06.png +0 -0
  188. package/assets/whaleGirl/img/smile-hearts/07.png +0 -0
  189. package/assets/whaleGirl/img/smile-hearts/08.png +0 -0
  190. package/assets/whaleGirl/img/smile-hearts/09.png +0 -0
  191. package/assets/whaleGirl/img/smile-hearts/10.png +0 -0
  192. package/assets/whaleGirl/img/smile-hearts/11.png +0 -0
  193. package/assets/whaleGirl/img/smile-hearts/12.png +0 -0
  194. package/assets/whaleGirl/img/smile-hearts/13.png +0 -0
  195. package/assets/whaleGirl/img/smile-hearts/14.png +0 -0
  196. package/assets/whaleGirl/img/smile-hearts/15.png +0 -0
  197. package/assets/whaleGirl/img/swim/00.png +0 -0
  198. package/assets/whaleGirl/img/swim/01.png +0 -0
  199. package/assets/whaleGirl/img/swim/02.png +0 -0
  200. package/assets/whaleGirl/img/swim/03.png +0 -0
  201. package/assets/whaleGirl/img/swim/04.png +0 -0
  202. package/assets/whaleGirl/img/swim/05.png +0 -0
  203. package/assets/whaleGirl/img/swim/06.png +0 -0
  204. package/assets/whaleGirl/img/swim/07.png +0 -0
  205. package/assets/whaleGirl/img/thinking/00.png +0 -0
  206. package/assets/whaleGirl/img/thinking/01.png +0 -0
  207. package/assets/whaleGirl/img/thinking/02.png +0 -0
  208. package/assets/whaleGirl/img/thinking/03.png +0 -0
  209. package/assets/whaleGirl/img/thinking/04.png +0 -0
  210. package/assets/whaleGirl/img/thinking/05.png +0 -0
  211. package/assets/whaleGirl/img/thinking/06.png +0 -0
  212. package/assets/whaleGirl/img/thinking/07.png +0 -0
  213. package/assets/whaleGirl/img/thinking/08.png +0 -0
  214. package/assets/whaleGirl/img/thinking/09.png +0 -0
  215. package/assets/whaleGirl/img/thinking/10.png +0 -0
  216. package/assets/whaleGirl/img/thinking/11.png +0 -0
  217. package/assets/whaleGirl/img/thinking/12.png +0 -0
  218. package/assets/whaleGirl/img/thinking/13.png +0 -0
  219. package/assets/whaleGirl/img/thinking/14.png +0 -0
  220. package/assets/whaleGirl/img/thinking/15.png +0 -0
  221. package/assets/whaleGirl/img/thinking/16.png +0 -0
  222. package/assets/whaleGirl/img/thumbs-up/00.png +0 -0
  223. package/assets/whaleGirl/img/thumbs-up/01.png +0 -0
  224. package/assets/whaleGirl/img/thumbs-up/02.png +0 -0
  225. package/assets/whaleGirl/img/thumbs-up/03.png +0 -0
  226. package/assets/whaleGirl/img/thumbs-up/04.png +0 -0
  227. package/assets/whaleGirl/img/thumbs-up/05.png +0 -0
  228. package/assets/whaleGirl/img/thumbs-up/06.png +0 -0
  229. package/assets/whaleGirl/img/thumbs-up/07.png +0 -0
  230. package/assets/whaleGirl/img/thumbs-up/08.png +0 -0
  231. package/assets/whaleGirl/img/thumbs-up/09.png +0 -0
  232. package/assets/whaleGirl/img/thumbs-up/10.png +0 -0
  233. package/assets/whaleGirl/img/thumbs-up/11.png +0 -0
  234. package/assets/whaleGirl/img/thumbs-up/12.png +0 -0
  235. package/assets/whaleGirl/img/tickle/00.png +0 -0
  236. package/assets/whaleGirl/img/tickle/01.png +0 -0
  237. package/assets/whaleGirl/img/tickle/02.png +0 -0
  238. package/assets/whaleGirl/img/tickle/03.png +0 -0
  239. package/assets/whaleGirl/img/tickle/04.png +0 -0
  240. package/assets/whaleGirl/img/tickle/05.png +0 -0
  241. package/assets/whaleGirl/img/tickle/06.png +0 -0
  242. package/assets/whaleGirl/img/tickle/07.png +0 -0
  243. package/assets/whaleGirl/img/tickle/08.png +0 -0
  244. package/assets/whaleGirl/img/tickle/09.png +0 -0
  245. package/assets/whaleGirl/img/tickle/10.png +0 -0
  246. package/assets/whaleGirl/img/tickle/11.png +0 -0
  247. package/assets/whaleGirl/img/tickle/12.png +0 -0
  248. package/assets/whaleGirl/img/tickle/13.png +0 -0
  249. package/assets/whaleGirl/img/timings.json +1 -0
  250. package/assets/whaleGirl/img/typing/00.png +0 -0
  251. package/assets/whaleGirl/img/typing/01.png +0 -0
  252. package/assets/whaleGirl/img/typing/02.png +0 -0
  253. package/assets/whaleGirl/img/typing/03.png +0 -0
  254. package/assets/whaleGirl/img/typing/04.png +0 -0
  255. package/assets/whaleGirl/img/typing/05.png +0 -0
  256. package/assets/whaleGirl/img/typing/06.png +0 -0
  257. package/assets/whaleGirl/img/typing/07.png +0 -0
  258. package/assets/whaleGirl/img/typing/08.png +0 -0
  259. package/assets/whaleGirl/img/typing/09.png +0 -0
  260. package/assets/whaleGirl/img/typing/10.png +0 -0
  261. package/assets/whaleGirl/img/typing/11.png +0 -0
  262. package/assets/whaleGirl/img/typing/12.png +0 -0
  263. package/assets/whaleGirl/img/typing/13.png +0 -0
  264. package/assets/whaleGirl/img/waking/00.png +0 -0
  265. package/assets/whaleGirl/img/waking/01.png +0 -0
  266. package/assets/whaleGirl/img/waking/02.png +0 -0
  267. package/assets/whaleGirl/img/waking/03.png +0 -0
  268. package/assets/whaleGirl/img/waking/04.png +0 -0
  269. package/assets/whaleGirl/img/waking/05.png +0 -0
  270. package/assets/whaleGirl/img/waking/06.png +0 -0
  271. package/assets/whaleGirl/img/waking/07.png +0 -0
  272. package/assets/whaleGirl/img/waking/08.png +0 -0
  273. package/assets/whaleGirl/img/waking/09.png +0 -0
  274. package/assets/whaleGirl/img/waking/10.png +0 -0
  275. package/bin/dsh-tui.js +38 -6
  276. package/cordis.patch.yml +31 -6
  277. package/guide/dsh-tui-guide/SKILL.md +37 -0
  278. package/guide/dsh-tui-guide/configuration.en.md +415 -0
  279. package/guide/dsh-tui-guide/configuration.md +354 -0
  280. package/guide/dsh-tui-guide/getting-started.en.md +401 -0
  281. package/guide/dsh-tui-guide/getting-started.md +399 -0
  282. package/guide/dsh-tui-guide/interaction.en.md +738 -0
  283. package/guide/dsh-tui-guide/interaction.md +793 -0
  284. package/guide/dsh-tui-guide/migrate.en.md +166 -0
  285. package/guide/dsh-tui-guide/migrate.md +132 -0
  286. package/guide/dsh-tui-guide/plugins.en.md +78 -0
  287. package/guide/dsh-tui-guide/plugins.md +64 -0
  288. package/guide/dsh-tui-guide/themes.en.md +197 -0
  289. package/guide/dsh-tui-guide/themes.md +188 -0
  290. package/guide/dsh-tui-guide/user-guide.en.md +711 -0
  291. package/guide/dsh-tui-guide/user-guide.md +666 -0
  292. package/guide/dsh-tui-guide/vscode.en.md +301 -0
  293. package/guide/dsh-tui-guide/vscode.md +272 -0
  294. package/lib/settings.json +219 -5
  295. package/lib/types/adapter/channel/ui-policy.d.ts +7 -2
  296. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  297. package/lib/types/adapter/channel/ui-policy.js +18 -1
  298. package/lib/types/adapter/index.d.ts +1 -1
  299. package/lib/types/adapter/index.js +1 -1
  300. package/lib/types/adapter/kernel/host-facade.d.ts +4 -1
  301. package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
  302. package/lib/types/adapter/kernel/host-facade.js +8 -1
  303. package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
  304. package/lib/types/adapter/kernel/runtime.js +13 -0
  305. package/lib/types/adapter/kernel/slices/index.d.ts +1 -0
  306. package/lib/types/adapter/kernel/slices/index.d.ts.map +1 -1
  307. package/lib/types/adapter/kernel/slices/index.js +3 -0
  308. package/lib/types/adapter/kernel/slices/panels.d.ts +3 -0
  309. package/lib/types/adapter/kernel/slices/panels.d.ts.map +1 -0
  310. package/lib/types/adapter/kernel/slices/panels.js +14 -0
  311. package/lib/types/adapter/ports/channel-capabilities.d.ts +48 -0
  312. package/lib/types/adapter/ports/channel-capabilities.d.ts.map +1 -0
  313. package/lib/types/adapter/ports/channel-capabilities.js +2 -0
  314. package/lib/types/adapter/ports/channel-catalog.d.ts +7 -0
  315. package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
  316. package/lib/types/adapter/ports/channel-display.d.ts +10 -0
  317. package/lib/types/adapter/ports/channel-display.d.ts.map +1 -1
  318. package/lib/types/adapter/ports/channel-settings.d.ts +2 -1
  319. package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
  320. package/lib/types/adapter/ports/channel-ui.d.ts +91 -4
  321. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  322. package/lib/types/adapter/ports/channel-view.d.ts +137 -9
  323. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  324. package/lib/types/adapter/ports/channel.d.ts +1 -1
  325. package/lib/types/adapter/ports/channel.js +1 -1
  326. package/lib/types/adapter/ports/descriptor.d.ts +1 -1
  327. package/lib/types/adapter/ports/descriptor.js +1 -1
  328. package/lib/types/adapter/ports/index.d.ts +1 -1
  329. package/lib/types/adapter/ports/index.js +1 -1
  330. package/lib/types/adapter/ports/panels.d.ts +28 -0
  331. package/lib/types/adapter/ports/panels.d.ts.map +1 -0
  332. package/lib/types/adapter/ports/panels.js +8 -0
  333. package/lib/types/adapter/ports/workspace.d.ts +1 -1
  334. package/lib/types/adapter/ports/workspace.js +1 -1
  335. package/lib/types/adapter/spec/index.d.ts +2 -2
  336. package/lib/types/adapter/spec/index.js +2 -2
  337. package/lib/types/adapter/spec/profile.d.ts +6 -6
  338. package/lib/types/adapter/spec/profile.d.ts.map +1 -1
  339. package/lib/types/adapter/spec/profile.js +6 -6
  340. package/lib/types/adapter/spec/protocol-constants.d.ts +3 -3
  341. package/lib/types/adapter/spec/protocol-constants.d.ts.map +1 -1
  342. package/lib/types/adapter/spec/protocol-constants.js +5 -5
  343. package/lib/types/adapter/spec/tui-contributions.d.ts +4 -4
  344. package/lib/types/adapter/spec/tui-contributions.d.ts.map +1 -1
  345. package/lib/types/adapter/spec/tui-contributions.js +5 -5
  346. package/lib/types/adapter/standard/descriptor.js +1 -1
  347. package/lib/types/adapter/standard/index.d.ts +3 -3
  348. package/lib/types/adapter/standard/index.js +3 -3
  349. package/lib/types/adapter/standard/registry.d.ts +3 -2
  350. package/lib/types/adapter/standard/registry.d.ts.map +1 -1
  351. package/lib/types/adapter/standard/registry.js +6 -5
  352. package/lib/types/adapter/standard/tui-extension.d.ts +1 -1
  353. package/lib/types/adapter/standard/tui-extension.js +1 -1
  354. package/lib/types/adapter/upstream/index.d.ts +1 -0
  355. package/lib/types/adapter/upstream/index.d.ts.map +1 -1
  356. package/lib/types/adapter/upstream/index.js +1 -0
  357. package/lib/types/adapter/upstream/panels-driver.d.ts +18 -0
  358. package/lib/types/adapter/upstream/panels-driver.d.ts.map +1 -0
  359. package/lib/types/adapter/upstream/panels-driver.js +159 -0
  360. package/lib/types/api.d.ts +2 -0
  361. package/lib/types/api.d.ts.map +1 -1
  362. package/lib/types/api.js +3 -1
  363. package/lib/types/commands.d.ts.map +1 -1
  364. package/lib/types/commands.js +12 -4
  365. package/lib/types/components/ActivityLine.d.ts +6 -9
  366. package/lib/types/components/ActivityLine.d.ts.map +1 -1
  367. package/lib/types/components/ActivityLine.js +7 -8
  368. package/lib/types/components/Chat/JobCard.d.ts +27 -1
  369. package/lib/types/components/Chat/JobCard.d.ts.map +1 -1
  370. package/lib/types/components/Chat/JobCard.js +128 -33
  371. package/lib/types/components/Chat/JobGroupHeader.d.ts +27 -0
  372. package/lib/types/components/Chat/JobGroupHeader.d.ts.map +1 -0
  373. package/lib/types/components/Chat/JobGroupHeader.js +62 -0
  374. package/lib/types/components/Chat/SubagentMessage.js +6 -6
  375. package/lib/types/components/GoalTodoPanel.d.ts +13 -1
  376. package/lib/types/components/GoalTodoPanel.d.ts.map +1 -1
  377. package/lib/types/components/GoalTodoPanel.js +55 -14
  378. package/lib/types/components/JobsPanel.d.ts +42 -2
  379. package/lib/types/components/JobsPanel.d.ts.map +1 -1
  380. package/lib/types/components/JobsPanel.js +220 -40
  381. package/lib/types/components/LogoV2.d.ts +43 -3
  382. package/lib/types/components/LogoV2.d.ts.map +1 -1
  383. package/lib/types/components/LogoV2.js +42 -15
  384. package/lib/types/components/MessageList.d.ts +8 -2
  385. package/lib/types/components/MessageList.d.ts.map +1 -1
  386. package/lib/types/components/MessageList.js +215 -21
  387. package/lib/types/components/OverlayAbove.d.ts +11 -1
  388. package/lib/types/components/OverlayAbove.d.ts.map +1 -1
  389. package/lib/types/components/OverlayAbove.js +2 -12
  390. package/lib/types/components/PageMargin.d.ts.map +1 -1
  391. package/lib/types/components/PageMargin.js +3 -1
  392. package/lib/types/components/PluginSceneBoundary.d.ts +1 -1
  393. package/lib/types/components/PluginSceneBoundary.js +1 -1
  394. package/lib/types/components/PresetPicker.d.ts +4 -0
  395. package/lib/types/components/PresetPicker.d.ts.map +1 -1
  396. package/lib/types/components/PresetPicker.js +5 -1
  397. package/lib/types/components/PromptInput.d.ts +20 -22
  398. package/lib/types/components/PromptInput.d.ts.map +1 -1
  399. package/lib/types/components/PromptInput.js +425 -157
  400. package/lib/types/components/SearchBox.d.ts +22 -5
  401. package/lib/types/components/SearchBox.d.ts.map +1 -1
  402. package/lib/types/components/SearchBox.js +17 -13
  403. package/lib/types/components/StarPrompt.d.ts +6 -0
  404. package/lib/types/components/StarPrompt.d.ts.map +1 -1
  405. package/lib/types/components/StarPrompt.js +45 -0
  406. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  407. package/lib/types/components/SubagentCard.js +24 -9
  408. package/lib/types/components/SubagentDashboard.d.ts +18 -3
  409. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  410. package/lib/types/components/SubagentDashboard.js +53 -5
  411. package/lib/types/components/SubagentDetailScene.d.ts +7 -1
  412. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  413. package/lib/types/components/SubagentDetailScene.js +160 -13
  414. package/lib/types/components/SurfaceEdges.d.ts +21 -0
  415. package/lib/types/components/SurfaceEdges.d.ts.map +1 -0
  416. package/lib/types/components/SurfaceEdges.js +7 -0
  417. package/lib/types/components/ThemePicker.d.ts +9 -0
  418. package/lib/types/components/ThemePicker.d.ts.map +1 -1
  419. package/lib/types/components/ThemePicker.js +74 -10
  420. package/lib/types/components/ThemePreviewPane.d.ts +26 -0
  421. package/lib/types/components/ThemePreviewPane.d.ts.map +1 -0
  422. package/lib/types/components/ThemePreviewPane.js +99 -0
  423. package/lib/types/components/WhaleGirl.d.ts +6 -0
  424. package/lib/types/components/WhaleGirl.d.ts.map +1 -1
  425. package/lib/types/components/WhaleGirl.js +18 -0
  426. package/lib/types/components/design-system/Divider.d.ts.map +1 -1
  427. package/lib/types/components/design-system/Divider.js +14 -9
  428. package/lib/types/components/launchpadActions.d.ts +73 -0
  429. package/lib/types/components/launchpadActions.d.ts.map +1 -0
  430. package/lib/types/components/launchpadActions.js +111 -0
  431. package/lib/types/components/launchpadLayout.d.ts +62 -0
  432. package/lib/types/components/launchpadLayout.d.ts.map +1 -0
  433. package/lib/types/components/launchpadLayout.js +176 -0
  434. package/lib/types/components/messages/AssistantThinkingMessage.js +4 -4
  435. package/lib/types/components/onboardingModel.d.ts +83 -0
  436. package/lib/types/components/onboardingModel.d.ts.map +1 -0
  437. package/lib/types/components/onboardingModel.js +128 -0
  438. package/lib/types/components/sidePanel/InfoPanel.d.ts +17 -0
  439. package/lib/types/components/sidePanel/InfoPanel.d.ts.map +1 -0
  440. package/lib/types/components/sidePanel/InfoPanel.js +124 -0
  441. package/lib/types/components/sidePanel/PanelBar.d.ts +45 -0
  442. package/lib/types/components/sidePanel/PanelBar.d.ts.map +1 -0
  443. package/lib/types/components/sidePanel/PanelBar.js +104 -0
  444. package/lib/types/components/sidePanel/PanelErrorBoundary.d.ts +21 -0
  445. package/lib/types/components/sidePanel/PanelErrorBoundary.d.ts.map +1 -0
  446. package/lib/types/components/sidePanel/PanelErrorBoundary.js +30 -0
  447. package/lib/types/components/sidePanel/PanelHost.d.ts +36 -0
  448. package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -0
  449. package/lib/types/components/sidePanel/PanelHost.js +81 -0
  450. package/lib/types/components/sidePanel/PanelPicker.d.ts +24 -0
  451. package/lib/types/components/sidePanel/PanelPicker.d.ts.map +1 -0
  452. package/lib/types/components/sidePanel/PanelPicker.js +45 -0
  453. package/lib/types/components/sidePanel/PanelStore.d.ts +55 -0
  454. package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -0
  455. package/lib/types/components/sidePanel/PanelStore.js +109 -0
  456. package/lib/types/components/sidePanel/SidePanelColumn.d.ts +31 -0
  457. package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -0
  458. package/lib/types/components/sidePanel/SidePanelColumn.js +70 -0
  459. package/lib/types/components/sidePanel/SidePanelLayout.d.ts +54 -0
  460. package/lib/types/components/sidePanel/SidePanelLayout.d.ts.map +1 -0
  461. package/lib/types/components/sidePanel/SidePanelLayout.js +81 -0
  462. package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +43 -0
  463. package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -0
  464. package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +26 -0
  465. package/lib/types/components/sidePanel/TrajectoryPanel.d.ts +7 -0
  466. package/lib/types/components/sidePanel/TrajectoryPanel.d.ts.map +1 -0
  467. package/lib/types/components/sidePanel/TrajectoryPanel.js +263 -0
  468. package/lib/types/components/sidePanel/WorkspacePanel.d.ts +4 -0
  469. package/lib/types/components/sidePanel/WorkspacePanel.d.ts.map +1 -0
  470. package/lib/types/components/sidePanel/WorkspacePanel.js +171 -0
  471. package/lib/types/components/sidePanel/builtinPanels.d.ts +3 -0
  472. package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -0
  473. package/lib/types/components/sidePanel/builtinPanels.js +229 -0
  474. package/lib/types/components/sidePanel/companion/CompanionPanel.d.ts +53 -0
  475. package/lib/types/components/sidePanel/companion/CompanionPanel.d.ts.map +1 -0
  476. package/lib/types/components/sidePanel/companion/CompanionPanel.js +528 -0
  477. package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +32 -0
  478. package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -0
  479. package/lib/types/components/sidePanel/companion/SplashMascot.js +103 -0
  480. package/lib/types/components/sidePanel/companion/deepy.d.ts +75 -0
  481. package/lib/types/components/sidePanel/companion/deepy.d.ts.map +1 -0
  482. package/lib/types/components/sidePanel/companion/deepy.js +222 -0
  483. package/lib/types/components/sidePanel/companion/mood.d.ts +176 -0
  484. package/lib/types/components/sidePanel/companion/mood.d.ts.map +1 -0
  485. package/lib/types/components/sidePanel/companion/mood.js +212 -0
  486. package/lib/types/components/sidePanel/companion/pose.d.ts +43 -0
  487. package/lib/types/components/sidePanel/companion/pose.d.ts.map +1 -0
  488. package/lib/types/components/sidePanel/companion/pose.js +34 -0
  489. package/lib/types/components/sidePanel/companion/skins.d.ts +102 -0
  490. package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -0
  491. package/lib/types/components/sidePanel/companion/skins.js +408 -0
  492. package/lib/types/components/sidePanel/dimensions.d.ts +60 -0
  493. package/lib/types/components/sidePanel/dimensions.d.ts.map +1 -0
  494. package/lib/types/components/sidePanel/dimensions.js +75 -0
  495. package/lib/types/components/sidePanel/jobsFocusStore.d.ts +21 -0
  496. package/lib/types/components/sidePanel/jobsFocusStore.d.ts.map +1 -0
  497. package/lib/types/components/sidePanel/jobsFocusStore.js +30 -0
  498. package/lib/types/components/sidePanel/panelBridge.d.ts +44 -0
  499. package/lib/types/components/sidePanel/panelBridge.d.ts.map +1 -0
  500. package/lib/types/components/sidePanel/panelBridge.js +55 -0
  501. package/lib/types/components/sidePanel/pluginPanelAdapter.d.ts +22 -0
  502. package/lib/types/components/sidePanel/pluginPanelAdapter.d.ts.map +1 -0
  503. package/lib/types/components/sidePanel/pluginPanelAdapter.js +148 -0
  504. package/lib/types/components/sidePanel/types.d.ts +110 -0
  505. package/lib/types/components/sidePanel/types.d.ts.map +1 -0
  506. package/lib/types/components/sidePanel/types.js +1 -0
  507. package/lib/types/components/sidePanel/usePanelInput.d.ts +5 -0
  508. package/lib/types/components/sidePanel/usePanelInput.d.ts.map +1 -0
  509. package/lib/types/components/sidePanel/usePanelInput.js +44 -0
  510. package/lib/types/components/sidePanel/useSidePanel.d.ts +47 -0
  511. package/lib/types/components/sidePanel/useSidePanel.d.ts.map +1 -0
  512. package/lib/types/components/sidePanel/useSidePanel.js +284 -0
  513. package/lib/types/components/trajectory/MiniWake.d.ts +11 -1
  514. package/lib/types/components/trajectory/MiniWake.d.ts.map +1 -1
  515. package/lib/types/components/trajectory/MiniWake.js +9 -3
  516. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +3 -1
  517. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -1
  518. package/lib/types/components/workspaces/HomeWorkspaceRow.js +3 -2
  519. package/lib/types/deepseekPricing.d.ts +10 -9
  520. package/lib/types/deepseekPricing.d.ts.map +1 -1
  521. package/lib/types/deepseekPricing.js +18 -11
  522. package/lib/types/dsh-adapter/channel/attached-context.d.ts +58 -0
  523. package/lib/types/dsh-adapter/channel/attached-context.d.ts.map +1 -0
  524. package/lib/types/dsh-adapter/channel/attached-context.js +109 -0
  525. package/lib/types/dsh-adapter/channel/binding-events.d.ts +6 -0
  526. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  527. package/lib/types/dsh-adapter/channel/binding-events.js +43 -0
  528. package/lib/types/dsh-adapter/channel/capabilities.d.ts +82 -0
  529. package/lib/types/dsh-adapter/channel/capabilities.d.ts.map +1 -0
  530. package/lib/types/dsh-adapter/channel/capabilities.js +165 -0
  531. package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
  532. package/lib/types/dsh-adapter/channel/command-completions.js +8 -0
  533. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +4 -12
  534. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
  535. package/lib/types/dsh-adapter/channel/context-bookkeeping.js +9 -4
  536. package/lib/types/dsh-adapter/channel/external-commands.d.ts.map +1 -1
  537. package/lib/types/dsh-adapter/channel/external-commands.js +8 -2
  538. package/lib/types/dsh-adapter/channel/input-delivery.d.ts +13 -2
  539. package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
  540. package/lib/types/dsh-adapter/channel/input-delivery.js +18 -1
  541. package/lib/types/dsh-adapter/channel/job-projection.d.ts +1 -0
  542. package/lib/types/dsh-adapter/channel/job-projection.d.ts.map +1 -1
  543. package/lib/types/dsh-adapter/channel/job-projection.js +88 -3
  544. package/lib/types/dsh-adapter/channel/mode-actions.d.ts +13 -3
  545. package/lib/types/dsh-adapter/channel/mode-actions.d.ts.map +1 -1
  546. package/lib/types/dsh-adapter/channel/mode-actions.js +55 -9
  547. package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +8 -1
  548. package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
  549. package/lib/types/dsh-adapter/channel/mode-permission-actions.js +25 -10
  550. package/lib/types/dsh-adapter/channel/preferences.d.ts +1 -1
  551. package/lib/types/dsh-adapter/channel/preferences.d.ts.map +1 -1
  552. package/lib/types/dsh-adapter/channel/preferences.js +26 -8
  553. package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
  554. package/lib/types/dsh-adapter/channel/projection.js +42 -37
  555. package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
  556. package/lib/types/dsh-adapter/channel/reports.js +13 -4
  557. package/lib/types/dsh-adapter/channel/session-reset.d.ts +5 -1
  558. package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
  559. package/lib/types/dsh-adapter/channel/session-reset.js +1 -0
  560. package/lib/types/dsh-adapter/channel/state.d.ts +21 -3
  561. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  562. package/lib/types/dsh-adapter/channel/state.js +3 -2
  563. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  564. package/lib/types/dsh-adapter/channel/subagent-projection.js +5 -0
  565. package/lib/types/dsh-adapter/channel/types.d.ts +1 -1
  566. package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
  567. package/lib/types/dsh-adapter/channel/usage.d.ts +18 -1
  568. package/lib/types/dsh-adapter/channel/usage.d.ts.map +1 -1
  569. package/lib/types/dsh-adapter/channel/usage.js +97 -2
  570. package/lib/types/dsh-adapter/channel.d.ts +1 -1
  571. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  572. package/lib/types/dsh-adapter/channel.js +72 -4
  573. package/lib/types/dsh-adapter/context-occupancy.d.ts +204 -0
  574. package/lib/types/dsh-adapter/context-occupancy.d.ts.map +1 -0
  575. package/lib/types/dsh-adapter/context-occupancy.js +299 -0
  576. package/lib/types/dsh-adapter/contract.d.ts +19 -4
  577. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  578. package/lib/types/dsh-adapter/contract.js +33 -3
  579. package/lib/types/dsh-adapter/index.d.ts +32 -0
  580. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  581. package/lib/types/dsh-adapter/index.js +20 -1
  582. package/lib/types/dsh-adapter/jobs.d.ts +82 -16
  583. package/lib/types/dsh-adapter/jobs.d.ts.map +1 -1
  584. package/lib/types/dsh-adapter/jobs.js +104 -9
  585. package/lib/types/dsh-adapter/migrate/sessionize.d.ts.map +1 -1
  586. package/lib/types/dsh-adapter/migrate/sessionize.js +11 -12
  587. package/lib/types/dsh-adapter/oauth/bonus.d.ts +27 -0
  588. package/lib/types/dsh-adapter/oauth/bonus.d.ts.map +1 -0
  589. package/lib/types/dsh-adapter/oauth/bonus.js +69 -0
  590. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.d.ts +2 -1
  591. package/lib/types/dsh-adapter/oauth/command.d.ts.map +1 -0
  592. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/command.js +7 -4
  593. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.d.ts +13 -10
  594. package/lib/types/dsh-adapter/oauth/credentials.d.ts.map +1 -0
  595. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/credentials.js +61 -23
  596. package/lib/types/dsh-adapter/oauth/deepseek.d.ts +39 -0
  597. package/lib/types/dsh-adapter/oauth/deepseek.d.ts.map +1 -0
  598. package/lib/types/dsh-adapter/oauth/deepseek.js +189 -0
  599. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.d.ts +24 -16
  600. package/lib/types/dsh-adapter/oauth/index.d.ts.map +1 -0
  601. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/index.js +34 -20
  602. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/interaction.d.ts +14 -12
  603. package/lib/types/dsh-adapter/oauth/interaction.d.ts.map +1 -0
  604. package/lib/types/dsh-adapter/oauth/interaction.js +283 -0
  605. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.d.ts +2 -1
  606. package/lib/types/dsh-adapter/oauth/opener.d.ts.map +1 -0
  607. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/opener.js +1 -1
  608. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.d.ts +10 -3
  609. package/lib/types/dsh-adapter/oauth/pi-ai.d.ts.map +1 -0
  610. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/pi-ai.js +8 -3
  611. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.d.ts +6 -3
  612. package/lib/types/dsh-adapter/oauth/profiles.d.ts.map +1 -0
  613. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/profiles.js +11 -3
  614. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.d.ts +14 -7
  615. package/lib/types/dsh-adapter/oauth/service.d.ts.map +1 -0
  616. package/{node_modules/@deepseek-harness-tui/dsh-auth/lib → lib/types/dsh-adapter/oauth}/service.js +82 -17
  617. package/lib/types/dsh-adapter/panel-runtime-registry.d.ts +39 -0
  618. package/lib/types/dsh-adapter/panel-runtime-registry.d.ts.map +1 -0
  619. package/lib/types/dsh-adapter/panel-runtime-registry.js +84 -0
  620. package/lib/types/dsh-adapter/panels.d.ts +251 -0
  621. package/lib/types/dsh-adapter/panels.d.ts.map +1 -0
  622. package/lib/types/dsh-adapter/panels.js +407 -0
  623. package/lib/types/dsh-adapter/plugin-host.js +1 -1
  624. package/lib/types/dsh-adapter/plugin.d.ts +25 -2
  625. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  626. package/lib/types/dsh-adapter/plugin.js +241 -44
  627. package/lib/types/dsh-adapter/presets.d.ts +26 -4
  628. package/lib/types/dsh-adapter/presets.d.ts.map +1 -1
  629. package/lib/types/dsh-adapter/presets.js +29 -5
  630. package/lib/types/dsh-adapter/providerWizard.d.ts.map +1 -1
  631. package/lib/types/dsh-adapter/providerWizard.js +6 -2
  632. package/lib/types/dsh-adapter/startup-args.d.ts +8 -0
  633. package/lib/types/dsh-adapter/startup-args.d.ts.map +1 -0
  634. package/lib/types/dsh-adapter/startup-args.js +34 -0
  635. package/lib/types/dsh-adapter/subagents.d.ts +1 -0
  636. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  637. package/lib/types/dsh-adapter/subagents.js +10 -1
  638. package/lib/types/history.d.ts +24 -7
  639. package/lib/types/history.d.ts.map +1 -1
  640. package/lib/types/history.js +77 -18
  641. package/lib/types/i18n.d.ts +908 -33
  642. package/lib/types/i18n.d.ts.map +1 -1
  643. package/lib/types/i18n.js +355 -17
  644. package/lib/types/ink/components/App.d.ts +14 -1
  645. package/lib/types/ink/components/App.d.ts.map +1 -1
  646. package/lib/types/ink/components/App.js +71 -61
  647. package/lib/types/ink/components/TerminalSizeContext.d.ts +7 -0
  648. package/lib/types/ink/components/TerminalSizeContext.d.ts.map +1 -1
  649. package/lib/types/ink/hooks/use-input.d.ts +11 -0
  650. package/lib/types/ink/hooks/use-input.d.ts.map +1 -1
  651. package/lib/types/ink/hooks/use-input.js +8 -2
  652. package/lib/types/ink/hooks/use-terminal-viewport.d.ts.map +1 -1
  653. package/lib/types/ink/hooks/use-terminal-viewport.js +3 -1
  654. package/lib/types/ink/ink.d.ts +18 -0
  655. package/lib/types/ink/ink.d.ts.map +1 -1
  656. package/lib/types/ink/ink.js +51 -8
  657. package/lib/types/ink/output.d.ts +10 -1
  658. package/lib/types/ink/output.d.ts.map +1 -1
  659. package/lib/types/ink/output.js +3 -3
  660. package/lib/types/ink/parse-keypress.d.ts +71 -3
  661. package/lib/types/ink/parse-keypress.d.ts.map +1 -1
  662. package/lib/types/ink/parse-keypress.js +815 -133
  663. package/lib/types/ink/render-node-to-output.d.ts +1 -1
  664. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  665. package/lib/types/ink/render-node-to-output.js +37 -5
  666. package/lib/types/ink/screen.d.ts +26 -1
  667. package/lib/types/ink/screen.d.ts.map +1 -1
  668. package/lib/types/ink/screen.js +96 -1
  669. package/lib/types/ink/selection.d.ts +23 -1
  670. package/lib/types/ink/selection.d.ts.map +1 -1
  671. package/lib/types/ink/selection.js +102 -26
  672. package/lib/types/ink/sixel-codec.d.ts +11 -0
  673. package/lib/types/ink/sixel-codec.d.ts.map +1 -1
  674. package/lib/types/ink/sixel-codec.js +42 -8
  675. package/lib/types/ink/terminal-querier.d.ts +25 -0
  676. package/lib/types/ink/terminal-querier.d.ts.map +1 -1
  677. package/lib/types/ink/terminal-querier.js +42 -0
  678. package/lib/types/ink/update-overflow-guard.d.ts +24 -0
  679. package/lib/types/ink/update-overflow-guard.d.ts.map +1 -1
  680. package/lib/types/ink/update-overflow-guard.js +25 -3
  681. package/lib/types/math/renderer.d.ts +2 -0
  682. package/lib/types/math/renderer.d.ts.map +1 -1
  683. package/lib/types/math/renderer.js +4 -0
  684. package/lib/types/minimalUiMode.d.ts +19 -0
  685. package/lib/types/minimalUiMode.d.ts.map +1 -0
  686. package/lib/types/minimalUiMode.js +23 -0
  687. package/lib/types/oauth.d.ts +5 -10
  688. package/lib/types/oauth.d.ts.map +1 -1
  689. package/lib/types/oauth.js +5 -10
  690. package/lib/types/onboardingPrefs.d.ts +52 -0
  691. package/lib/types/onboardingPrefs.d.ts.map +1 -0
  692. package/lib/types/onboardingPrefs.js +107 -0
  693. package/lib/types/panels.d.ts +4 -0
  694. package/lib/types/panels.d.ts.map +1 -0
  695. package/lib/types/panels.js +4 -0
  696. package/lib/types/screens/Chat.d.ts +25 -1
  697. package/lib/types/screens/Chat.d.ts.map +1 -1
  698. package/lib/types/screens/Chat.js +1149 -302
  699. package/lib/types/screens/Launchpad.d.ts +194 -0
  700. package/lib/types/screens/Launchpad.d.ts.map +1 -0
  701. package/lib/types/screens/Launchpad.js +607 -0
  702. package/lib/types/screens/Onboarding.d.ts +37 -0
  703. package/lib/types/screens/Onboarding.d.ts.map +1 -0
  704. package/lib/types/screens/Onboarding.js +492 -0
  705. package/lib/types/screens/SessionSupervisor.d.ts.map +1 -1
  706. package/lib/types/screens/SessionSupervisor.js +18 -12
  707. package/lib/types/screens/StatusLine.d.ts +4 -0
  708. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  709. package/lib/types/screens/StatusLine.js +43 -25
  710. package/lib/types/screens/StatusMetrics.d.ts +22 -0
  711. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  712. package/lib/types/screens/StatusMetrics.js +20 -0
  713. package/lib/types/screens/chatOverlay.d.ts +18 -1
  714. package/lib/types/screens/chatOverlay.d.ts.map +1 -1
  715. package/lib/types/screens/chatOverlay.js +3 -0
  716. package/lib/types/screens/sessionSupervisor/model.d.ts +1 -2
  717. package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -1
  718. package/lib/types/screens/sessionSupervisor/model.js +4 -2
  719. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -1
  720. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +7 -3
  721. package/lib/types/settings/definitions.d.ts +93 -0
  722. package/lib/types/settings/definitions.d.ts.map +1 -1
  723. package/lib/types/settings/definitions.js +87 -4
  724. package/lib/types/tips.d.ts.map +1 -1
  725. package/lib/types/tips.js +13 -7
  726. package/lib/types/tuiDisplayPrefs.d.ts +42 -2
  727. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  728. package/lib/types/tuiDisplayPrefs.js +74 -0
  729. package/lib/types/update.d.ts +53 -9
  730. package/lib/types/update.d.ts.map +1 -1
  731. package/lib/types/update.js +103 -46
  732. package/lib/types/utils/bmp.d.ts.map +1 -1
  733. package/lib/types/utils/bmp.js +8 -1
  734. package/lib/types/utils/clipboard.d.ts.map +1 -1
  735. package/lib/types/utils/clipboard.js +25 -6
  736. package/lib/types/utils/credentials.d.ts.map +1 -1
  737. package/lib/types/utils/credentials.js +5 -1
  738. package/lib/types/utils/draftWordBoundary.d.ts +23 -0
  739. package/lib/types/utils/draftWordBoundary.d.ts.map +1 -0
  740. package/lib/types/utils/draftWordBoundary.js +161 -0
  741. package/lib/types/utils/fileTarget.d.ts.map +1 -1
  742. package/lib/types/utils/fileTarget.js +7 -9
  743. package/lib/types/utils/inputPaste.d.ts +62 -0
  744. package/lib/types/utils/inputPaste.d.ts.map +1 -0
  745. package/lib/types/utils/inputPaste.js +111 -0
  746. package/lib/types/utils/intl.d.ts +8 -0
  747. package/lib/types/utils/intl.d.ts.map +1 -1
  748. package/lib/types/utils/intl.js +11 -0
  749. package/lib/types/utils/keymap.d.ts +13 -3
  750. package/lib/types/utils/keymap.d.ts.map +1 -1
  751. package/lib/types/utils/keymap.js +27 -5
  752. package/lib/types/utils/pastedImagePath.d.ts +8 -0
  753. package/lib/types/utils/pastedImagePath.d.ts.map +1 -1
  754. package/lib/types/utils/pastedImagePath.js +29 -5
  755. package/package.json +128 -106
  756. package/presets/liangshen/agent.cordis.yml +4 -1
  757. package/presets/liangshen/preset.yml +1 -1
  758. package/{dsh-ecosystem-spec → tui-profile}/registry/README.md +1 -1
  759. package/{dsh-ecosystem-spec → tui-profile}/registry/registry-0.15.json +1 -1
  760. package/lib/types/minimalMode.d.ts +0 -13
  761. package/lib/types/minimalMode.d.ts.map +0 -1
  762. package/lib/types/minimalMode.js +0 -17
  763. package/node_modules/@deepseek-harness-tui/dsh-auth/LICENSE +0 -21
  764. package/node_modules/@deepseek-harness-tui/dsh-auth/README.md +0 -143
  765. package/node_modules/@deepseek-harness-tui/dsh-auth/cordis.patch.yml +0 -11
  766. package/node_modules/@deepseek-harness-tui/dsh-auth/dsh-plugin.json +0 -44
  767. package/node_modules/@deepseek-harness-tui/dsh-auth/lib/interaction.js +0 -193
  768. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +0 -69
  769. /package/{dsh-ecosystem-spec → tui-profile}/protocols/profile-definitions.d.ts +0 -0
  770. /package/{dsh-ecosystem-spec → tui-profile}/protocols/profile-definitions.js +0 -0
  771. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel-http.d.ts +0 -0
  772. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel-http.js +0 -0
  773. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel.d.ts +0 -0
  774. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-channel.js +0 -0
  775. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-contributions.d.ts +0 -0
  776. /package/{dsh-ecosystem-spec → tui-profile}/protocols/tui-contributions.js +0 -0
  777. /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/decision-events-v1alpha1.json +0 -0
  778. /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/scene-v1alpha1.json +0 -0
  779. /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/settings-section-v1alpha1.json +0 -0
  780. /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/tui-channel-v1alpha1.json +0 -0
  781. /package/{dsh-ecosystem-spec → tui-profile}/registry/contracts/workspace-provider-v1alpha1.json +0 -0
  782. /package/{dsh-ecosystem-spec → tui-profile}/registry/host-descriptor.tui.example.json +0 -0
  783. /package/{dsh-ecosystem-spec → tui-profile}/registry/permissions-0.1.json +0 -0
  784. /package/{dsh-ecosystem-spec → tui-profile}/schemas/conformance-claim.schema.json +0 -0
  785. /package/{dsh-ecosystem-spec → tui-profile}/schemas/effect-ledger-record.schema.json +0 -0
  786. /package/{dsh-ecosystem-spec → tui-profile}/schemas/host-descriptor.schema.json +0 -0
@@ -5,6 +5,7 @@
5
5
  * then interprets sequences as keypresses.
6
6
  */
7
7
  import { Buffer } from 'buffer';
8
+ import { fileURLToPath } from 'node:url';
8
9
  import { isCSIFinal, isCSIIntermediate, isCSIParam, PASTE_END, PASTE_START } from './termio/csi.js';
9
10
  import { createTokenizer } from './termio/tokenize.js';
10
11
  // eslint-disable-next-line no-control-regex
@@ -74,6 +75,20 @@ const SGR_MOUSE_TAIL_RE = /^\[<\d+;\d+;\d+[Mm]$/;
74
75
  // "token is exactly one report" path.
75
76
  // eslint-disable-next-line no-control-regex
76
77
  const SGR_MOUSE_TAIL_PREFIX_RE = /^\[<\d+;\d+;\d+[Mm]/;
78
+ // True prefix of an SGR report body (`[<btn;col;row`), including the
79
+ // ambiguous 2/3-byte heads `[` and `[<`. Mirrors SGR_MOUSE_RE's grammar
80
+ // (three numeric params, final byte excluded) and MUST stay in sync with
81
+ // SGR_MOUSE_RE / parseMouseEvent() — the single SGR grammar source for this
82
+ // family. Only the provenance-gated head hold below uses it; the pre-existing
83
+ // `[<`+digits hold keeps SGR_MOUSE_PREFIX_RE.
84
+ const SGR_MOUSE_HEAD_PREFIX_RE = /^\[(?:<(?:\d+(?:;\d*){0,2})?)?$/;
85
+ // ADR-0007 D4: an ambiguous `[`/`ESC[` head may be claimed this long after a
86
+ // REAL mouse report (kind:'mouse') was parsed — the P2 evidence that the user
87
+ // is actually using the mouse. 5s is the initial value; on-device calibration
88
+ // may retune it (still bounded by the first-capture hold deadline below).
89
+ const MOUSE_REPORT_ACTIVITY_MS = 5000;
90
+ // ADR-0007 D5: gated head holds obey the family's 64-byte bound.
91
+ const MOUSE_HEAD_HOLD_MAX_LENGTH = 64;
77
92
  // How long a held SGR mouse prefix waits for its continuation before being
78
93
  // discarded. The hold sentinel re-arms App's 50ms flush timer, so the hold
79
94
  // faces a discard decision on every parse call — the lifetime is measured
@@ -85,6 +100,14 @@ const SGR_MOUSE_TAIL_PREFIX_RE = /^\[<\d+;\d+;\d+[Mm]/;
85
100
  // the deadline bounds this ambiguity. It runs on every call so continuous
86
101
  // input cannot starve it into a de-facto immortal hold.
87
102
  const MOUSE_TAIL_HOLD_GRACE_MS = 1000;
103
+ // How long an unfinished terminal-reply prefix may be held for its
104
+ // continuation, and how many bytes of it may accumulate. Same caliber as the
105
+ // win32 record hold above: measured from FIRST capture (later flushes must not
106
+ // renew it), deadline checked on every parse call, and an over-limit or
107
+ // expired hold is released by tokenizer.reset() — protocol bytes are dropped
108
+ // rather than leaked into the prompt.
109
+ const TERMINAL_RESPONSE_TAIL_GRACE_MS = 1000;
110
+ const TERMINAL_RESPONSE_MAX_LENGTH = 64;
88
111
  // dwControlKeyState modifier bits (others — NUMLOCK_ON 0x20, CAPSLOCK_ON
89
112
  // 0x80, ENHANCED_KEY 0x100 — are state indicators, not pressed modifiers)
90
113
  const WIN32_CS_ALT = 0x01 | 0x02;
@@ -176,7 +199,54 @@ const XTVERSION_RE = /^\x1bP>\|(.*?)(?:\x07|\x1b\\)$/s;
176
199
  // Button 32=left-drag (0x20 | motion-bit). Plain 0/1/2 = left/mid/right click.
177
200
  // eslint-disable-next-line no-control-regex
178
201
  const SGR_MOUSE_RE = /^\x1b\[<(\d+);(\d+);(\d+)([Mm])$/;
202
+ /**
203
+ * Terminal control sequences that must never survive into a paste payload.
204
+ *
205
+ * A desktop file DROP reaches the TUI as terminal bytes, not as text: Windows
206
+ * Terminal / OpenConsole hand a dropped path over as an OSC 8 hyperlink
207
+ * (`ESC ] 8 ; <params> ; file:///… ST`), and a `DECSET 9001` (win32-input-mode)
208
+ * host additionally decomposes that payload into per-character key records
209
+ * (see the decomposed-paste matcher below). The hyperlink itself is RESTORED
210
+ * to its local path before this hygiene runs (see `createPasteKey` and the
211
+ * OSC 8 block below); what is stripped here is the rest of the protocol
212
+ * shapes a paste payload can carry.
213
+ *
214
+ * Scope (DESIGN D3, narrowed by measurement): COMPLETE OSC sequences only.
215
+ * CSI sequences, bare ESC bytes and C0 control bytes are deliberately KEPT —
216
+ * a bracketed paste's payload is user data. PR #1142 pins that protocol-shaped
217
+ * literal text (`ESC[1;2;3;1A`) survives byte-for-byte
218
+ * (`scripts/verify-win32-input.tsx`), and the whole C0 band belongs to the
219
+ * COMPOSER: every single-line paste ingress flattens C0/C1 to a SPACE
220
+ * (`flattenPasteInline`), so a C0 byte dropped here does not sanitize
221
+ * anything — it deletes the space the composer owes
222
+ * (`scripts/verify-question-paste.tsx` 1a/1c pin that flattening, TAB/CR/LF
223
+ * and DEL included). The wide strip (CSI + residual ESC) proved mutually
224
+ * exclusive with the #1142 contract.
225
+ */
226
+ // eslint-disable-next-line no-control-regex -- deliberate: paste payloads carry terminal sequences
227
+ const OSC_IN_PASTE = /\u001b\][^\u0007\u001b]*(?:\u0007|\u001b\\)/gu;
228
+ /**
229
+ * Strip the protocol frames one paste payload can carry — see
230
+ * {@link OSC_IN_PASTE} for the exact scope.
231
+ *
232
+ * This is the LAST step of the paste choke point, and it is deliberately the
233
+ * only one: no C0/C1 byte is removed here, because removal is lossy where the
234
+ * composer is not (it flattens those bytes to spaces). The registration that
235
+ * briefly added an inert-C0 strip here was the CI regression that
236
+ * `scripts/verify-question-paste.tsx` 1a/1c caught; `verify-paste-drop`
237
+ * re-pins the surviving side of that contract.
238
+ */
239
+ function cleanPastePayload(content) {
240
+ return content.replace(OSC_IN_PASTE, '');
241
+ }
179
242
  function createPasteKey(content) {
243
+ // Order is a contract (D6/D1): a complete OSC 8 drop frame is RESTORED to
244
+ // its local path BEFORE the payload hygiene — hygiene strips the very OSC
245
+ // frame the drop is encoded in. Every paste path goes through this ONE
246
+ // choke point (VT bracketed paste, the decomposed win32 paste, and both
247
+ // flush paths), so no caller has to remember the order.
248
+ const dropPath = osc8DropPath(content);
249
+ const text = dropPath === null ? cleanPastePayload(content) : pastePayloadForPath(dropPath);
180
250
  return {
181
251
  kind: 'key',
182
252
  name: '',
@@ -186,11 +256,143 @@ function createPasteKey(content) {
186
256
  shift: false,
187
257
  option: false,
188
258
  super: false,
189
- sequence: content,
190
- raw: content,
259
+ sequence: text,
260
+ raw: text,
191
261
  isPasted: true,
192
262
  };
193
263
  }
264
+ // -- OSC 8 drop frames -------------------------------------------------------
265
+ //
266
+ // Windows hands a dropped file over as an OSC 8 hyperlink:
267
+ //
268
+ // ESC ] 8 ; <params> ; <URI> ( BEL | ST )
269
+ //
270
+ // `params` is the hyperlink attribute list (`id=16:42`, or empty for the form
271
+ // this TUI itself emits) and the URI is everything after the SECOND `;` — a
272
+ // URI may legally contain `;`, params may not.
273
+ //
274
+ // A frame is claimed in the fixed decision order (D6) by
275
+ // `claimProtocolSequence`, and — when the frame set was already reassembled
276
+ // into one paste body (win32 decomposed paste / VT bracketed paste) — by
277
+ // `createPasteKey` before the payload hygiene runs.
278
+ /** OSC 8 introducer: `ESC ] 8 ;`. */
279
+ const OSC8_FRAME_PREFIX = '\x1b]8;';
280
+ /**
281
+ * Longest OSC 8 frame the record accumulator will hold. A drop URI is a local
282
+ * path plus percent-encoding, and the composer's own pasted-path cap is 4096
283
+ * chars (`MAX_PASTED_PATH_CHARS`), so anything beyond this cannot become a
284
+ * usable path and must not extend a hold indefinitely.
285
+ */
286
+ const OSC8_FRAME_MAX_LENGTH = 4096;
287
+ /**
288
+ * Read one OSC 8 frame at `offset`. Returns null when the bytes there are not
289
+ * an OSC 8 frame, or when the URI is followed by anything other than a frame
290
+ * terminator (BEL 0x07 / ST `ESC \`) or the start of the next frame.
291
+ *
292
+ * The "next frame" shape matters because the win32 record translator drops
293
+ * BEL outright (a synthesized Uc=7 record carries no key meaning), so a
294
+ * decomposed drop can lose its BEL terminator before the payload is
295
+ * reassembled. A lone frame with neither a terminator nor a successor stays
296
+ * unclaimed: the tail of a truncated stream must not be read as a path.
297
+ */
298
+ function readOsc8Frame(payload, offset) {
299
+ if (!payload.startsWith(OSC8_FRAME_PREFIX, offset))
300
+ return null;
301
+ const paramsEnd = payload.indexOf(';', offset + OSC8_FRAME_PREFIX.length);
302
+ if (paramsEnd === -1)
303
+ return null;
304
+ const uriStart = paramsEnd + 1;
305
+ let uriEnd = uriStart;
306
+ while (uriEnd < payload.length && payload[uriEnd] !== '\u0007' && payload[uriEnd] !== '\x1b') {
307
+ uriEnd++;
308
+ }
309
+ const uri = payload.slice(uriStart, uriEnd);
310
+ if (payload[uriEnd] === '\u0007')
311
+ return { uri, end: uriEnd + 1 };
312
+ if (payload.startsWith('\x1b\\', uriEnd))
313
+ return { uri, end: uriEnd + 2 };
314
+ if (payload.startsWith(OSC8_FRAME_PREFIX, uriEnd))
315
+ return { uri, end: uriEnd };
316
+ return null;
317
+ }
318
+ /**
319
+ * The local path a payload restores to, or null when the payload is NOT a
320
+ * pure OSC 8 frame set or its URI is not a decodable LOCAL file URL. Called
321
+ * with one complete sequence (the decision chain) or with a whole reassembled
322
+ * paste body (the choke point).
323
+ *
324
+ * Fail-closed rules (DESIGN D4 / AC-4):
325
+ * - only `file://` URIs are restored — other schemes fall through to the
326
+ * normal response/prose handling;
327
+ * - a URI carrying whitespace is multiple tokens, not one path, and is
328
+ * refused (the same conservative contract as `parsePastedImagePath`);
329
+ * - more than one distinct URI is a multi-file drop, which v1 does not
330
+ * cover: fail closed rather than silently keep only the first file;
331
+ * - a remote authority (`file://server/share/…`) or a decode failure is
332
+ * never silently reinterpreted as a local path;
333
+ * - a payload with any trailing non-OSC-8 bytes is NOT a drop and is left to
334
+ * the hygiene path, so prose that merely quotes a link keeps its text.
335
+ */
336
+ function osc8DropPath(payload) {
337
+ let offset = 0;
338
+ let uri;
339
+ while (offset < payload.length) {
340
+ const frame = readOsc8Frame(payload, offset);
341
+ if (frame === null || frame.end <= offset)
342
+ return null;
343
+ if (frame.uri !== '') {
344
+ if (uri !== undefined && uri !== frame.uri)
345
+ return null;
346
+ uri = frame.uri;
347
+ }
348
+ offset = frame.end;
349
+ }
350
+ if (uri === undefined || /\s/u.test(uri))
351
+ return null;
352
+ return localPathOfFileUri(uri);
353
+ }
354
+ /**
355
+ * `file://` URI → local path (percent-escapes decoded by `fileURLToPath`), or
356
+ * null when the URI is not a local file URL or decoding fails.
357
+ */
358
+ function localPathOfFileUri(uri) {
359
+ if (!/^file:\/\//iu.test(uri))
360
+ return null;
361
+ let url;
362
+ try {
363
+ url = new URL(uri);
364
+ }
365
+ catch {
366
+ return null;
367
+ }
368
+ // A non-empty authority is a remote share (UNC): refuse it instead of
369
+ // letting `fileURLToPath` reinterpret it as a local path on hosts that
370
+ // ignore the authority. `localhost` is this machine.
371
+ if (url.host !== '' && url.host !== 'localhost')
372
+ return null;
373
+ try {
374
+ return fileURLToPath(url);
375
+ }
376
+ catch {
377
+ return null;
378
+ }
379
+ }
380
+ /**
381
+ * The paste payload for a restored drop path. `parsePastedImagePath` accepts
382
+ * exactly one whitespace-free token, or the whole payload quoted — and a drop
383
+ * is ONE unambiguous file, so a path containing whitespace is handed over in
384
+ * the quoted single-token form the composer already documents
385
+ * (`@"my dir/a.ts"` is the mention spelling of the same shape). A path no
386
+ * quote can represent (a literal `"`) stays bare: the composer then inserts
387
+ * it verbatim, which is the pre-existing fail-closed behaviour for ambiguous
388
+ * tokens. Windows paths cannot contain `"`, so on this platform the quoted
389
+ * form is exactly the decoded path.
390
+ */
391
+ function pastePayloadForPath(path) {
392
+ if (!/[\s"]/u.test(path))
393
+ return path;
394
+ return path.includes('"') ? path : `"${path}"`;
395
+ }
194
396
  /** DECRPM status values (response to DECRQM) */
195
397
  export const DECRPM_STATUS = {
196
398
  NOT_RECOGNIZED: 0,
@@ -199,6 +401,23 @@ export const DECRPM_STATUS = {
199
401
  PERMANENTLY_SET: 3,
200
402
  PERMANENTLY_RESET: 4,
201
403
  };
404
+ // Unfinished prefixes of every reply shape parseTerminalResponse() knows.
405
+ // MUST be kept in sync with the pattern list above: a reply type added there
406
+ // needs an alternative here too, or a ConPTY-split tail of that shape is
407
+ // released at the flush and leaks into the prompt as literal text.
408
+ // ESC [ ? … DA1 / kitty flags / DECXCPR / DECRPM
409
+ // ESC [ ? … $ DECRPM's `$y` intermediate byte
410
+ // ESC [ > … DA2
411
+ // ESC [ digits [; digits] DSR (row;col) and XTWINOPS pixel-size replies
412
+ // ESC [ … $ a `$`-intermediate CSI awaiting its final byte
413
+ // ESC P > | … XTVERSION (DCS)
414
+ // Only PREFIXES are matched; completeness is always decided by
415
+ // parseTerminalResponse(), never by this regex. Introducer-only prefixes
416
+ // (`ESC [`, `ESC P`) are deliberately excluded: they are indistinguishable
417
+ // from ordinary key input, and holding the bare `ESC [` swallowed
418
+ // `ESC[`+letter input in the #1073 review.
419
+ // eslint-disable-next-line no-control-regex
420
+ const TERMINAL_RESPONSE_PREFIX_RE = /^(?:\x1b\[\?[\d;]*\$?|\x1b\[>[\d;]*|\x1b\[[\d;]*\$|\x1b\[\d+(?:;\d+)*|\x1bP>\|)/;
202
421
  /**
203
422
  * Try to recognize a sequence token as a terminal response.
204
423
  * Returns null if the sequence is not a known response pattern
@@ -277,6 +496,90 @@ function parseTerminalResponse(s) {
277
496
  }
278
497
  return null;
279
498
  }
499
+ // Unfinished prefixes of the reply shapes parseTerminalResponse() accepts,
500
+ // paired with the response type they can still complete into. This is the
501
+ // shape half of the claim gate: a held reply fragment is only joined to its
502
+ // continuation while one of these types is actually expected by a pending
503
+ // query (terminalExpectedResponseTypes). The patterns are deliberately
504
+ // tighter than TERMINAL_RESPONSE_PREFIX_RE: `?` + two params can still grow
505
+ // into a DA1 but never into a DECRPM (`?mode;status$y` has exactly two
506
+ // params), so a DA1-shaped fragment is released as literal text as soon as
507
+ // only DECRPM is in flight. `parseTerminalResponse` remains the only
508
+ // authority on completeness.
509
+ // eslint-disable-next-line no-control-regex
510
+ const INCOMPLETE_RESPONSE_PREFIXES = [
511
+ // DA1: any `?`-parameter list (including the bare introducer).
512
+ ['da1', /^\x1b\[\?[\d;]*$/],
513
+ // DECRPM: `?mode;status` before the `$` intermediate, then the same before
514
+ // `$` + final.
515
+ ['decrpm', /^\x1b\[\?\d*(?:;\d*)?$/],
516
+ ['decrpm', /^\x1b\[\?\d+;\d+\$$/],
517
+ // Kitty flags and DECXCPR: one `?` + at most two numeric params.
518
+ ['kittyKeyboard', /^\x1b\[\?\d*$/],
519
+ ['cursorPosition', /^\x1b\[\?\d*(?:;\d*)?$/],
520
+ // DA2: any `>`-parameter list.
521
+ ['da2', /^\x1b\[>[\d;]*$/],
522
+ // XTWINOPS: `6;h;w t` / `4;h;w t`, up to the final byte.
523
+ ['terminalPixelSize', /^\x1b\[[46](?:;\d*){0,2}$/],
524
+ // XTVERSION: `P>` can still gain `|` and a payload; ST's ESC may flush.
525
+ ['xtversion', /^\x1bP>(?:\|[^\x1b]*(?:\x1b)?)?$/],
526
+ ];
527
+ /**
528
+ * Response types an unfinished reply-shaped candidate could still complete
529
+ * into, judged from its introducer and parameter bytes alone. Empty means
530
+ * the bytes cannot become a known reply (either a final byte already ended a
531
+ * different sequence, or the shape is unknown) and must stay literal.
532
+ * @param candidate - the inbound bytes, with the ESC introducer synthesized.
533
+ * @returns the types a continuation could still produce.
534
+ */
535
+ function possibleResponseTypes(candidate) {
536
+ const types = new Set();
537
+ for (const [type, pattern] of INCOMPLETE_RESPONSE_PREFIXES) {
538
+ if (pattern.test(candidate))
539
+ types.add(type);
540
+ }
541
+ return types;
542
+ }
543
+ /**
544
+ * Whether an unfinished candidate can still become a response the host is
545
+ * currently waiting for. Both halves are required: an in-flight query of the
546
+ * matching type, and a shape that can still produce it.
547
+ */
548
+ function expectsResponseType(candidate, expected) {
549
+ for (const type of possibleResponseTypes(candidate)) {
550
+ if (expected.has(type))
551
+ return true;
552
+ }
553
+ return false;
554
+ }
555
+ /**
556
+ * Find the shortest complete terminal response at the head of a post-flush
557
+ * tail whose introducer ESC was already flushed as a key. The caller
558
+ * synthesizes the ESC back; `parseTerminalResponse` decides completeness, so
559
+ * only a type the host expects is claimed and trailing bytes are left for
560
+ * ordinary parsing.
561
+ * @param tail - fragment without its ESC introducer.
562
+ * @param expected - response types of the queries still awaiting a reply.
563
+ * @returns the claimed response and how many bytes of `tail` it consumed, or
564
+ * undefined when no expected reply completes at the head.
565
+ */
566
+ function claimExpectedResponseHead(tail, expected) {
567
+ const limit = Math.min(tail.length, TERMINAL_RESPONSE_MAX_LENGTH);
568
+ for (let consumed = 2; consumed <= limit; consumed++) {
569
+ const head = '\x1b' + tail.slice(0, consumed);
570
+ const response = parseTerminalResponse(head);
571
+ if (response) {
572
+ return expected.has(response.type)
573
+ ? { response, consumed }
574
+ : undefined;
575
+ }
576
+ // A head that can no longer become an expected reply ends the scan:
577
+ // longer candidates extend a shape that is already closed or unknown.
578
+ if (!expectsResponseType(head, expected))
579
+ return undefined;
580
+ }
581
+ return undefined;
582
+ }
280
583
  function splitNumericParams(params) {
281
584
  if (!params)
282
585
  return [];
@@ -630,50 +933,85 @@ function synthesizedWin32Char(key) {
630
933
  }
631
934
  return win32RecordChar(key);
632
935
  }
633
- function parseReassembledWin32Protocol(sequence) {
936
+ /**
937
+ * The ONE decision chain for a complete reassembled terminal sequence. The
938
+ * order is a contract (DESIGN D6) and must not be permuted:
939
+ *
940
+ * 1. OSC 8 drop frame — a dropped file's hyperlink. It must win over the
941
+ * OSC response shape in step 2, which also matches `ESC ] 8 ; …` and
942
+ * used to swallow the whole link as a bogus terminal reply (#1067).
943
+ * 2. terminal response — DECRPM/DA/OSC replies to our own queries
944
+ * (#1177's type-bound claiming).
945
+ * 3. mouse report — SGR (1006), with the X10 report as the legacy
946
+ * compatibility fallback.
947
+ *
948
+ * A sequence that falls through all three is a record body or a keypress, and
949
+ * only the caller can tell which (`parseReassembledWin32Protocol` for records,
950
+ * the token loop for VT input), so that dispatch stays with the caller.
951
+ */
952
+ function claimProtocolSequence(sequence) {
953
+ const dropPath = osc8DropPath(sequence);
954
+ if (dropPath !== null) {
955
+ // Hand the local path to the ONE paste choke point (D2), so the existing
956
+ // image stage / `@` reference pipeline sees a normal paste.
957
+ return createPasteKey(pastePayloadForPath(dropPath));
958
+ }
634
959
  const response = parseTerminalResponse(sequence);
635
960
  if (response)
636
961
  return { kind: 'response', sequence, response };
637
- const mouse = parseMouseEvent(sequence) ?? parseX10MouseEvent(sequence);
638
- if (mouse)
639
- return mouse;
962
+ return parseMouseEvent(sequence) ?? parseX10MouseEvent(sequence);
963
+ }
964
+ function parseReassembledWin32Protocol(sequence) {
965
+ // Fixed decision order (D6) — see claimProtocolSequence.
966
+ const claimed = claimProtocolSequence(sequence);
967
+ if (claimed)
968
+ return claimed;
640
969
  if (SGR_MOUSE_RE.test(sequence) || (sequence.length === 6 && sequence.startsWith('\x1b[M'))) {
641
970
  return parseKeypress(sequence);
642
971
  }
643
972
  return null;
644
973
  }
645
- function feedWin32Protocol(state, key) {
646
- const ch = synthesizedWin32Char(key);
647
- if (state.held.length === 0) {
648
- if (ch !== '\x1b')
649
- return [key];
650
- state.held.push(key);
651
- state.sequence = ch;
652
- return [];
653
- }
654
- if (ch === undefined) {
655
- const held = state.held;
656
- state.held = [];
657
- state.sequence = '';
658
- return [...held, ...feedWin32Protocol(state, key)];
659
- }
660
- state.held.push(key);
661
- state.sequence += ch;
662
- const sequence = state.sequence;
974
+ /** Release every key held for the current candidate sequence. */
975
+ function releaseWin32ProtocolHold(state) {
976
+ const held = state.held;
977
+ state.held = [];
978
+ state.sequence = '';
979
+ return held;
980
+ }
981
+ /**
982
+ * Resolve one accumulated protocol candidate. An array is the dispatch result
983
+ * (empty while the frame is still incomplete); null means the bytes are not a
984
+ * protocol shape we may hold, and the caller releases its keys.
985
+ *
986
+ * Order and bounds are the existing contract (#1142/#1177) with the OSC 8
987
+ * drop frame (D1) added FIRST: under win32-input-mode a drop can arrive as
988
+ * per-character synthesized records without bracketed-paste markers, so the
989
+ * hyperlink has to be reassembled here, ahead of any response/mouse claim.
990
+ * Holding past the introducer also keeps it away from
991
+ * `parseTerminalResponse`, whose OSC shape would otherwise take
992
+ * `ESC ] 8 ; …` as a bogus reply and swallow the dropped path (#1067).
993
+ * The OSC terminator is BEL or ST with its own length cap: a BEL record has
994
+ * no key meaning and is dropped by the translator, so a decomposed frame can
995
+ * lose its BEL — such a frame is released by the flush bound like any other
996
+ * abandoned sequence.
997
+ */
998
+ function resolveWin32ProtocolCandidate(state, sequence, ch) {
663
999
  if (sequence === '\x1b' || sequence === '\x1b[')
664
1000
  return [];
665
- if (!sequence.startsWith('\x1b[') || sequence.length > 64) {
666
- const held = state.held;
667
- state.held = [];
668
- state.sequence = '';
669
- return held;
1001
+ if (sequence.startsWith('\x1b]')) {
1002
+ const terminated = sequence.endsWith('\u0007') || sequence.endsWith('\x1b\\');
1003
+ if (!terminated)
1004
+ return sequence.length <= OSC8_FRAME_MAX_LENGTH ? [] : null;
1005
+ const held = releaseWin32ProtocolHold(state);
1006
+ const parsed = parseReassembledWin32Protocol(sequence);
1007
+ return parsed ? [parsed] : held;
670
1008
  }
1009
+ if (!sequence.startsWith('\x1b[') || sequence.length > 64)
1010
+ return null;
671
1011
  if (sequence.startsWith('\x1b[M')) {
672
1012
  if (sequence.length < 6)
673
1013
  return [];
674
- const held = state.held;
675
- state.held = [];
676
- state.sequence = '';
1014
+ const held = releaseWin32ProtocolHold(state);
677
1015
  const parsed = sequence.length === 6
678
1016
  ? parseReassembledWin32Protocol(sequence)
679
1017
  : null;
@@ -683,16 +1021,29 @@ function feedWin32Protocol(state, key) {
683
1021
  if ((code >= 0x20 && code <= 0x3f))
684
1022
  return [];
685
1023
  if (code >= 0x40 && code <= 0x7e) {
686
- const held = state.held;
687
- state.held = [];
688
- state.sequence = '';
1024
+ const held = releaseWin32ProtocolHold(state);
689
1025
  const parsed = parseReassembledWin32Protocol(sequence);
690
1026
  return parsed ? [parsed] : held;
691
1027
  }
692
- const held = state.held;
693
- state.held = [];
694
- state.sequence = '';
695
- return held;
1028
+ return null;
1029
+ }
1030
+ function feedWin32Protocol(state, key) {
1031
+ const ch = synthesizedWin32Char(key);
1032
+ if (state.held.length === 0) {
1033
+ if (ch !== '\x1b')
1034
+ return [key];
1035
+ state.held.push(key);
1036
+ state.sequence = ch;
1037
+ return [];
1038
+ }
1039
+ if (ch === undefined) {
1040
+ const held = releaseWin32ProtocolHold(state);
1041
+ return [...held, ...feedWin32Protocol(state, key)];
1042
+ }
1043
+ state.held.push(key);
1044
+ state.sequence += ch;
1045
+ return (resolveWin32ProtocolCandidate(state, state.sequence, ch) ??
1046
+ releaseWin32ProtocolHold(state));
696
1047
  }
697
1048
  function feedWin32Input(pasteState, protocolState, key) {
698
1049
  const inputs = [];
@@ -762,6 +1113,22 @@ export function parseMultipleKeypresses(prevState, input = '') {
762
1113
  const win32HoldAllowed = (prevState.win32Capable ?? true) && win32InputMode;
763
1114
  const isRecordPrefix = (value) => !inPaste && WIN32_INPUT_PREFIX_RE.test(value) &&
764
1115
  (WIN32_INPUT_BODY_PREFIX_RE.test(value) || win32HoldAllowed);
1116
+ // Evidence is the live query lifecycle, not a recency window: the host
1117
+ // injects the response types of the queries still awaiting an answer, and
1118
+ // a reply is only ever claimed when its shape can complete into one of
1119
+ // them. Read-only here and re-read on every call, so a query that settles
1120
+ // (matched reply, sentinel drain, dispose) stops authorizing immediately.
1121
+ const expectedResponseTypes = new Set(prevState.terminalExpectedResponseTypes ?? []);
1122
+ const hasQueryEvidence = expectedResponseTypes.size > 0;
1123
+ // A buffer that is already reply-specific (TERMINAL_RESPONSE_PREFIX_RE) is
1124
+ // eligible to be held open for its continuation — but only with the
1125
+ // host-injected in-flight-query evidence (D1) AND a shape/type match (D2).
1126
+ // Record framing is judged first (D6): every numeric CSI prefix the record
1127
+ // branch can frame belongs to it, and this predicate never re-labels one.
1128
+ // Without evidence nothing is held, so a direct caller that never injects
1129
+ // it keeps the pre-gate behavior (D7 / AC-3). Paste payloads stay literal,
1130
+ // exactly like the record branch above.
1131
+ const isResponsePrefix = (value) => !isRecordPrefix(value) && !inPaste && TERMINAL_RESPONSE_PREFIX_RE.test(value);
765
1132
  if (win32InputStartedAt !== undefined && now - win32InputStartedAt >= WIN32_INPUT_GRACE_MS) {
766
1133
  tokenizer.reset();
767
1134
  win32InputStartedAt = undefined;
@@ -769,13 +1136,31 @@ export function parseMultipleKeypresses(prevState, input = '') {
769
1136
  if (win32EscFlushedAt !== undefined && now - win32EscFlushedAt >= WIN32_INPUT_GRACE_MS) {
770
1137
  win32EscFlushedAt = undefined;
771
1138
  }
1139
+ // Tracks the win32 ESC-flush recovery below: a reconstructed `ESC` byte is
1140
+ // NOT transport evidence for the single-read `ESC[` claim (F-2) — the ESC
1141
+ // came from an earlier read's flush, i.e. exactly the two-stroke typing the
1142
+ // P2 gate exists for.
1143
+ let win32EscPrepended = false;
1144
+ // Same deadline discipline for the reply-prefix hold: first capture only,
1145
+ // checked on every call (not just on flush, so continuous input cannot
1146
+ // starve it), and released by resetting the tokenizer — bytes dropped, the
1147
+ // same caliber as the win32 record hold, never leaked into the prompt.
1148
+ let terminalResponseHoldAt = prevState.terminalResponseHoldAt;
1149
+ if (terminalResponseHoldAt !== undefined &&
1150
+ now - terminalResponseHoldAt >= TERMINAL_RESPONSE_TAIL_GRACE_MS) {
1151
+ if (isResponsePrefix(tokenizer.buffer()))
1152
+ tokenizer.reset();
1153
+ terminalResponseHoldAt = undefined;
1154
+ }
772
1155
  if (inputString && win32EscFlushedAt !== undefined) {
773
1156
  // Only the immediate continuation of an actual ESC flush may acquire
774
1157
  // a missing introducer. Never capture arbitrary '['-led user text.
775
1158
  if (prevState.mode !== 'IN_PASTE' && tokenizer.buffer() === '' &&
776
1159
  /^\[(?:[\d;]|$)/.test(inputString) &&
777
- (WIN32_INPUT_BODY_PREFIX_RE.test('\x1b' + inputString) || win32HoldAllowed))
1160
+ (WIN32_INPUT_BODY_PREFIX_RE.test('\x1b' + inputString) || win32HoldAllowed)) {
778
1161
  inputString = '\x1b' + inputString;
1162
+ win32EscPrepended = true;
1163
+ }
779
1164
  win32EscFlushedAt = undefined;
780
1165
  }
781
1166
  const pending = tokenizer.buffer();
@@ -796,14 +1181,61 @@ export function parseMultipleKeypresses(prevState, input = '') {
796
1181
  inputString = inputString.slice(continuation.length);
797
1182
  }
798
1183
  }
799
- let deferFlush = isFlush && isRecordPrefix(tokenizer.buffer());
800
- if (deferFlush && tokenizer.buffer().length > WIN32_INPUT_MAX_LENGTH) {
1184
+ // Two independent reasons to keep the tokenizer's buffer across a flush:
1185
+ // the #1142 record frame (win32), and an unfinished reply prefix when the
1186
+ // host reports a query in flight (D1 × D2). Each keeps its own bound (D6);
1187
+ // an over-limit buffer is dropped exactly like #1142 drops an oversized
1188
+ // record frame.
1189
+ const recordPrefix = isRecordPrefix(tokenizer.buffer());
1190
+ // Evidence is re-read on every call, never latched: once the host stops
1191
+ // reporting a query in flight — or the buffer's shape can no longer
1192
+ // complete into a response type it expects — the claim ends. A captured
1193
+ // hold is then dropped by the release branch below instead of being
1194
+ // flushed into the body (no evidence, no claim).
1195
+ const claimsResponsePrefix = hasQueryEvidence &&
1196
+ isResponsePrefix(tokenizer.buffer()) &&
1197
+ expectsResponseType(tokenizer.buffer(), expectedResponseTypes);
1198
+ let deferFlush = isFlush && (recordPrefix || claimsResponsePrefix);
1199
+ if (deferFlush &&
1200
+ tokenizer.buffer().length >
1201
+ (recordPrefix ? WIN32_INPUT_MAX_LENGTH : TERMINAL_RESPONSE_MAX_LENGTH)) {
801
1202
  tokenizer.reset();
802
1203
  deferFlush = false;
803
1204
  }
1205
+ // Tokenizer buffer entering THIS call — captured before feed so the
1206
+ // single-read `ESC[` evidence below cannot be granted to a head whose ESC
1207
+ // or `[` was buffered by an earlier call.
1208
+ const pendingBeforeFeed = tokenizer.buffer();
1209
+ // First capture only: later flushes (App re-arms its timer while
1210
+ // `incomplete` is set) must not renew the deadline.
1211
+ if (deferFlush && claimsResponsePrefix)
1212
+ terminalResponseHoldAt ??= now;
1213
+ // A captured hold can lose its authorization before the flush: the host
1214
+ // re-injects the pending set on every call, so a query that settles (or a
1215
+ // shape that evolves out of the expected types) turns claimsResponsePrefix
1216
+ // false. Falling back to tokenizer.flush() there would hand the buffered
1217
+ // reply prefix to parseKeypress() as a key/literal text. Drop it like the
1218
+ // expiry path instead and end the hold. Both the captured timestamp and
1219
+ // the reply shape are required: bytes that were never held keep the
1220
+ // pre-gate release semantics (AC-3).
1221
+ const releaseResponseHold = isFlush &&
1222
+ terminalResponseHoldAt !== undefined &&
1223
+ isResponsePrefix(tokenizer.buffer()) &&
1224
+ !claimsResponsePrefix;
1225
+ if (releaseResponseHold) {
1226
+ tokenizer.reset();
1227
+ terminalResponseHoldAt = undefined;
1228
+ }
804
1229
  const tokens = isFlush
805
- ? deferFlush ? [] : tokenizer.flush()
1230
+ ? deferFlush || releaseResponseHold ? [] : tokenizer.flush()
806
1231
  : tokenizer.feed(inputString);
1232
+ // ADR-0007 D4 addendum (F-2): grant the ambiguous `ESC[` head P1-only claim
1233
+ // when both bytes were buffered inside this one non-flush read from an empty
1234
+ // buffer. Set here, consumed by the flush call that emits it, then cleared
1235
+ // (a flush feeds nothing). Cross-read accumulation leaves it unset → P2.
1236
+ const mouseHeadSingleRead = !isFlush && !win32EscPrepended && pendingBeforeFeed === '' && tokenizer.buffer() === '\x1b['
1237
+ ? '\x1b['
1238
+ : undefined;
807
1239
  if (isFlush && !inPaste && tokens.some(token => token.value === '\x1b')) {
808
1240
  win32EscFlushedAt = now;
809
1241
  }
@@ -839,16 +1271,112 @@ export function parseMultipleKeypresses(prevState, input = '') {
839
1271
  // App's 50ms flush timer, so a per-call flag would still let the SECOND
840
1272
  // quiet flush kill a press split by >~100ms (observed over SSH).
841
1273
  let mouseTailHoldAt = prevState.mouseTailHoldAt;
842
- // Hard deadline, checked at the top of EVERY call — not only on flush.
843
- // Continuous input keeps cancelling and re-arming App's 50ms flush timer,
844
- // so a flush-only check can starve while the user types: a hold past its
845
- // grace would then still merge a late `;34M` (or plain digits) into a
846
- // phantom report.
847
- if (mouseTailHold !== undefined &&
848
- Date.now() - (mouseTailHoldAt ?? 0) > MOUSE_TAIL_HOLD_GRACE_MS) {
1274
+ const terminalResponseTailAfterEscFlushAt = prevState.terminalResponseTailAfterEscFlushAt;
1275
+ // Fragment of a reply whose introducer ESC was already flushed as a key,
1276
+ // held across calls until the rest of its shape arrives (see
1277
+ // KeyParseState.terminalResponseReattachTail).
1278
+ let terminalResponseReattachTail = prevState.terminalResponseReattachTail;
1279
+ // Re-attach (D3b, handed over from #796): a lone Escape was genuinely
1280
+ // flushed, so a text token inside the window may be the rest of a reply
1281
+ // that lost its introducer. Claim it only inside the bounded window AND
1282
+ // with a query in flight whose expected response type the shape can still
1283
+ // complete into — the missing gate CodeRabbit flagged on #796.
1284
+ // parseTerminalResponse() remains the completeness authority; bytes whose
1285
+ // shape matches no expected type stay literal, and without injected
1286
+ // evidence this branch never fires (AC-3).
1287
+ // Bounded by TIME, not by "the next call": a burst can interleave a chunk
1288
+ // between the flush and the tail (another reply answering an earlier query,
1289
+ // or a keystroke), and the real-ConPTY dry run leaked the tail when that
1290
+ // chunk closed the window early.
1291
+ const mayRecoverTerminalResponseTail = hasQueryEvidence &&
1292
+ terminalResponseTailAfterEscFlushAt !== undefined &&
1293
+ Date.now() - terminalResponseTailAfterEscFlushAt <= TERMINAL_RESPONSE_TAIL_GRACE_MS;
1294
+ // Carry that stamp under the same discipline while it is still in-window,
1295
+ // and drop it once it expires (or the evidence is gone) so no stale window
1296
+ // survives — the #1142 record window above carries its own stamp the same way.
1297
+ const carryTailAfterEscFlush = mayRecoverTerminalResponseTail
1298
+ ? terminalResponseTailAfterEscFlushAt
1299
+ : undefined;
1300
+ // A closed window also drops the fragment it was holding: those bytes
1301
+ // cannot be joined to a reply any more, and protocol-shaped bytes are
1302
+ // dropped rather than shown.
1303
+ if (carryTailAfterEscFlush === undefined)
1304
+ terminalResponseReattachTail = undefined;
1305
+ // Claim provenance of the current hold (see KeyParseState docs): true =
1306
+ // gated (RELEASE on expiry/overflow), false = legacy silent discard,
1307
+ // undefined = no hold.
1308
+ let mouseTailHoldReleasable = prevState.mouseTailHoldReleasable;
1309
+ let lastMouseReportAt = prevState.lastMouseReportAt;
1310
+ const clearMouseTailHold = () => {
849
1311
  mouseTailHold = undefined;
850
1312
  mouseTailHoldAt = undefined;
851
- }
1313
+ mouseTailHoldReleasable = undefined;
1314
+ };
1315
+ // Evidence gate for the SGR head family (ADR-0007 D2/D4). App injects
1316
+ // mouseReportingActive on every processInput; absent keeps direct callers on
1317
+ // the pre-gate `[<`+digits hold, explicit `false` claims nothing, explicit
1318
+ // `true` also allows the ambiguous `[`/`[<` heads — with `[`/`ESC[` needing
1319
+ // a real report inside MOUSE_REPORT_ACTIVITY_MS (P2 evidence).
1320
+ const mouseClaim = prevState.mouseReportingActive === true
1321
+ ? 'gated'
1322
+ : prevState.mouseReportingActive === false
1323
+ ? 'closed'
1324
+ : 'legacy';
1325
+ const mouseReportActivityLive = () => lastMouseReportAt !== undefined &&
1326
+ now - lastMouseReportAt <= MOUSE_REPORT_ACTIVITY_MS;
1327
+ /**
1328
+ * Decide whether `body` (a token with any leading ESC stripped) is an SGR
1329
+ * report head this parser may hold. `undefined` = ordinary input (no claim);
1330
+ * otherwise `releasable` says whether invalidation RELEASES the bytes
1331
+ * (gated claim) or silently discards them (legacy shape-specific claim).
1332
+ * `singleReadHead` = the `ESC[` body was formed inside ONE read (transport
1333
+ * evidence, ADR-0007 D4 addendum) and therefore counts as P1 evidence.
1334
+ */
1335
+ const claimSgrHead = (body, singleReadHead = false) => {
1336
+ if (mouseClaim === 'closed')
1337
+ return undefined;
1338
+ if (SGR_MOUSE_PREFIX_RE.test(body))
1339
+ return { releasable: mouseClaim === 'gated' };
1340
+ // The ambiguous 1/2-byte heads exist only for the gated family; legacy
1341
+ // callers keep the pre-gate behavior (plain text).
1342
+ if (mouseClaim !== 'gated')
1343
+ return undefined;
1344
+ if (body === '[<')
1345
+ return { releasable: true };
1346
+ // `[`/`ESC[` (D4): without a recent real report this stays ordinary text
1347
+ // (the pre-fix Esc+`[` behavior) unless both bytes arrived in one read —
1348
+ // human two-stroke typing cannot do that (F-2).
1349
+ if (body === '[')
1350
+ return mouseReportActivityLive() || singleReadHead ? { releasable: true } : undefined;
1351
+ return undefined;
1352
+ };
1353
+ /**
1354
+ * Single invalidation/RELEASE entry (F-6): a gated hold is replayed as
1355
+ * ordinary keys in arrival order (ADR-0007 D5); a legacy hold keeps the
1356
+ * pre-gate silent discard. Every invalidation, including an unrelated
1357
+ * protocol boundary, funnels through here; only successful completion of
1358
+ * the held report clears it without replay.
1359
+ */
1360
+ const releaseHeldMouseHead = () => {
1361
+ if (mouseTailHold === undefined)
1362
+ return;
1363
+ if (mouseTailHoldReleasable === true)
1364
+ keys.push(parseKeypress(mouseTailHold));
1365
+ clearMouseTailHold();
1366
+ };
1367
+ /** Hard deadline + gated size bound, checked at the top of EVERY call. */
1368
+ const shouldReleaseHold = () => mouseTailHold !== undefined &&
1369
+ (now - (mouseTailHoldAt ?? 0) > MOUSE_TAIL_HOLD_GRACE_MS ||
1370
+ (mouseTailHoldReleasable === true &&
1371
+ mouseTailHold.length > MOUSE_HEAD_HOLD_MAX_LENGTH));
1372
+ // Hard deadline + size bound, checked at the top of EVERY call — not only on
1373
+ // flush. Continuous input keeps cancelling and re-arming App's 50ms flush
1374
+ // timer, so a flush-only check can starve while the user types: a hold past
1375
+ // its grace would then still merge a late `;34M` (or plain digits) into a
1376
+ // phantom report. A gated hold RELEASES its bytes as ordinary keys instead
1377
+ // of dropping them (ADR-0007 D5); legacy holds keep the pre-gate discard.
1378
+ if (shouldReleaseHold())
1379
+ releaseHeldMouseHead();
852
1380
  // Mutable token queue: a text token that starts with the completion of
853
1381
  // the CURRENT hold but carries trailing bytes is split in-place — the
854
1382
  // completion tail is consumed now, the suffix is pushed back for the
@@ -861,15 +1389,43 @@ export function parseMultipleKeypresses(prevState, input = '') {
861
1389
  for (let qi = 0; qi < tokenQueue.length; qi++) {
862
1390
  const token = tokenQueue[qi];
863
1391
  if (token.type === 'sequence') {
1392
+ // Once the DCS introducer ESC has timed out, the tokenizer emits an
1393
+ // XTVERSION tail as text but its ST/BEL terminator as a sequence. Join
1394
+ // that terminator before treating it as an unrelated key. A lone ST
1395
+ // introducer ESC may itself time out before the trailing backslash.
1396
+ const heldTail = terminalResponseReattachTail;
1397
+ if (heldTail !== undefined &&
1398
+ heldTail.startsWith('P>|') &&
1399
+ mayRecoverTerminalResponseTail &&
1400
+ expectedResponseTypes.has('xtversion')) {
1401
+ if (token.value === '\x1b' && heldTail.length + 3 <= TERMINAL_RESPONSE_MAX_LENGTH) {
1402
+ terminalResponseReattachTail = heldTail + '\x1b';
1403
+ win32EscFlushedAt = undefined;
1404
+ continue;
1405
+ }
1406
+ if (token.value === '\x1b\\' || token.value === '\x07') {
1407
+ const sequence = '\x1b' + heldTail + token.value;
1408
+ const response = sequence.length <= TERMINAL_RESPONSE_MAX_LENGTH
1409
+ ? parseTerminalResponse(sequence)
1410
+ : null;
1411
+ if (response?.type === 'xtversion') {
1412
+ terminalResponseReattachTail = undefined;
1413
+ keys.push({ kind: 'response', sequence, response });
1414
+ continue;
1415
+ }
1416
+ }
1417
+ }
1418
+ // An unrelated sequence between the held fragment and its terminator
1419
+ // proves the reply died; do not merge it with later input.
1420
+ terminalResponseReattachTail = undefined;
864
1421
  if (token.value === PASTE_START) {
865
1422
  inPaste = true;
866
1423
  pasteBuffer = '';
867
1424
  // Bracketed paste is a terminal-controlled mode: report bytes are
868
1425
  // contiguous on the wire, so a held SGR prefix's report is dead
869
- // once paste starts. Discard it — a late `;34M` arriving after the
1426
+ // once paste starts. Release it — a late `;34M` arriving after the
870
1427
  // paste must not merge into a phantom press.
871
- mouseTailHold = undefined;
872
- mouseTailHoldAt = undefined;
1428
+ releaseHeldMouseHead();
873
1429
  }
874
1430
  else if (token.value === PASTE_END) {
875
1431
  // Always emit a paste key, even for empty pastes. This allows
@@ -880,8 +1436,7 @@ export function parseMultipleKeypresses(prevState, input = '') {
880
1436
  pasteBuffer = '';
881
1437
  // Paste end is a terminal protocol boundary too — a held SGR prefix
882
1438
  // from before the paste cannot complete now.
883
- mouseTailHold = undefined;
884
- mouseTailHoldAt = undefined;
1439
+ releaseHeldMouseHead();
885
1440
  }
886
1441
  else if (inPaste) {
887
1442
  // Sequences inside paste are treated as literal text
@@ -894,8 +1449,7 @@ export function parseMultipleKeypresses(prevState, input = '') {
894
1449
  // A fresh protocol record proves a held SGR prefix's report died:
895
1450
  // report bytes are contiguous on the wire, so nothing may
896
1451
  // interleave between a report's fragments.
897
- mouseTailHold = undefined;
898
- mouseTailHoldAt = undefined;
1452
+ releaseHeldMouseHead();
899
1453
  // win32-input-mode record. null means a swallowed event (keyup,
900
1454
  // bare modifier, orphaned surrogate) — the sequence is consumed
901
1455
  // either way and never reaches the VT keypress parser.
@@ -909,46 +1463,61 @@ export function parseMultipleKeypresses(prevState, input = '') {
909
1463
  }
910
1464
  }
911
1465
  else {
912
- const response = parseTerminalResponse(token.value);
913
- if (response) {
914
- // Terminal reply (DECRPM, DA, …) — same dead-report proof.
915
- mouseTailHold = undefined;
916
- mouseTailHoldAt = undefined;
917
- keys.push({ kind: 'response', sequence: token.value, response });
1466
+ // Fixed decision order (D6) for one complete token — see
1467
+ // claimProtocolSequence: OSC 8 drop (#1178) → terminal reply
1468
+ // (#1177) → mouse (SGR 1006 first, X10 as the legacy 1000/1002
1469
+ // fallback). This parser's SGR head-fragment claim family
1470
+ // (#1160/#1120) lives inside the mouse step, below the complete
1471
+ // claim, and never runs ahead of OSC 8 or a reply. Wheel falls
1472
+ // through to parseKeypress, which turns it into a wheel key WITH
1473
+ // the pointer coordinates for position-based routing.
1474
+ const claimed = claimProtocolSequence(token.value);
1475
+ if (claimed !== null) {
1476
+ // Any claim — drop, terminal reply or complete mouse report — is
1477
+ // a protocol boundary: a held prefix belongs to an older, dead
1478
+ // report. Release it BEFORE it can merge the next fragment into
1479
+ // a phantom event. A complete report also lights the P2 evidence
1480
+ // window for the ambiguous `[`/`ESC[` heads.
1481
+ if (claimed.kind === 'mouse')
1482
+ lastMouseReportAt = now;
1483
+ releaseHeldMouseHead();
1484
+ keys.push(claimed);
918
1485
  }
919
1486
  else {
920
- // SGR first (1006); X10 (legacy 1000/1002 without SGR) as the
921
- // compatibility fallback for clicks/drags. Wheel falls through
922
- // to parseKeypress, which turns it into a wheel key WITH the
923
- // pointer coordinates for position-based routing.
924
- const mouse = parseMouseEvent(token.value) ??
925
- parseX10MouseEvent(token.value);
926
- if (mouse) {
927
- // A complete report arrived — any held prefix belongs to an
928
- // older, dead report. Discard it BEFORE it can merge the next
929
- // fragment into a phantom event.
930
- mouseTailHold = undefined;
931
- mouseTailHoldAt = undefined;
932
- keys.push(mouse);
933
- }
934
- else if (SGR_MOUSE_PREFIX_RE.test(token.value.replace(/^\x1b/, ''))) {
935
- // Flush-truncated SGR mouse report: the tokenizer's flush
936
- // emitted the buffered prefix (ESC still attached) as a
937
- // sequence token. It is protocol bytes mid-report, not a key —
938
- // strip the ESC, hold for the continuation (the text-token
939
- // branch above completes it), and never let it fall through
940
- // to parseKeypress, where it would leak into the prompt.
941
- // A fresh prefix REPLACES any stale hold instead of appending:
1487
+ const head = token.value.replace(/^\x1b/, '');
1488
+ // F-2 transport evidence: `ESC[` buffered in ONE read (see
1489
+ // KeyParseState.mouseHeadSingleRead) grants the P1-only claim.
1490
+ const singleReadHead = token.value === '\x1b[' && prevState.mouseHeadSingleRead === token.value;
1491
+ const claim = claimSgrHead(head, singleReadHead);
1492
+ if (claim !== undefined &&
1493
+ (!claim.releasable || head.length <= MOUSE_HEAD_HOLD_MAX_LENGTH)) {
1494
+ // Flush-truncated SGR head: the tokenizer's flush emitted the
1495
+ // buffered head (ESC still attached) as a sequence token. It
1496
+ // is protocol bytes mid-report, not a key — strip the ESC,
1497
+ // hold for the continuation (the text-token branch below
1498
+ // completes it), and never let it fall through to
1499
+ // parseKeypress, where it would leak into the prompt.
1500
+ // A fresh head REPLACES any stale hold instead of appending:
942
1501
  // the new report's arrival proves the old one's tail never
943
1502
  // came (`[<0;18` + `[<64;…` concatenated parses as garbage).
944
- mouseTailHold = token.value.replace(/^\x1b/, '');
945
- mouseTailHoldAt = Date.now();
1503
+ // F-1: a gated old hold is RELEASED first so its bytes are
1504
+ // neither dropped nor ordered after the new head's bytes.
1505
+ releaseHeldMouseHead();
1506
+ mouseTailHold = head;
1507
+ mouseTailHoldAt = now;
1508
+ mouseTailHoldReleasable = claim.releasable;
1509
+ }
1510
+ else if (claim !== undefined && claim.releasable) {
1511
+ // Over the gated 64B bound: release immediately — never hold,
1512
+ // never drop (ADR-0007 D5).
1513
+ releaseHeldMouseHead();
1514
+ keys.push(parseKeypress(head));
946
1515
  }
947
1516
  else {
948
1517
  // Ordinary key sequence (arrows, function keys, …) — still an
949
- // ESC protocol start, so a held prefix's report is dead.
950
- mouseTailHold = undefined;
951
- mouseTailHoldAt = undefined;
1518
+ // ESC protocol start, so a held head's report is dead. F-1:
1519
+ // a gated hold is RELEASED in arrival order first.
1520
+ releaseHeldMouseHead();
952
1521
  keys.push(parseKeypress(token.value));
953
1522
  }
954
1523
  }
@@ -961,9 +1530,9 @@ export function parseMultipleKeypresses(prevState, input = '') {
961
1530
  }
962
1531
  else if (WIN32_INPUT_TAILS_RE.test(token.value)) {
963
1532
  // Protocol bytes — a live SGR mouse report cannot contain them, so
964
- // a held prefix's report is dead. Discard before recovering.
965
- mouseTailHold = undefined;
966
- mouseTailHoldAt = undefined;
1533
+ // a held prefix's report is dead. Release before recovering.
1534
+ releaseHeldMouseHead();
1535
+ terminalResponseReattachTail = undefined;
967
1536
  // A delayed win32-input-mode continuation can arrive after App's
968
1537
  // escape timer has already flushed its ESC prefix. Recover complete
969
1538
  // record tails so their protocol bytes do not leak into the prompt.
@@ -991,14 +1560,17 @@ export function parseMultipleKeypresses(prevState, input = '') {
991
1560
  // a full [\x20-] range would match typed input like `[MAX]` batched
992
1561
  // into one read and silently drop it as a phantom click.
993
1562
  // Any older hold belongs to a DIFFERENT, dead report: a complete
994
- // report's arrival proves its tail never came. Discard the stale
1563
+ // report's arrival proves its tail never came. Release the stale
995
1564
  // hold and resynthesize this tail cleanly — concatenating them
996
1565
  // (`ESC + hold + complete tail`) parses as garbage and leaks the
997
1566
  // protocol bytes into the prompt as an ordinary key.
998
- mouseTailHold = undefined;
999
- mouseTailHoldAt = undefined;
1567
+ releaseHeldMouseHead();
1568
+ terminalResponseReattachTail = undefined;
1000
1569
  const resynthesized = '\x1b' + token.value;
1001
1570
  const mouse = parseMouseEvent(resynthesized);
1571
+ // A real report lights the P2 evidence window for ambiguous heads.
1572
+ if (mouse)
1573
+ lastMouseReportAt = now;
1002
1574
  keys.push(mouse ?? parseKeypress(resynthesized));
1003
1575
  }
1004
1576
  else if (mouseTailHold !== undefined &&
@@ -1012,6 +1584,7 @@ export function parseMultipleKeypresses(prevState, input = '') {
1012
1584
  // protocol prefix, a win32 tail, or ordinary typing. The prefix
1013
1585
  // regex (no $ anchor) matches the report at the head; the suffix
1014
1586
  // is whatever follows.
1587
+ terminalResponseReattachTail = undefined;
1015
1588
  const combined = mouseTailHold + token.value;
1016
1589
  const m = combined.match(SGR_MOUSE_TAIL_PREFIX_RE);
1017
1590
  const reportEnd = m[0].length;
@@ -1020,39 +1593,111 @@ export function parseMultipleKeypresses(prevState, input = '') {
1020
1593
  tokenQueue.splice(qi + 1, 0, { type: 'text', value: suffix });
1021
1594
  }
1022
1595
  const resynthesized = '\x1b' + combined.slice(0, reportEnd);
1023
- mouseTailHold = undefined;
1024
- mouseTailHoldAt = undefined;
1596
+ clearMouseTailHold();
1025
1597
  const mouse = parseMouseEvent(resynthesized);
1598
+ // A real report lights the P2 evidence window for ambiguous heads.
1599
+ if (mouse)
1600
+ lastMouseReportAt = now;
1026
1601
  keys.push(mouse ?? parseKeypress(resynthesized));
1027
1602
  }
1028
- else if (SGR_MOUSE_PREFIX_RE.test(token.value) ||
1029
- // Continuation of an active hold: with the prefix already captured,
1030
- // the next fragment (`32;5M`'s leading digits, more params) is
1031
- // digits/semicolons — ambiguous with typing, so treat them as part
1032
- // of the in-flight report until the deadline. Completion uses the branch
1033
- // above first, so reaching here with a hold means still incomplete.
1034
- (mouseTailHold !== undefined && /^[\d;]*$/.test(token.value) && token.value !== '')) {
1035
- // Incomplete SGR mouse prefix: ConPTY split the report mid-sequence
1036
- // and the flush timer already released the buffered ESC prefix, so
1037
- // this text token carries protocol bytes, not typing. Hold it for
1038
- // the next chunk (which completes the tail — handled by the branch
1039
- // above via the combined `hold + value` check) instead of leaking
1040
- // into the prompt; the deadline check at the top of every call
1041
- // discards the hold once its grace expires. The regex demands `<` +
1042
- // digits, which no realistic typed text produces as a single text
1043
- // token.
1044
- if (mouseTailHold === undefined)
1045
- mouseTailHoldAt = Date.now();
1046
- mouseTailHold = (mouseTailHold ?? '') + token.value;
1047
- }
1048
1603
  else {
1049
- // Ordinary typing while a hold is pending: text that can never
1050
- // continue an SGR report proves the held report died. Discard the
1051
- // stale hold so it cannot merge the NEXT fragment into a phantom
1052
- // event, then pass the text through untouched.
1053
- mouseTailHold = undefined;
1054
- mouseTailHoldAt = undefined;
1055
- keys.push(parseKeypress(token.value));
1604
+ // #1177 terminal-reply re-attach runs first (decision order D6:
1605
+ // reply before mouse): the introducer ESC may already have been
1606
+ // flushed, so this text token — joined with any held fragment — may
1607
+ // be the rest of a reply. The one shape authority
1608
+ // (`claimExpectedResponseHead`) decides completeness, and a claim
1609
+ // needs BOTH a pending query of the matching expected type and a
1610
+ // shape that can still complete into it; anything else keeps the
1611
+ // token literal (AC-3) and drops a stale fragment rather than
1612
+ // leaking protocol bytes.
1613
+ const replyCandidate = (terminalResponseReattachTail ?? '') + token.value;
1614
+ const claimedResponse = mayRecoverTerminalResponseTail
1615
+ ? claimExpectedResponseHead(replyCandidate, expectedResponseTypes)
1616
+ : undefined;
1617
+ if (claimedResponse) {
1618
+ terminalResponseReattachTail = undefined;
1619
+ // A completed reply is a protocol boundary: a held SGR head's
1620
+ // report is dead (same caliber as the sequence-token reply path).
1621
+ releaseHeldMouseHead();
1622
+ keys.push({
1623
+ kind: 'response',
1624
+ sequence: '\x1b' + replyCandidate.slice(0, claimedResponse.consumed),
1625
+ response: claimedResponse.response,
1626
+ });
1627
+ // Trailing bytes beyond the completed reply are ordinary input
1628
+ // (the terminal batched the tail with the next keystrokes).
1629
+ const suffix = replyCandidate.slice(claimedResponse.consumed);
1630
+ if (suffix)
1631
+ tokenQueue.splice(qi + 1, 0, { type: 'text', value: suffix });
1632
+ }
1633
+ else {
1634
+ const replyStillOpen = mayRecoverTerminalResponseTail &&
1635
+ replyCandidate.length <= TERMINAL_RESPONSE_MAX_LENGTH &&
1636
+ expectsResponseType('\x1b' + replyCandidate, expectedResponseTypes);
1637
+ const freshReplyPrefix = mayRecoverTerminalResponseTail &&
1638
+ token.value.length <= TERMINAL_RESPONSE_MAX_LENGTH &&
1639
+ expectsResponseType('\x1b' + token.value, expectedResponseTypes);
1640
+ if (replyStillOpen) {
1641
+ // Still an open expected reply prefix — hold it for the next
1642
+ // call. The reply shape proves any held mouse report is dead, so
1643
+ // release the old mouse hold before claiming the reply.
1644
+ releaseHeldMouseHead();
1645
+ terminalResponseReattachTail = replyCandidate;
1646
+ }
1647
+ else if (freshReplyPrefix) {
1648
+ // The held fragment (if any) cannot be completed; drop it and
1649
+ // give the fresh bytes their own chance to start a reply prefix.
1650
+ // Again the reply claim releases any old mouse hold.
1651
+ releaseHeldMouseHead();
1652
+ terminalResponseReattachTail = token.value;
1653
+ }
1654
+ else {
1655
+ // SGR head family: claim a fresh head, extend an active hold, or
1656
+ // pass ordinary text through. `claimSgrHead` pairs shape with
1657
+ // evidence; a hold only survives while `hold + token` remains a
1658
+ // legal head prefix (SGR_MOUSE_HEAD_PREFIX_RE, grammar-locked to
1659
+ // SGR_MOUSE_RE), so text that cannot continue it proves the held
1660
+ // report died.
1661
+ terminalResponseReattachTail = undefined;
1662
+ const combined = (mouseTailHold ?? '') + token.value;
1663
+ const claim = claimSgrHead(token.value);
1664
+ const canExtend = mouseTailHold !== undefined &&
1665
+ SGR_MOUSE_HEAD_PREFIX_RE.test(combined);
1666
+ if (canExtend &&
1667
+ (mouseTailHoldReleasable !== true ||
1668
+ combined.length <= MOUSE_HEAD_HOLD_MAX_LENGTH)) {
1669
+ // Continuation (`<` after a bare `[`, digits/semicolons after
1670
+ // `[<`): keep the FIRST-capture deadline — flushes must not
1671
+ // renew it.
1672
+ mouseTailHold = combined;
1673
+ }
1674
+ else if (claim !== undefined &&
1675
+ (!claim.releasable ||
1676
+ token.value.length <= MOUSE_HEAD_HOLD_MAX_LENGTH)) {
1677
+ // Incomplete SGR head arriving as a text token (ConPTY/SSH
1678
+ // split the report mid-sequence and the flush timer already
1679
+ // released the buffered ESC prefix): hold it for the next
1680
+ // chunk instead of leaking protocol bytes into the prompt. A
1681
+ // fresh head REPLACES any stale hold — two reports never
1682
+ // concatenate — so the deadline restarts from this capture.
1683
+ // F-1: a gated old hold is RELEASED first so its bytes are
1684
+ // replayed in arrival order, not dropped.
1685
+ releaseHeldMouseHead();
1686
+ mouseTailHold = token.value;
1687
+ mouseTailHoldAt = now;
1688
+ mouseTailHoldReleasable = claim.releasable;
1689
+ }
1690
+ else {
1691
+ // The open report is dead (text that cannot continue it) or
1692
+ // the gated 64B bound is exceeded: a gated hold RELEASES its
1693
+ // bytes in arrival order (ADR-0007 D5) so no user input is
1694
+ // dropped; legacy holds keep the pre-gate silent discard.
1695
+ // This token then passes through as ordinary typing.
1696
+ releaseHeldMouseHead();
1697
+ keys.push(parseKeypress(token.value));
1698
+ }
1699
+ }
1700
+ }
1056
1701
  }
1057
1702
  }
1058
1703
  }
@@ -1113,13 +1758,31 @@ export function parseMultipleKeypresses(prevState, input = '') {
1113
1758
  win32Protocol.held = [];
1114
1759
  win32Protocol.sequence = '';
1115
1760
  }
1116
- // A held SGR mouse prefix that never completed is discarded by the
1117
- // deadline check at the top of this call — on flush AND on ordinary
1118
- // input alike (continuous input starves flush-only checks). Discarded
1119
- // silently, never emitted as text: protocol bytes that reach the prompt
1120
- // as text are exactly the leak this hold exists to prevent. Partial
1121
- // recovery of the coords is not worth one more branch — a mouse event
1122
- // with guessed terminators would dispatch phantom clicks.
1761
+ // A held SGR mouse head that never completed expires in the deadline check
1762
+ // at the top of this call — on flush AND on ordinary input alike
1763
+ // (continuous input starves flush-only checks). A gated hold is RELEASED as
1764
+ // ordinary keys there (ADR-0007 D5: delayed but never dropped); a legacy
1765
+ // shape-specific hold keeps the pre-gate silent discard. Partial recovery
1766
+ // of the coords is not attempted — a mouse event with guessed terminators
1767
+ // would dispatch phantom clicks.
1768
+ // The reply-prefix hold lives only while the tokenizer still retains a
1769
+ // reply-shaped prefix: completion, release or reset all end it, so the
1770
+ // NEXT hold captures its own first-capture deadline.
1771
+ if (terminalResponseHoldAt !== undefined && !isResponsePrefix(tokenizer.buffer())) {
1772
+ terminalResponseHoldAt = undefined;
1773
+ }
1774
+ // A REAL lone-Escape flush — an Escape emitted as its own key, not the ESC
1775
+ // introducer of a buffered sequence. Only this opens the #796 re-attach
1776
+ // window; the window alone is not evidence (D1), and a direct caller that
1777
+ // never injects the provenance keeps the pre-change behavior.
1778
+ const escapeFlushedNow = isFlush &&
1779
+ keys.some(key => key.kind === 'key' &&
1780
+ key.name === 'escape' &&
1781
+ key.sequence === '\x1b');
1782
+ // A fresh lone-Escape flush opens a NEW window: a fragment held against
1783
+ // the old one cannot be part of a reply whose introducer just arrived.
1784
+ if (escapeFlushedNow)
1785
+ terminalResponseReattachTail = undefined;
1123
1786
  // Build new state
1124
1787
  const newState = {
1125
1788
  mode: inPaste ? 'IN_PASTE' : 'NORMAL',
@@ -1140,6 +1803,9 @@ export function parseMultipleKeypresses(prevState, input = '') {
1140
1803
  // The host gate rides along: App replaces its state with this object on
1141
1804
  // every read, and the hold must stay closed for the whole session.
1142
1805
  win32Capable: prevState.win32Capable,
1806
+ // Same host-injected contract for the query evidence: read-only here,
1807
+ // re-injected by App on every input call, so it is never stale.
1808
+ terminalExpectedResponseTypes: prevState.terminalExpectedResponseTypes,
1143
1809
  win32InputMode,
1144
1810
  win32InputStartedAt,
1145
1811
  win32EscFlushedAt,
@@ -1147,8 +1813,24 @@ export function parseMultipleKeypresses(prevState, input = '') {
1147
1813
  win32AltHighSurrogate: win32Ctx.altHigh,
1148
1814
  win32Paste,
1149
1815
  win32Protocol,
1816
+ // The host gate rides along as well: App replaces its state with this
1817
+ // object on every read, and re-injects the latest evidence each call.
1818
+ mouseReportingActive: prevState.mouseReportingActive,
1819
+ lastMouseReportAt,
1150
1820
  mouseTailHold,
1151
1821
  mouseTailHoldAt,
1822
+ mouseTailHoldReleasable,
1823
+ // Parser-internal transport evidence: set by the read that buffered the
1824
+ // head, consumed by the next call (the flush that emits `ESC[`), then
1825
+ // cleared because that call feeds nothing.
1826
+ mouseHeadSingleRead,
1827
+ terminalResponseHoldAt,
1828
+ terminalResponseReattachTail,
1829
+ // A fresh lone-Escape flush re-arms the window; otherwise the carried
1830
+ // stamp keeps it open (see carryTailAfterEscFlush above).
1831
+ terminalResponseTailAfterEscFlushAt: escapeFlushedNow
1832
+ ? Date.now()
1833
+ : carryTailAfterEscFlush,
1152
1834
  _tokenizer: tokenizer,
1153
1835
  };
1154
1836
  return [keys, newState];