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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (351) hide show
  1. package/README.md +175 -123
  2. package/bin/dsh-tui.js +748 -22
  3. package/cordis.patch.yml +42 -13
  4. package/cordis.yml +5 -2
  5. package/lib/types/activityPrefs.d.ts +24 -0
  6. package/lib/types/activityPrefs.d.ts.map +1 -1
  7. package/lib/types/activityPrefs.js +50 -1
  8. package/lib/types/adapter/channel/session-projection.d.ts.map +1 -1
  9. package/lib/types/adapter/channel/session-projection.js +2 -0
  10. package/lib/types/adapter/channel/state.d.ts.map +1 -1
  11. package/lib/types/adapter/channel/state.js +3 -1
  12. package/lib/types/adapter/channel/ui-policy.d.ts +4 -1
  13. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  14. package/lib/types/adapter/channel/ui-policy.js +6 -2
  15. package/lib/types/adapter/kernel/host-facade.d.ts.map +1 -1
  16. package/lib/types/adapter/kernel/host-facade.js +1 -0
  17. package/lib/types/adapter/kernel/replay.d.ts.map +1 -1
  18. package/lib/types/adapter/kernel/replay.js +3 -0
  19. package/lib/types/adapter/kernel/runtime.d.ts.map +1 -1
  20. package/lib/types/adapter/kernel/runtime.js +2 -0
  21. package/lib/types/adapter/kernel/slices/workspace.d.ts.map +1 -1
  22. package/lib/types/adapter/kernel/slices/workspace.js +1 -0
  23. package/lib/types/adapter/ports/channel-ui.d.ts +29 -5
  24. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  25. package/lib/types/adapter/ports/channel-view.d.ts +66 -12
  26. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  27. package/lib/types/adapter/ports/channel-workspace.d.ts +23 -0
  28. package/lib/types/adapter/ports/channel-workspace.d.ts.map +1 -1
  29. package/lib/types/adapter/ports/channel.d.ts +4 -1
  30. package/lib/types/adapter/ports/channel.d.ts.map +1 -1
  31. package/lib/types/adapter/ports/workspace.d.ts +2 -0
  32. package/lib/types/adapter/ports/workspace.d.ts.map +1 -1
  33. package/lib/types/adapter/upstream/workspace-driver.d.ts.map +1 -1
  34. package/lib/types/adapter/upstream/workspace-driver.js +10 -2
  35. package/lib/types/commands.d.ts.map +1 -1
  36. package/lib/types/commands.js +1 -0
  37. package/lib/types/components/ActivityLine.d.ts +7 -2
  38. package/lib/types/components/ActivityLine.d.ts.map +1 -1
  39. package/lib/types/components/ActivityLine.js +0 -8
  40. package/lib/types/components/ContextBarView.d.ts +15 -7
  41. package/lib/types/components/ContextBarView.d.ts.map +1 -1
  42. package/lib/types/components/ContextBarView.js +26 -22
  43. package/lib/types/components/ImagePreviewOverlay.d.ts.map +1 -1
  44. package/lib/types/components/ImagePreviewOverlay.js +11 -5
  45. package/lib/types/components/Markdown.d.ts +11 -1
  46. package/lib/types/components/Markdown.d.ts.map +1 -1
  47. package/lib/types/components/Markdown.js +20 -2
  48. package/lib/types/components/MermaidDiagram.d.ts +13 -0
  49. package/lib/types/components/MermaidDiagram.d.ts.map +1 -0
  50. package/lib/types/components/MermaidDiagram.js +61 -0
  51. package/lib/types/components/MessageList.d.ts +26 -1
  52. package/lib/types/components/MessageList.d.ts.map +1 -1
  53. package/lib/types/components/MessageList.js +74 -8
  54. package/lib/types/components/PageMargin.d.ts +1 -1
  55. package/lib/types/components/PromptInput.d.ts +37 -1
  56. package/lib/types/components/PromptInput.d.ts.map +1 -1
  57. package/lib/types/components/PromptInput.js +291 -30
  58. package/lib/types/components/ScrollbarGutter.d.ts +20 -5
  59. package/lib/types/components/ScrollbarGutter.d.ts.map +1 -1
  60. package/lib/types/components/ScrollbarGutter.js +88 -10
  61. package/lib/types/components/SplitDiffView.d.ts.map +1 -1
  62. package/lib/types/components/SplitDiffView.js +2 -1
  63. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  64. package/lib/types/components/StreamingMarkdown.js +30 -28
  65. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  66. package/lib/types/components/SubagentCard.js +12 -6
  67. package/lib/types/components/SubagentDetailScene.d.ts.map +1 -1
  68. package/lib/types/components/SubagentDetailScene.js +8 -3
  69. package/lib/types/components/Tooltip.d.ts.map +1 -1
  70. package/lib/types/components/Tooltip.js +45 -7
  71. package/lib/types/components/design-system/HintLine.d.ts +1 -2
  72. package/lib/types/components/design-system/HintLine.d.ts.map +1 -1
  73. package/lib/types/components/design-system/HintLine.js +1 -2
  74. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +2 -1
  75. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  76. package/lib/types/components/messages/AssistantThinkingMessage.js +2 -1
  77. package/lib/types/components/messages/AssistantToolUseMessage.d.ts +2 -2
  78. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  79. package/lib/types/components/messages/AssistantToolUseMessage.js +30 -26
  80. package/lib/types/components/messages/UserPromptMessage.d.ts.map +1 -1
  81. package/lib/types/components/messages/UserPromptMessage.js +5 -7
  82. package/lib/types/components/messages/useImageInspection.d.ts.map +1 -1
  83. package/lib/types/components/messages/useImageInspection.js +19 -9
  84. package/lib/types/components/promptDraftCache.d.ts +102 -0
  85. package/lib/types/components/promptDraftCache.d.ts.map +1 -0
  86. package/lib/types/components/promptDraftCache.js +76 -0
  87. package/lib/types/components/questions/AskUserQuestionPanel.d.ts +17 -1
  88. package/lib/types/components/questions/AskUserQuestionPanel.d.ts.map +1 -1
  89. package/lib/types/components/questions/AskUserQuestionPanel.js +40 -7
  90. package/lib/types/components/questions/QuestionMinimizedBar.d.ts +12 -0
  91. package/lib/types/components/questions/QuestionMinimizedBar.d.ts.map +1 -0
  92. package/lib/types/components/questions/QuestionMinimizedBar.js +43 -0
  93. package/lib/types/components/sessions/SessionListRow.d.ts +23 -1
  94. package/lib/types/components/sessions/SessionListRow.d.ts.map +1 -1
  95. package/lib/types/components/sessions/SessionListRow.js +52 -5
  96. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts +49 -0
  97. package/lib/types/components/workspaces/HomeWorkspaceRow.d.ts.map +1 -0
  98. package/lib/types/components/workspaces/HomeWorkspaceRow.js +50 -0
  99. package/lib/types/dsh-adapter/activity-store.d.ts +221 -0
  100. package/lib/types/dsh-adapter/activity-store.d.ts.map +1 -0
  101. package/lib/types/dsh-adapter/activity-store.js +359 -0
  102. package/lib/types/dsh-adapter/bundled-presets.d.ts +10 -0
  103. package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -0
  104. package/lib/types/dsh-adapter/bundled-presets.js +80 -0
  105. package/lib/types/dsh-adapter/channel/action-readiness.d.ts +1 -1
  106. package/lib/types/dsh-adapter/channel/action-readiness.d.ts.map +1 -1
  107. package/lib/types/dsh-adapter/channel/action-readiness.js +3 -0
  108. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts +1 -1
  109. package/lib/types/dsh-adapter/channel/agent-view-projection.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/channel/agent-view-projection.js +19 -2
  111. package/lib/types/dsh-adapter/channel/background-action.d.ts +1 -1
  112. package/lib/types/dsh-adapter/channel/background-action.d.ts.map +1 -1
  113. package/lib/types/dsh-adapter/channel/background-action.js +53 -29
  114. package/lib/types/dsh-adapter/channel/binding-events.d.ts +8 -6
  115. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  116. package/lib/types/dsh-adapter/channel/binding-events.js +5 -4
  117. package/lib/types/dsh-adapter/channel/binding.d.ts +19 -2
  118. package/lib/types/dsh-adapter/channel/binding.d.ts.map +1 -1
  119. package/lib/types/dsh-adapter/channel/binding.js +78 -18
  120. package/lib/types/dsh-adapter/channel/compaction.d.ts +0 -1
  121. package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
  122. package/lib/types/dsh-adapter/channel/compaction.js +0 -2
  123. package/lib/types/dsh-adapter/channel/composer-images.d.ts +5 -0
  124. package/lib/types/dsh-adapter/channel/composer-images.d.ts.map +1 -1
  125. package/lib/types/dsh-adapter/channel/composer-images.js +105 -6
  126. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts +8 -0
  127. package/lib/types/dsh-adapter/channel/context-bookkeeping.d.ts.map +1 -1
  128. package/lib/types/dsh-adapter/channel/context-bookkeeping.js +3 -2
  129. package/lib/types/dsh-adapter/channel/ide-selection.d.ts +58 -0
  130. package/lib/types/dsh-adapter/channel/ide-selection.d.ts.map +1 -0
  131. package/lib/types/dsh-adapter/channel/ide-selection.js +226 -0
  132. package/lib/types/dsh-adapter/channel/input-delivery.d.ts +6 -1
  133. package/lib/types/dsh-adapter/channel/input-delivery.d.ts.map +1 -1
  134. package/lib/types/dsh-adapter/channel/input-delivery.js +9 -1
  135. package/lib/types/dsh-adapter/channel/local-actions.d.ts +3 -19
  136. package/lib/types/dsh-adapter/channel/local-actions.d.ts.map +1 -1
  137. package/lib/types/dsh-adapter/channel/local-actions.js +2 -1
  138. package/lib/types/dsh-adapter/channel/model-actions.d.ts +4 -1
  139. package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
  140. package/lib/types/dsh-adapter/channel/model-actions.js +82 -6
  141. package/lib/types/dsh-adapter/channel/model-switch.d.ts +1 -2
  142. package/lib/types/dsh-adapter/channel/model-switch.d.ts.map +1 -1
  143. package/lib/types/dsh-adapter/channel/model-switch.js +49 -31
  144. package/lib/types/dsh-adapter/channel/projection.d.ts +4 -0
  145. package/lib/types/dsh-adapter/channel/projection.d.ts.map +1 -1
  146. package/lib/types/dsh-adapter/channel/projection.js +29 -19
  147. package/lib/types/dsh-adapter/channel/reports.d.ts.map +1 -1
  148. package/lib/types/dsh-adapter/channel/reports.js +4 -12
  149. package/lib/types/dsh-adapter/channel/session-adoption.d.ts +1 -1
  150. package/lib/types/dsh-adapter/channel/session-adoption.d.ts.map +1 -1
  151. package/lib/types/dsh-adapter/channel/session-adoption.js +1 -0
  152. package/lib/types/dsh-adapter/channel/session-fork.d.ts.map +1 -1
  153. package/lib/types/dsh-adapter/channel/session-fork.js +26 -2
  154. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts +4 -1
  155. package/lib/types/dsh-adapter/channel/session-live-adoption.d.ts.map +1 -1
  156. package/lib/types/dsh-adapter/channel/session-live-adoption.js +2 -0
  157. package/lib/types/dsh-adapter/channel/session-metadata.d.ts +10 -0
  158. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  159. package/lib/types/dsh-adapter/channel/session-metadata.js +6 -4
  160. package/lib/types/dsh-adapter/channel/session-reset.d.ts +1 -1
  161. package/lib/types/dsh-adapter/channel/session-reset.d.ts.map +1 -1
  162. package/lib/types/dsh-adapter/channel/session-reset.js +0 -1
  163. package/lib/types/dsh-adapter/channel/session-resume.d.ts +12 -3
  164. package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
  165. package/lib/types/dsh-adapter/channel/session-resume.js +292 -94
  166. package/lib/types/dsh-adapter/channel/session-rewind.d.ts.map +1 -1
  167. package/lib/types/dsh-adapter/channel/session-rewind.js +34 -16
  168. package/lib/types/dsh-adapter/channel/session-tree-actions.d.ts.map +1 -1
  169. package/lib/types/dsh-adapter/channel/session-tree-actions.js +19 -3
  170. package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
  171. package/lib/types/dsh-adapter/channel/settings-host.js +4 -3
  172. package/lib/types/dsh-adapter/channel/state.d.ts +6 -1
  173. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  174. package/lib/types/dsh-adapter/channel/state.js +3 -3
  175. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts +21 -1
  176. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  177. package/lib/types/dsh-adapter/channel/subagent-projection.js +173 -6
  178. package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
  179. package/lib/types/dsh-adapter/channel/transcript.js +6 -12
  180. package/lib/types/dsh-adapter/channel/types.d.ts +6 -1
  181. package/lib/types/dsh-adapter/channel/types.d.ts.map +1 -1
  182. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts +4 -1
  183. package/lib/types/dsh-adapter/channel/workspace-actions.d.ts.map +1 -1
  184. package/lib/types/dsh-adapter/channel/workspace-actions.js +41 -1
  185. package/lib/types/dsh-adapter/channel.d.ts +1 -1
  186. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  187. package/lib/types/dsh-adapter/channel.js +82 -20
  188. package/lib/types/dsh-adapter/compat/index.d.ts +1 -1
  189. package/lib/types/dsh-adapter/compat/index.d.ts.map +1 -1
  190. package/lib/types/dsh-adapter/compat/index.js +1 -1
  191. package/lib/types/dsh-adapter/compat/messages.d.ts +15 -0
  192. package/lib/types/dsh-adapter/compat/messages.d.ts.map +1 -0
  193. package/lib/types/dsh-adapter/compat/messages.js +15 -0
  194. package/lib/types/dsh-adapter/compat/sessionLog.d.ts +18 -0
  195. package/lib/types/dsh-adapter/compat/sessionLog.d.ts.map +1 -1
  196. package/lib/types/dsh-adapter/compat/sessionLog.js +18 -3
  197. package/lib/types/dsh-adapter/compat/settings.d.ts +35 -0
  198. package/lib/types/dsh-adapter/compat/settings.d.ts.map +1 -0
  199. package/lib/types/dsh-adapter/compat/settings.js +57 -0
  200. package/lib/types/dsh-adapter/compat/shell.d.ts +25 -0
  201. package/lib/types/dsh-adapter/compat/shell.d.ts.map +1 -0
  202. package/lib/types/dsh-adapter/compat/shell.js +11 -0
  203. package/lib/types/dsh-adapter/contract.d.ts +5 -4
  204. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  205. package/lib/types/dsh-adapter/contract.js +17 -6
  206. package/lib/types/dsh-adapter/ide-channel.d.ts +224 -0
  207. package/lib/types/dsh-adapter/ide-channel.d.ts.map +1 -0
  208. package/lib/types/dsh-adapter/ide-channel.js +556 -0
  209. package/lib/types/dsh-adapter/index.d.ts +18 -3
  210. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  211. package/lib/types/dsh-adapter/index.js +32 -5
  212. package/lib/types/dsh-adapter/plugin.d.ts +4 -1
  213. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  214. package/lib/types/dsh-adapter/plugin.js +197 -49
  215. package/lib/types/dsh-adapter/preset-resolution.d.ts +8 -0
  216. package/lib/types/dsh-adapter/preset-resolution.d.ts.map +1 -1
  217. package/lib/types/dsh-adapter/preset-resolution.js +10 -0
  218. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts +53 -0
  219. package/lib/types/dsh-adapter/session-mount-heartbeat.d.ts.map +1 -0
  220. package/lib/types/dsh-adapter/session-mount-heartbeat.js +100 -0
  221. package/lib/types/dsh-adapter/sessionTree.d.ts.map +1 -1
  222. package/lib/types/dsh-adapter/sessionTree.js +3 -2
  223. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  224. package/lib/types/dsh-adapter/settings-sections.js +7 -4
  225. package/lib/types/dsh-adapter/subagents.d.ts +16 -3
  226. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  227. package/lib/types/dsh-adapter/subagents.js +92 -29
  228. package/lib/types/dsh-adapter/workspaces.d.ts +46 -3
  229. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  230. package/lib/types/dsh-adapter/workspaces.js +116 -0
  231. package/lib/types/effortPrefs.d.ts +15 -3
  232. package/lib/types/effortPrefs.d.ts.map +1 -1
  233. package/lib/types/effortPrefs.js +37 -3
  234. package/lib/types/history.d.ts +13 -1
  235. package/lib/types/history.d.ts.map +1 -1
  236. package/lib/types/history.js +15 -2
  237. package/lib/types/homePrefs.d.ts +15 -0
  238. package/lib/types/homePrefs.d.ts.map +1 -0
  239. package/lib/types/homePrefs.js +53 -0
  240. package/lib/types/i18n.d.ts +347 -297
  241. package/lib/types/i18n.d.ts.map +1 -1
  242. package/lib/types/i18n.js +133 -85
  243. package/lib/types/ink/colorize.d.ts +10 -0
  244. package/lib/types/ink/colorize.d.ts.map +1 -1
  245. package/lib/types/ink/colorize.js +21 -0
  246. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  247. package/lib/types/ink/components/ScrollBox.js +12 -1
  248. package/lib/types/ink/dom.d.ts +1 -0
  249. package/lib/types/ink/dom.d.ts.map +1 -1
  250. package/lib/types/ink/hooks/use-copy-on-select.d.ts +5 -1
  251. package/lib/types/ink/hooks/use-copy-on-select.d.ts.map +1 -1
  252. package/lib/types/ink/hooks/use-copy-on-select.js +15 -1
  253. package/lib/types/ink/ink.d.ts.map +1 -1
  254. package/lib/types/ink/ink.js +36 -1
  255. package/lib/types/ink/log-update.d.ts.map +1 -1
  256. package/lib/types/ink/log-update.js +19 -1
  257. package/lib/types/ink/output.d.ts +10 -1
  258. package/lib/types/ink/output.d.ts.map +1 -1
  259. package/lib/types/ink/output.js +109 -17
  260. package/lib/types/ink/render-border.d.ts +6 -1
  261. package/lib/types/ink/render-border.d.ts.map +1 -1
  262. package/lib/types/ink/render-border.js +25 -9
  263. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  264. package/lib/types/ink/render-node-to-output.js +48 -6
  265. package/lib/types/ink/renderer.d.ts +6 -0
  266. package/lib/types/ink/renderer.d.ts.map +1 -1
  267. package/lib/types/ink/renderer.js +2 -1
  268. package/lib/types/ink/selection.d.ts +88 -0
  269. package/lib/types/ink/selection.d.ts.map +1 -1
  270. package/lib/types/ink/selection.js +164 -0
  271. package/lib/types/ink/sixel-graphics.d.ts +19 -1
  272. package/lib/types/ink/sixel-graphics.d.ts.map +1 -1
  273. package/lib/types/ink/sixel-graphics.js +124 -12
  274. package/lib/types/ink/terminal-image.d.ts +18 -1
  275. package/lib/types/ink/terminal-image.d.ts.map +1 -1
  276. package/lib/types/ink/terminal.d.ts +17 -0
  277. package/lib/types/ink/terminal.d.ts.map +1 -1
  278. package/lib/types/ink/terminal.js +20 -1
  279. package/lib/types/screens/Chat.d.ts +20 -1
  280. package/lib/types/screens/Chat.d.ts.map +1 -1
  281. package/lib/types/screens/Chat.js +296 -76
  282. package/lib/types/screens/SessionSupervisor.d.ts +84 -0
  283. package/lib/types/screens/SessionSupervisor.d.ts.map +1 -0
  284. package/lib/types/screens/SessionSupervisor.js +350 -0
  285. package/lib/types/screens/SessionTree.js +2 -2
  286. package/lib/types/screens/Settings.d.ts.map +1 -1
  287. package/lib/types/screens/Settings.js +32 -9
  288. package/lib/types/screens/StatusLine.d.ts +18 -1
  289. package/lib/types/screens/StatusLine.d.ts.map +1 -1
  290. package/lib/types/screens/StatusLine.js +38 -19
  291. package/lib/types/screens/StatusMetrics.d.ts +79 -22
  292. package/lib/types/screens/StatusMetrics.d.ts.map +1 -1
  293. package/lib/types/screens/StatusMetrics.js +153 -88
  294. package/lib/types/screens/sessionSupervisor/model.d.ts +83 -0
  295. package/lib/types/screens/sessionSupervisor/model.d.ts.map +1 -0
  296. package/lib/types/screens/sessionSupervisor/model.js +69 -0
  297. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts +120 -0
  298. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.d.ts.map +1 -0
  299. package/lib/types/screens/sessionSupervisor/useSessionSupervisor.js +593 -0
  300. package/lib/types/sessionMounts.d.ts +272 -0
  301. package/lib/types/sessionMounts.d.ts.map +1 -0
  302. package/lib/types/sessionMounts.js +706 -0
  303. package/lib/types/sessions/format.d.ts.map +1 -1
  304. package/lib/types/sessions/format.js +4 -1
  305. package/lib/types/sessions/resumeFailure.d.ts +38 -0
  306. package/lib/types/sessions/resumeFailure.d.ts.map +1 -0
  307. package/lib/types/sessions/resumeFailure.js +58 -0
  308. package/lib/types/terminal-utils/mermaid.d.ts +44 -0
  309. package/lib/types/terminal-utils/mermaid.d.ts.map +1 -0
  310. package/lib/types/terminal-utils/mermaid.js +83 -0
  311. package/lib/types/tips.d.ts.map +1 -1
  312. package/lib/types/tips.js +175 -19
  313. package/lib/types/tuiDisplayPrefs.d.ts +7 -9
  314. package/lib/types/tuiDisplayPrefs.d.ts.map +1 -1
  315. package/lib/types/tuiDisplayPrefs.js +31 -28
  316. package/lib/types/update.d.ts +61 -0
  317. package/lib/types/update.d.ts.map +1 -1
  318. package/lib/types/update.js +194 -0
  319. package/lib/types/utils/fold-long-lines.d.ts +1 -1
  320. package/lib/types/utils/fold-long-lines.js +1 -1
  321. package/lib/types/utils/imageResize.d.ts +90 -0
  322. package/lib/types/utils/imageResize.d.ts.map +1 -0
  323. package/lib/types/utils/imageResize.js +272 -0
  324. package/lib/types/utils/keymap.d.ts +5 -1
  325. package/lib/types/utils/keymap.d.ts.map +1 -1
  326. package/lib/types/utils/keymap.js +23 -0
  327. package/lib/types/working-activity.d.ts +10 -9
  328. package/lib/types/working-activity.d.ts.map +1 -1
  329. package/lib/types/working-activity.js +17 -10
  330. package/lib/types/workspaces.d.ts +2 -1
  331. package/lib/types/workspaces.d.ts.map +1 -1
  332. package/lib/types/workspaces.js +1 -1
  333. package/node_modules/@deepseek-harness-tui/dsh-auth/package.json +10 -10
  334. package/package.json +126 -105
  335. package/presets/liangshen/.dsh-tui-managed.json +1 -1
  336. package/presets/liangshen/agent.cordis.yml +15 -0
  337. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts +0 -19
  338. package/lib/types/components/design-system/KeyboardShortcutHint.d.ts.map +0 -1
  339. package/lib/types/components/design-system/KeyboardShortcutHint.js +0 -14
  340. package/lib/types/dsh-adapter/channel/activity.d.ts +0 -20
  341. package/lib/types/dsh-adapter/channel/activity.d.ts.map +0 -1
  342. package/lib/types/dsh-adapter/channel/activity.js +0 -113
  343. package/lib/types/screens/AgentView.d.ts +0 -23
  344. package/lib/types/screens/AgentView.d.ts.map +0 -1
  345. package/lib/types/screens/AgentView.js +0 -546
  346. package/lib/types/screens/SessionBrowser.d.ts +0 -42
  347. package/lib/types/screens/SessionBrowser.d.ts.map +0 -1
  348. package/lib/types/screens/SessionBrowser.js +0 -911
  349. package/lib/types/terminal-utils/terminal.d.ts +0 -18
  350. package/lib/types/terminal-utils/terminal.d.ts.map +0 -1
  351. package/lib/types/terminal-utils/terminal.js +0 -96
@@ -1,11 +1,13 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { SessionId } from '@deepseek-ai/dsh-session';
3
3
  import { explicitModelRoute, recordedModelRoute, resolveModelRoute, validateModelRoute } from '../../modelRoute.js';
4
- import { clearResumeTarget, touchAgentViewSession, touchSession, writeResumeTarget } from '../../sessionHistory.js';
4
+ import { clearResumeTarget, writeResumeTarget, touchAgentViewSession, touchSession } from '../../sessionHistory.js';
5
+ import { mountFailureText } from '../../sessions/resumeFailure.js';
5
6
  import { t } from '../../i18n.js';
6
7
  import { readModelPref } from '../../modelPrefs.js';
7
8
  import { migratePresetPref, readPresetPref } from '../../presetPrefs.js';
8
9
  import { agentViewHasTurns } from '../agent-view.js';
10
+ import { occupancyOf, readSessionOwners, reserveMount, reserveNewSession, } from '../../sessionMounts.js';
9
11
  import { ensureLegacySessionEventTypes } from '../compat/index.js';
10
12
  import { snapshotLiveSessionEvents } from '../compat/liveSession.js';
11
13
  import { composePreset, resolvePersistedPreset, resolvePersistedRoute } from '../presets.js';
@@ -18,6 +20,7 @@ export function createSessionResumeActions(ctx, state, options, deps) {
18
20
  resetSessionProjection(state, deps.rowIds, deps.resetProjector, deps.resetSubagents, deps.resetJobs);
19
21
  state.status = handle.agent.status;
20
22
  state.agentId = handle.agent.id;
23
+ state.sessionId = handle.agent.session.id;
21
24
  state.agentPreset = agentPreset;
22
25
  if (route !== undefined) {
23
26
  state.provider = route.provider;
@@ -39,96 +42,272 @@ export function createSessionResumeActions(ctx, state, options, deps) {
39
42
  void deps.refreshSkillCommands();
40
43
  };
41
44
  /** Resume a persisted session, optionally parking the attached agent for agent-view attachment. */
42
- const resume = async (sessionId, kind, keepCurrent, adoption, entrySession) => {
43
- const agents = ctx.get('agents');
44
- if (!agents) {
45
- deps.notify(t('resume-unavailable'), { color: 'error' });
46
- return { ok: false, reason: 'unavailable' };
47
- }
48
- ensureLegacySessionEventTypes();
49
- const composed = await composePreset(ctx, await resolvePersistedPreset(ctx, SessionId(sessionId)));
50
- const explicitRoute = explicitModelRoute({ provider: options.configuredProvider, model: options.configuredModel });
51
- const persistedRoute = await resolvePersistedRoute(ctx, SessionId(sessionId));
52
- let handle;
53
- try {
54
- handle = await deps.binding.prepare(adoption, () => agents.resume({
55
- resumeSessionId: SessionId(sessionId),
56
- agentOptions: {
57
- provider: explicitRoute?.provider ?? persistedRoute?.provider,
58
- model: explicitRoute?.model ?? persistedRoute?.model,
59
- },
60
- ...(composed.setup === undefined ? {} : { setup: composed.setup }),
61
- }));
62
- }
63
- catch (error) {
64
- const message = error instanceof Error ? error.message : String(error);
65
- deps.notify(t('resume-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
66
- return { ok: false, reason: 'failed', error: message };
67
- }
68
- if (!deps.binding.isCurrent(adoption)) {
69
- await deps.binding.abandon(handle);
70
- return { ok: false, reason: 'cancelled' };
71
- }
45
+ const resume = async (sessionId, kind, keepCurrent, adoption, entrySession,
46
+ /**
47
+ * The ledger reservation this call holds for `sessionId`; ended by this
48
+ * function on every exit. `settle` on a commit (the agent is in the
49
+ * registry from then on), `abandon` on every path that ends WITHOUT one —
50
+ * including a throwing `binding.adopt()` and a setup failure inside its
51
+ * tail, which revokes the candidate.
52
+ */
53
+ reservation) => {
54
+ /**
55
+ * The reservation is ended on EVERY exit from here, so the attempt owns it
56
+ * for the whole body: `settle` on a commit (the agent is in the registry
57
+ * from then on), `abandon` on everything else — a throwing
58
+ * `binding.adopt()`, a setup failure inside its tail, and also a throw from
59
+ * the preset/route reads below, which used to sit outside any guard and
60
+ * would have left a pin that republishes the session as ours on every beat.
61
+ */
62
+ let committed = false;
72
63
  try {
73
- await attachSessionToWorkspace(ctx, handle.agent.session.header.cwd ?? state.cwd, SessionId(sessionId));
64
+ return await runResume();
74
65
  }
75
- catch (error) {
76
- deps.notify(t('resume-attach-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'warning', timeoutMs: 8000 });
66
+ finally {
67
+ if (committed)
68
+ reservation?.settle();
69
+ else
70
+ reservation?.abandon();
77
71
  }
78
- if (entrySession !== undefined && (!deps.owner.current() || deps.binding.agent.session !== entrySession)) {
79
- await deps.binding.abandon(handle);
80
- deps.notify(t('resume-session-changed'), { color: 'error' });
81
- return { ok: false, reason: 'failed', error: 'live session changed during resume' };
82
- }
83
- return deps.binding.adopt(handle, adoption, (committed, disposePrevious) => {
84
- const previousSessionId = String(committed.agent.session.id);
85
- state.cwd = handle.agent.session.header.cwd ?? state.cwd;
86
- state.displayCwd = deps.describeWorkspace(state.cwd).description ?? state.cwd;
87
- deps.refreshGitBranch();
88
- // Reset the input FIFO and pending-decision indicators BEFORE the first
89
- // emit (main's bind → clear → refresh order); see the /new tail.
90
- deps.clearStagedImages();
91
- resetAndBind(handle, composed.agentPreset, explicitRoute ?? recordedModelRoute(snapshotLiveSessionEvents(handle.agent.session)), true);
92
- writeResumeTarget(sessionId);
93
- touchSession(sessionId);
94
- state.emit();
95
- const keepPrevious = keepCurrent && committed.handle !== undefined
96
- && (committed.handle.agent.status === 'running' || agentViewHasTurns(snapshotLiveSessionEvents(committed.handle.agent.session)));
97
- if (committed.handle !== undefined) {
98
- if (keepPrevious) {
99
- deps.backgroundHandles.set(previousSessionId, committed.handle);
100
- disposePrevious('park');
72
+ async function runResume() {
73
+ const agents = ctx.get('agents');
74
+ if (!agents) {
75
+ deps.notify(t('resume-unavailable'), { color: 'error' });
76
+ return { ok: false, reason: 'unavailable' };
77
+ }
78
+ ensureLegacySessionEventTypes();
79
+ const composed = await composePreset(ctx, await resolvePersistedPreset(ctx, SessionId(sessionId)));
80
+ const explicitRoute = explicitModelRoute({ provider: options.configuredProvider, model: options.configuredModel });
81
+ const persistedRoute = await resolvePersistedRoute(ctx, SessionId(sessionId));
82
+ let handle;
83
+ try {
84
+ handle = await deps.binding.prepare(adoption, () => agents.resume({
85
+ resumeSessionId: SessionId(sessionId),
86
+ agentOptions: {
87
+ provider: explicitRoute?.provider ?? persistedRoute?.provider,
88
+ model: explicitRoute?.model ?? persistedRoute?.model,
89
+ },
90
+ ...(composed.setup === undefined ? {} : { setup: composed.setup }),
91
+ }));
92
+ }
93
+ catch (error) {
94
+ const message = error instanceof Error ? error.message : String(error);
95
+ deps.notify(t('resume-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
96
+ return { ok: false, reason: 'failed', error: message };
97
+ }
98
+ if (!deps.binding.isCurrent(adoption)) {
99
+ await deps.binding.abandon(handle);
100
+ return { ok: false, reason: 'cancelled' };
101
+ }
102
+ try {
103
+ await attachSessionToWorkspace(ctx, handle.agent.session.header.cwd ?? state.cwd, SessionId(sessionId));
104
+ }
105
+ catch (error) {
106
+ deps.notify(t('resume-attach-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'warning', timeoutMs: 8000 });
107
+ }
108
+ if (entrySession !== undefined && (!deps.owner.current() || deps.binding.agent.session !== entrySession)) {
109
+ await deps.binding.abandon(handle);
110
+ deps.notify(t('resume-session-changed'), { color: 'error' });
111
+ return { ok: false, reason: 'failed', error: 'live session changed during resume' };
112
+ }
113
+ // `adopt` is a transaction: it revokes the candidate and disposes it when
114
+ // the tail fails, and it can also refuse before the tail ever runs. Only a
115
+ // normal return is a commit, which is what the outer finally keys on.
116
+ const result = deps.binding.adopt(handle, adoption, (committedBinding, disposePrevious) => {
117
+ const previousSessionId = String(committedBinding.agent.session.id);
118
+ state.cwd = handle.agent.session.header.cwd ?? state.cwd;
119
+ state.displayCwd = deps.describeWorkspace(state.cwd).description ?? state.cwd;
120
+ deps.resetIdeSelection();
121
+ deps.refreshGitBranch();
122
+ // Reset the input FIFO and pending-decision indicators BEFORE the first
123
+ // emit (main's bind → clear → refresh order); see the /new tail.
124
+ deps.clearStagedImages();
125
+ resetAndBind(handle, composed.agentPreset, explicitRoute ?? recordedModelRoute(snapshotLiveSessionEvents(handle.agent.session)), true);
126
+ writeResumeTarget(sessionId);
127
+ touchSession(sessionId);
128
+ state.emit();
129
+ const keepPrevious = keepCurrent && committedBinding.handle !== undefined
130
+ && (committedBinding.handle.agent.status === 'running' || agentViewHasTurns(snapshotLiveSessionEvents(committedBinding.handle.agent.session)));
131
+ if (committedBinding.handle !== undefined) {
132
+ if (keepPrevious) {
133
+ deps.backgroundHandles.set(previousSessionId, committedBinding.handle);
134
+ disposePrevious('park');
135
+ }
136
+ else {
137
+ disposePrevious('dispose');
138
+ }
101
139
  }
102
- else {
103
- disposePrevious('dispose');
140
+ if (kind === 'agent-view') {
141
+ touchAgentViewSession(sessionId);
142
+ touchAgentViewSession(previousSessionId);
104
143
  }
144
+ deps.notifySessionSwitched(kind, sessionId, previousSessionId);
145
+ return { ok: true };
146
+ });
147
+ committed = true;
148
+ return result;
149
+ }
150
+ };
151
+ /**
152
+ * Reserve `sessionId` for a disk resume on THIS terminal.
153
+ *
154
+ * Returning a reservation means the caller owns the session in the ledger
155
+ * until it calls `finish()`. That covers the whole attempt, not just the
156
+ * `agents.resume` call: the awaits in between (veto, compaction, preset and
157
+ * route reads) are exactly the window a publisher beat — or a peer — could
158
+ * otherwise slip into.
159
+ * @param sessionId - Session about to be resumed from disk.
160
+ * @returns The reservation, or the refusal to hand back to the caller.
161
+ */
162
+ const reserveDiskResume = async (sessionId) => {
163
+ // Cheap pre-filter for the friendly toast: the authoritative conflict test
164
+ // runs inside reserveMount, under the ledger lock.
165
+ const occupancy = occupancyOf(sessionId, readSessionOwners());
166
+ if (occupancy.kind === 'occupied') {
167
+ deps.notify(t('resume-session-occupied', { pid: occupancy.pid }), { color: 'error', timeoutMs: 8000 });
168
+ return { ok: false, result: { ok: false, reason: 'occupied', pid: occupancy.pid } };
169
+ }
170
+ const reserved = await reserveMount(sessionId);
171
+ if (!reserved.ok) {
172
+ if (reserved.reason === 'occupied') {
173
+ const pid = reserved.holders[0] ?? 0;
174
+ deps.notify(t('resume-session-occupied', { pid }), { color: 'error', timeoutMs: 8000 });
175
+ return { ok: false, result: { ok: false, reason: 'occupied', pid } };
105
176
  }
106
- if (kind === 'agent-view') {
107
- touchAgentViewSession(sessionId);
108
- touchAgentViewSession(previousSessionId);
109
- }
110
- deps.notifySessionSwitched(kind, sessionId, previousSessionId);
111
- return { ok: true };
112
- });
177
+ // Both of these are refusals, not conflicts, and neither may be dressed
178
+ // up as "held by pid 0". The generic failure branch carries the sentence.
179
+ return { ok: false, result: { ok: false, reason: 'failed', error: mountFailureText(reserved) } };
180
+ }
181
+ return { ok: true, reservation: reserved.reservation };
113
182
  };
114
- const resumeInto = (sessionId, kind, keepCurrent) => resume(sessionId, kind, keepCurrent, deps.binding.capture());
115
- /** `/resume` keeps the entry capture across its veto/compaction awaits, so a rival switch cannot adopt over newer state. */
116
- const resumeTo = async (sessionId) => {
183
+ /**
184
+ * Reserve a freshly minted session id for a create on THIS terminal. See
185
+ * `reserveNewSession`: a new id cannot conflict, so a refusal is a loss of
186
+ * announcement rather than a reason to stop, and it is warned about.
187
+ * @param sessionId - Session id minted for the new agent.
188
+ * @returns The reservation (a no-op when the claim could not be made).
189
+ */
190
+ const reserveCreatedSession = async (sessionId) => {
191
+ const { reservation, failure } = await reserveNewSession(sessionId);
192
+ if (failure !== undefined && failure.reason !== 'occupied') {
193
+ deps.notify(mountFailureText(failure), { color: 'warning', timeoutMs: 8000 });
194
+ }
195
+ return reservation;
196
+ };
197
+ const resumeInto = async (sessionId, kind, keepCurrent) => {
117
198
  const adoption = deps.binding.capture();
118
- if (state.working) {
119
- deps.notify(t('resume-while-working'), { color: 'warning' });
120
- return { ok: false, reason: 'working' };
199
+ const agents = ctx.get('agents');
200
+ if (!agents) {
201
+ deps.notify(t('resume-unavailable'), { color: 'error' });
202
+ return { ok: false, reason: 'unavailable' };
121
203
  }
204
+ // Same reservation as `/resume`: this is the other public door onto the
205
+ // disk path (`ChannelUi.attachToAgent`), and a mount that skips the ledger
206
+ // is a mount a peer can race from the outside.
207
+ const reserved = await reserveDiskResume(sessionId);
208
+ if (!reserved.ok)
209
+ return reserved.result;
210
+ return resume(sessionId, kind, keepCurrent, adoption, undefined, reserved.reservation);
211
+ };
212
+ /**
213
+ * `/resume` — mount a session on THIS terminal, the same non-destructive way
214
+ * `/agentview` does.
215
+ *
216
+ * There used to be two different mount models behind these two commands.
217
+ * `/agentview` parked the session it left behind (it kept running in this
218
+ * process and was reachable again from the view), while `/resume` DISPOSED
219
+ * it, and refused outright while a turn was running. That split was never a
220
+ * decision about resuming; it was an accident of the two commands growing
221
+ * separately, and it is what made `/resume` feel like a different product
222
+ * from the session overview sitting one command away.
223
+ *
224
+ * The unified model is the overview's, because it is the one that matches
225
+ * what a TUI terminal actually is: one process that can host several agent
226
+ * sessions at once. So:
227
+ *
228
+ * - Leaving a session parks it (`keepCurrent`), it does not end it, and the
229
+ * parked handle is reachable from the session screen afterwards.
230
+ * - A running turn is not a refusal. The user is switching what they are
231
+ * LOOKING at, not asking the model to stop; the turn keeps running in the
232
+ * background and its row keeps reporting progress.
233
+ *
234
+ * The one thing that IS refused is a session another TUI process already has
235
+ * mounted: two processes driving one append-only log interleave its events.
236
+ * The occupancy check happens BEFORE the resume awaits (which yield), and
237
+ * the claim is published immediately after it so this process owns the
238
+ * session before any other process can pass the same check.
239
+ *
240
+ * The reservation also outlives those awaits: it is released only when the
241
+ * attempt ends without a commit. The publisher's beat derives its set from
242
+ * the agent registry, which does not list the target yet, so a reservation
243
+ * that was not held across them could be erased by this process's own
244
+ * heartbeat and handed straight to a peer.
245
+ */
246
+ const resumeTo = async (sessionId) => {
247
+ const adoption = deps.binding.capture();
122
248
  const agents = ctx.get('agents');
123
249
  if (!agents) {
124
250
  deps.notify(t('resume-unavailable'), { color: 'error' });
125
251
  return { ok: false, reason: 'unavailable' };
126
252
  }
127
- if (await deps.sessionSwitchVetoed('resume', sessionId))
253
+ // The attached session as of entry, so a rival switch that commits while
254
+ // the awaits below yield cannot be adopted over.
255
+ const entrySession = deps.binding.agent.session;
256
+ // Already attached: this is a no-op, not a switch.
257
+ //
258
+ // The live-adoption path below hands `backgroundHandles.get(targetId)` to
259
+ // `binding.switchTo()`, and a session this terminal is CURRENTLY attached
260
+ // to has no entry there — its handle is the binding's own `currentHandle`.
261
+ // Calling it with `undefined` therefore parks nothing, and the adoption's
262
+ // default disposition disposes the handle that IS running, so a second
263
+ // Enter on the `current` row stopped the live turn while reporting success.
264
+ // `attachToAgent()` has always short-circuited here; this is the same rule
265
+ // for the unified screen's `/resume` path.
266
+ if (String(sessionId) === String(entrySession.id))
267
+ return { ok: true };
268
+ // Switching away starts disposal without blocking the synchronous commit.
269
+ // Do not adopt that closing Agent (or reopen its still-held JSONL writer).
270
+ await deps.binding.waitForDisposal(sessionId);
271
+ if (!deps.binding.isCurrent(adoption))
272
+ return { ok: false, reason: 'cancelled' };
273
+ // A live agent of this process is already mounted here; there is nothing
274
+ // to claim and nothing that can be occupied. Adoption takes the live
275
+ // handle (parking the current one) with no occupancy round-trip.
276
+ const live = agents.get?.(SessionId(sessionId));
277
+ let reservation;
278
+ if (live === undefined) {
279
+ const reserved = await reserveDiskResume(sessionId);
280
+ if (!reserved.ok)
281
+ return reserved.result;
282
+ reservation = reserved.reservation;
283
+ }
284
+ if (await deps.sessionSwitchVetoed('resume', sessionId)) {
285
+ reservation?.abandon();
128
286
  return { ok: false, reason: 'cancelled' };
287
+ }
129
288
  await deps.settleCompaction();
130
- const entrySession = deps.binding.agent.session;
131
- return resume(sessionId, 'resume', false, adoption, entrySession);
289
+ if (!deps.binding.isCurrent(adoption) || deps.binding.agent.session !== entrySession) {
290
+ reservation?.abandon();
291
+ return { ok: false, reason: 'cancelled' };
292
+ }
293
+ // The target was read BEFORE those awaits. Re-read it in BOTH directions:
294
+ // the registry can have replaced or dropped that agent while we yielded
295
+ // (adopting the captured object would hand the screen a session nothing
296
+ // owns any more), and it can also have GROWN one — a peer action mounting
297
+ // this very session here means the disk path below would resume a log this
298
+ // process is already driving. `agent-view-projection.attach` has always made
299
+ // the first check.
300
+ const liveNow = agents.get?.(SessionId(sessionId));
301
+ if (liveNow !== live) {
302
+ reservation?.abandon();
303
+ return { ok: false, reason: 'cancelled' };
304
+ }
305
+ // A live target is adopted in place — the same path `/agentview` uses, so
306
+ // the session being left is parked rather than disposed of. Only a target
307
+ // with no live agent here goes back to the persistence backend.
308
+ if (live !== undefined)
309
+ return deps.adoptLive(live);
310
+ return resume(sessionId, 'agent-view', true, adoption, entrySession, reservation);
132
311
  };
133
312
  const newSessionWithTarget = async (target) => {
134
313
  // The typed workspace target seam still creates a real Agent/session; it
@@ -181,6 +360,10 @@ export function createSessionResumeActions(ctx, state, options, deps) {
181
360
  }
182
361
  return false;
183
362
  }
363
+ // Reserve BEFORE the factory runs: the moment `agents.create` returns, this
364
+ // process holds the only write handle on a log no peer has been told about
365
+ // yet, and the publisher would not name it until its next beat.
366
+ const reservation = await reserveCreatedSession(sessionId);
184
367
  let handle;
185
368
  try {
186
369
  handle = await deps.binding.prepare(adoption, () => agents.create({
@@ -191,6 +374,7 @@ export function createSessionResumeActions(ctx, state, options, deps) {
191
374
  }));
192
375
  }
193
376
  catch (error) {
377
+ reservation.abandon();
194
378
  if (current()) {
195
379
  const message = error instanceof Error ? error.message : String(error);
196
380
  deps.notify(t('new-session-failed', { err: message }), { color: 'error', timeoutMs: 8000 });
@@ -199,6 +383,7 @@ export function createSessionResumeActions(ctx, state, options, deps) {
199
383
  }
200
384
  if (!current()) {
201
385
  await deps.binding.abandon(handle);
386
+ reservation.abandon();
202
387
  return false;
203
388
  }
204
389
  try {
@@ -210,30 +395,43 @@ export function createSessionResumeActions(ctx, state, options, deps) {
210
395
  }
211
396
  if (!current()) {
212
397
  await deps.binding.abandon(handle);
398
+ reservation.abandon();
213
399
  return false;
214
400
  }
215
401
  // Do not catch this synchronous commit tail. A post-commit setup failure
216
402
  // is owned by binding.adopt(), which revokes the new live handle and must
217
403
  // reject its caller rather than masquerade as an ordinary precommit false.
218
- return deps.binding.adopt(handle, adoption, (committed, disposePrevious) => {
219
- const previousSessionId = String(committed.agent.session.id);
220
- // The target only becomes shared channel state inside the successful
221
- // adoption tail. A losing prepared handle therefore cannot publish or
222
- // roll back another workspace's cwd.
223
- state.cwd = targetCwd;
224
- state.displayCwd = targetDisplayCwd ?? deps.describeWorkspace(targetCwd).description ?? targetCwd;
225
- // Reset the input FIFO and the pending-decision indicators BEFORE the
226
- // first emit: a submit enqueued from a session-changed subscriber must
227
- // land on a fresh chain instead of behind the replaced session's parked
228
- // promise (main's bind → clear → refresh order).
229
- deps.clearStagedImages();
230
- resetAndBind(handle, composed.agentPreset, route, false);
231
- clearResumeTarget();
232
- touchSession(handle.agent.id);
233
- disposePrevious('dispose');
234
- deps.notifySessionSwitched('new', String(handle.agent.id), previousSessionId);
235
- return true;
236
- });
404
+ let committed = false;
405
+ try {
406
+ const result = deps.binding.adopt(handle, adoption, (committedBinding, disposePrevious) => {
407
+ const previousSessionId = String(committedBinding.agent.session.id);
408
+ // The target only becomes shared channel state inside the successful
409
+ // adoption tail. A losing prepared handle therefore cannot publish or
410
+ // roll back another workspace's cwd.
411
+ state.cwd = targetCwd;
412
+ state.displayCwd = targetDisplayCwd ?? deps.describeWorkspace(targetCwd).description ?? targetCwd;
413
+ deps.resetIdeSelection();
414
+ // Reset the input FIFO and the pending-decision indicators BEFORE the
415
+ // first emit: a submit enqueued from a session-changed subscriber must
416
+ // land on a fresh chain instead of behind the replaced session's parked
417
+ // promise (main's bind → clear → refresh order).
418
+ deps.clearStagedImages();
419
+ resetAndBind(handle, composed.agentPreset, route, false);
420
+ clearResumeTarget();
421
+ touchSession(handle.agent.id);
422
+ disposePrevious('dispose');
423
+ deps.notifySessionSwitched('new', String(handle.agent.id), previousSessionId);
424
+ return true;
425
+ });
426
+ committed = true;
427
+ return result;
428
+ }
429
+ finally {
430
+ if (committed)
431
+ reservation.settle();
432
+ else
433
+ reservation.abandon();
434
+ }
237
435
  };
238
436
  const newSession = () => newSessionWithTarget();
239
437
  return { resumeInto, resumeTo, newSession, newSessionWithTarget };
@@ -1 +1 @@
1
- {"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAQvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAgB/E,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1F,IAEa,KAAK,OAAO,EAAE,OAAM,MAAM,GAAG,IAAW,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAwFhF"}
1
+ {"version":3,"file":"session-rewind.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-rewind.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AASvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAEvD,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,WAAW,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAgB/E,4EAA4E;AAC5E,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,WAAW,EAClB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CAC1F,IAEa,KAAK,OAAO,EAAE,OAAM,MAAM,GAAG,IAAW,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAsGhF"}
@@ -6,6 +6,7 @@ import { dispatchTuiDecision } from '../extension-events.js';
6
6
  import { normalizeRewindDoneSummary } from './decisions.js';
7
7
  import { composePreset, runningPresetOf } from '../presets.js';
8
8
  import { attachSessionToWorkspace } from '../workspace.js';
9
+ import { reserveNewSession } from '../../sessionMounts.js';
9
10
  async function waitForTurnEnd(session, fromSeq, timeoutMs) {
10
11
  const deadline = Date.now() + timeoutMs;
11
12
  while (Date.now() < deadline) {
@@ -65,6 +66,10 @@ export function createRewindToAction(ctx, state, deps) {
65
66
  return null;
66
67
  }
67
68
  const composed = await composePreset(ctx, runningPresetOf(deps.binding.agent.session));
69
+ // Announce the id before the factory: the rewind creates the child's log
70
+ // here, and the publisher only learns the id from the registry on its next
71
+ // beat.
72
+ const { reservation } = await reserveNewSession(String(childId));
68
73
  let handle;
69
74
  try {
70
75
  handle = await deps.binding.prepare(adoption, () => agents.create(liveSessionCreateOptions({
@@ -87,11 +92,13 @@ export function createRewindToAction(ctx, state, deps) {
87
92
  })));
88
93
  }
89
94
  catch {
95
+ reservation.abandon();
90
96
  deps.notify(t('rewind-create-failed'), { color: 'error' });
91
97
  return null;
92
98
  }
93
99
  if (!deps.binding.isCurrent(adoption)) {
94
100
  await deps.binding.abandon(handle);
101
+ reservation.abandon();
95
102
  return null;
96
103
  }
97
104
  try {
@@ -102,27 +109,38 @@ export function createRewindToAction(ctx, state, deps) {
102
109
  }
103
110
  if (!deps.owner.current()) {
104
111
  await deps.binding.abandon(handle);
112
+ reservation.abandon();
105
113
  return null;
106
114
  }
107
- const sourceSessionId = deps.adoptForkedAgent(handle, adoption, snapshotLiveSessionEvents(handle.agent.session), composed.agentPreset, childId);
115
+ // `adoptForkedAgent` is the commit: the child is the session this process
116
+ // now drives, so the reservation hands over to the registry. It THROWS when
117
+ // the adoption transaction revokes the candidate, so it is guarded.
108
118
  try {
109
- void dispatchTuiDecision(ctx, 'tui/rewind-done', {
110
- text: row.text,
111
- mode,
112
- boundarySeq: boundary,
113
- sourceSessionId,
114
- childSessionId: String(childId),
115
- sessionId: String(childId),
116
- cwd: state.cwd,
117
- }, normalizeRewindDoneSummary).then(summary => {
118
- if (typeof summary === 'string')
119
- deps.notify(summary, { timeoutMs: 6000 });
120
- }).catch((error) => ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error));
119
+ const sourceSessionId = deps.adoptForkedAgent(handle, adoption, snapshotLiveSessionEvents(handle.agent.session), composed.agentPreset, childId);
120
+ reservation.settle();
121
+ try {
122
+ void dispatchTuiDecision(ctx, 'tui/rewind-done', {
123
+ text: row.text,
124
+ mode,
125
+ boundarySeq: boundary,
126
+ sourceSessionId,
127
+ childSessionId: String(childId),
128
+ sessionId: String(childId),
129
+ cwd: state.cwd,
130
+ }, normalizeRewindDoneSummary).then(summary => {
131
+ if (typeof summary === 'string')
132
+ deps.notify(summary, { timeoutMs: 6000 });
133
+ }).catch((error) => ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error));
134
+ }
135
+ catch (error) {
136
+ ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error);
137
+ }
138
+ deps.notifySessionSwitched('rewind', String(childId), sourceSessionId);
139
+ return row.text;
121
140
  }
122
141
  catch (error) {
123
- ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error);
142
+ reservation.abandon();
143
+ throw error;
124
144
  }
125
- deps.notifySessionSwitched('rewind', String(childId), sourceSessionId);
126
- return row.text;
127
145
  };
128
146
  }
@@ -1 +1 @@
1
- {"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AASvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAYnF,+GAA+G;AAC/G,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnG,IAEa,WAAW,MAAM,EAAE,KAAK,MAAM,EAAE,OAAM,QAAQ,GAAG,MAAiB,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4G1G"}
1
+ {"version":3,"file":"session-tree-actions.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/session-tree-actions.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,wBAAwB,CAAA;AAC7E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,EAAE,SAAS,EAAE,KAAK,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAUvE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAC9C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAE9C,KAAK,OAAO,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAA;AACtD,KAAK,eAAe,GAAG,IAAI,CAAC,YAAY,EAAE,SAAS,GAAG,KAAK,GAAG,UAAU,GAAG,OAAO,CAAC,CAAA;AAYnF,+GAA+G;AAC/G,wBAAgB,sBAAsB,CACpC,GAAG,EAAE,OAAO,EACZ,KAAK,EAAE,eAAe,EACtB,IAAI,EAAE;IACJ,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,SAAS,CAAC,CAAA;IACpC,OAAO,EAAE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,SAAS,GAAG,WAAW,GAAG,SAAS,GAAG,SAAS,CAAC,CAAA;IACjF,gBAAgB,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACjC,MAAM,EAAE,YAAY,CAAC,QAAQ,CAAC,CAAA;IAC9B,gBAAgB,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,YAAY,EAAE,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,SAAS,GAAG,MAAM,CAAA;IAC1K,qBAAqB,CAAC,IAAI,EAAE,QAAQ,GAAG,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;CACnG,IAEa,WAAW,MAAM,EAAE,KAAK,MAAM,EAAE,OAAM,QAAQ,GAAG,MAAiB,KAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyH1G"}
@@ -6,6 +6,7 @@ import { readPersistedSession } from '../compat/persistence.js';
6
6
  import { closeLiveForkTurn } from '../compat/liveSession.js';
7
7
  import { composePreset, resolvePersistedPreset, runningPresetOf } from '../presets.js';
8
8
  import { attachSessionToWorkspace } from '../workspace.js';
9
+ import { reserveNewSession } from '../../sessionMounts.js';
9
10
  import { forkTarget, rewindTarget, turnUserText } from '../sessionTree.js';
10
11
  async function waitForTurnEnd(session, fromSeq, timeoutMs) {
11
12
  const deadline = Date.now() + timeoutMs;
@@ -85,6 +86,9 @@ export function createTreeRewindAction(ctx, state, deps) {
85
86
  if (target.closeTurn !== undefined && !closeAfterCreate) {
86
87
  appendInterruptedTurnEnd(seed, target.closeTurn);
87
88
  }
89
+ // Announce the id before the factory: the child's log is created here, and
90
+ // the publisher only learns the id from the registry on its next beat.
91
+ const { reservation } = await reserveNewSession(String(childId));
88
92
  let handle;
89
93
  try {
90
94
  handle = await deps.binding.prepare(adoption, () => agents.create(liveSessionCreateOptions({
@@ -112,11 +116,13 @@ export function createTreeRewindAction(ctx, state, deps) {
112
116
  })));
113
117
  }
114
118
  catch {
119
+ reservation.abandon();
115
120
  deps.notify(t('rewind-create-failed'), { color: 'error' });
116
121
  return null;
117
122
  }
118
123
  if (!deps.binding.isCurrent(adoption)) {
119
124
  await deps.binding.abandon(handle);
125
+ reservation.abandon();
120
126
  return null;
121
127
  }
122
128
  try {
@@ -127,12 +133,22 @@ export function createTreeRewindAction(ctx, state, deps) {
127
133
  }
128
134
  if (!deps.owner.current() || deps.binding.agent.session !== entrySession) {
129
135
  await deps.binding.abandon(handle);
136
+ reservation.abandon();
130
137
  deps.notify(t('rewind-session-changed'), { color: 'error' });
131
138
  return null;
132
139
  }
133
140
  const replay = closeAfterCreate || mode === 'rewind' ? snapshotLiveSessionEvents(handle.agent.session) : seed;
134
- const sourceSessionId = deps.adoptForkedAgent(handle, adoption, replay, composed.agentPreset, childId);
135
- deps.notifySessionSwitched(mode === 'fork' ? 'fork' : 'rewind', String(childId), sourceSessionId);
136
- return restoredText;
141
+ // `adoptForkedAgent` is the commit, and it THROWS when the adoption
142
+ // transaction revokes the candidate.
143
+ try {
144
+ const sourceSessionId = deps.adoptForkedAgent(handle, adoption, replay, composed.agentPreset, childId);
145
+ reservation.settle();
146
+ deps.notifySessionSwitched(mode === 'fork' ? 'fork' : 'rewind', String(childId), sourceSessionId);
147
+ return restoredText;
148
+ }
149
+ catch (error) {
150
+ reservation.abandon();
151
+ throw error;
152
+ }
137
153
  };
138
154
  }
@@ -1 +1 @@
1
- {"version":3,"file":"settings-host.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/settings-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAKvD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,GAAE,MAAM,IAAsB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC,CAqT5K"}
1
+ {"version":3,"file":"settings-host.d.ts","sourceRoot":"","sources":["../../../../src/dsh-adapter/channel/settings-host.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAA;AAMvD,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,OAAO,EAAE,YAAY,GAAE,MAAM,IAAsB,GAAG,IAAI,CAAC,YAAY,EAAE,cAAc,GAAG,eAAe,GAAG,uBAAuB,CAAC,CAqT5K"}