@deepseek-harness-tui/dsh-tui 0.8.0 → 0.8.2

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 (434) hide show
  1. package/README.md +32 -15
  2. package/cordis.patch.yml +21 -2
  3. package/cordis.yml +12 -1
  4. package/dsh-ecosystem-spec/protocols/profile-definitions.d.ts +13 -0
  5. package/dsh-ecosystem-spec/protocols/profile-definitions.js +59 -0
  6. package/dsh-ecosystem-spec/protocols/tui-channel-http.d.ts +15 -0
  7. package/dsh-ecosystem-spec/protocols/tui-channel-http.js +32 -0
  8. package/dsh-ecosystem-spec/protocols/tui-channel.d.ts +59 -0
  9. package/dsh-ecosystem-spec/protocols/tui-channel.js +166 -0
  10. package/dsh-ecosystem-spec/protocols/tui-contributions.d.ts +42 -0
  11. package/dsh-ecosystem-spec/protocols/tui-contributions.js +143 -0
  12. package/dsh-ecosystem-spec/registry/README.md +14 -0
  13. package/dsh-ecosystem-spec/registry/contracts/decision-events-v1alpha1.json +13 -0
  14. package/dsh-ecosystem-spec/registry/contracts/scene-v1alpha1.json +13 -0
  15. package/dsh-ecosystem-spec/registry/contracts/settings-section-v1alpha1.json +13 -0
  16. package/dsh-ecosystem-spec/registry/contracts/tui-channel-v1alpha1.json +30 -0
  17. package/dsh-ecosystem-spec/registry/contracts/workspace-provider-v1alpha1.json +6 -0
  18. package/dsh-ecosystem-spec/registry/host-descriptor.tui.example.json +56 -0
  19. package/dsh-ecosystem-spec/registry/permissions-0.1.json +13 -0
  20. package/dsh-ecosystem-spec/registry/registry-0.15.json +149 -0
  21. package/dsh-ecosystem-spec/schemas/conformance-claim.schema.json +15 -0
  22. package/dsh-ecosystem-spec/schemas/effect-ledger-record.schema.json +16 -0
  23. package/dsh-ecosystem-spec/schemas/host-descriptor.schema.json +26 -0
  24. package/lib/types/cc/figures.d.ts +4 -0
  25. package/lib/types/cc/figures.d.ts.map +1 -1
  26. package/lib/types/cc/figures.js +4 -0
  27. package/lib/types/command-trees.d.ts +2 -1
  28. package/lib/types/command-trees.d.ts.map +1 -1
  29. package/lib/types/command-trees.js +1 -1
  30. package/lib/types/commands.d.ts.map +1 -1
  31. package/lib/types/commands.js +2 -0
  32. package/lib/types/components/ExtensionDialog.d.ts +40 -0
  33. package/lib/types/components/ExtensionDialog.d.ts.map +1 -0
  34. package/lib/types/components/ExtensionDialog.js +210 -0
  35. package/lib/types/components/HelpMenu.js +1 -1
  36. package/lib/types/components/LoadedContextPanel.d.ts +5 -5
  37. package/lib/types/components/LoadedContextPanel.js +6 -6
  38. package/lib/types/components/LogoV2.d.ts.map +1 -1
  39. package/lib/types/components/LogoV2.js +16 -8
  40. package/lib/types/components/Markdown.d.ts +13 -1
  41. package/lib/types/components/Markdown.d.ts.map +1 -1
  42. package/lib/types/components/Markdown.js +15 -1
  43. package/lib/types/components/MessageList.d.ts +3 -1
  44. package/lib/types/components/MessageList.d.ts.map +1 -1
  45. package/lib/types/components/MessageList.js +111 -6
  46. package/lib/types/components/PromptInput.d.ts.map +1 -1
  47. package/lib/types/components/PromptInput.js +26 -12
  48. package/lib/types/components/RewindPicker.d.ts +13 -1
  49. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  50. package/lib/types/components/RewindPicker.js +26 -2
  51. package/lib/types/components/StreamingMarkdown.d.ts +2 -8
  52. package/lib/types/components/StreamingMarkdown.d.ts.map +1 -1
  53. package/lib/types/components/StreamingMarkdown.js +51 -9
  54. package/lib/types/components/ToolUseLoader.d.ts +8 -3
  55. package/lib/types/components/ToolUseLoader.d.ts.map +1 -1
  56. package/lib/types/components/ToolUseLoader.js +43 -9
  57. package/lib/types/components/activityFrames.d.ts +1 -1
  58. package/lib/types/components/activityFrames.d.ts.map +1 -1
  59. package/lib/types/components/activityFrames.js +4 -1
  60. package/lib/types/components/messages/AssistantThinkingMessage.d.ts +5 -1
  61. package/lib/types/components/messages/AssistantThinkingMessage.d.ts.map +1 -1
  62. package/lib/types/components/messages/AssistantThinkingMessage.js +26 -3
  63. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  64. package/lib/types/components/messages/AssistantToolUseMessage.js +13 -2
  65. package/lib/types/dsh-adapter/channel.d.ts +61 -11
  66. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  67. package/lib/types/dsh-adapter/channel.js +767 -120
  68. package/lib/types/dsh-adapter/childStderr.d.ts.map +1 -1
  69. package/lib/types/dsh-adapter/childStderr.js +10 -0
  70. package/lib/types/dsh-adapter/command-attribution.d.ts +51 -0
  71. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -0
  72. package/lib/types/dsh-adapter/command-attribution.js +93 -0
  73. package/lib/types/dsh-adapter/command-errors.d.ts +42 -0
  74. package/lib/types/dsh-adapter/command-errors.d.ts.map +1 -0
  75. package/lib/types/dsh-adapter/command-errors.js +63 -0
  76. package/lib/types/dsh-adapter/command-trees.d.ts +7 -1
  77. package/lib/types/dsh-adapter/command-trees.d.ts.map +1 -1
  78. package/lib/types/dsh-adapter/command-trees.js +70 -20
  79. package/lib/types/dsh-adapter/component-identity.d.ts +33 -0
  80. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -0
  81. package/lib/types/dsh-adapter/component-identity.js +108 -0
  82. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  83. package/lib/types/dsh-adapter/contract.js +2 -2
  84. package/lib/types/dsh-adapter/decision-guard.d.ts +84 -0
  85. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -0
  86. package/lib/types/dsh-adapter/decision-guard.js +348 -0
  87. package/lib/types/dsh-adapter/dialogs.d.ts +145 -0
  88. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -0
  89. package/lib/types/dsh-adapter/dialogs.js +316 -0
  90. package/lib/types/dsh-adapter/effect-ledger.d.ts +91 -0
  91. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -0
  92. package/lib/types/dsh-adapter/effect-ledger.js +251 -0
  93. package/lib/types/dsh-adapter/extension-events.d.ts +193 -0
  94. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -0
  95. package/lib/types/dsh-adapter/extension-events.js +207 -0
  96. package/lib/types/dsh-adapter/extensions.d.ts +32 -0
  97. package/lib/types/dsh-adapter/extensions.d.ts.map +1 -0
  98. package/lib/types/dsh-adapter/extensions.js +42 -0
  99. package/lib/types/dsh-adapter/grants.d.ts +25 -0
  100. package/lib/types/dsh-adapter/grants.d.ts.map +1 -0
  101. package/lib/types/dsh-adapter/grants.js +213 -0
  102. package/lib/types/dsh-adapter/host-access.d.ts +37 -0
  103. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -0
  104. package/lib/types/dsh-adapter/host-access.js +652 -0
  105. package/lib/types/dsh-adapter/host-descriptor.d.ts +24 -0
  106. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -0
  107. package/lib/types/dsh-adapter/host-descriptor.js +169 -0
  108. package/lib/types/dsh-adapter/index.d.ts +4 -0
  109. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  110. package/lib/types/dsh-adapter/index.js +1 -0
  111. package/lib/types/dsh-adapter/message-observer.d.ts +173 -0
  112. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -0
  113. package/lib/types/dsh-adapter/message-observer.js +553 -0
  114. package/lib/types/dsh-adapter/plugin-host.d.ts +136 -0
  115. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -0
  116. package/lib/types/dsh-adapter/plugin-host.js +365 -0
  117. package/lib/types/dsh-adapter/plugin-storage.d.ts +108 -0
  118. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -0
  119. package/lib/types/dsh-adapter/plugin-storage.js +397 -0
  120. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  121. package/lib/types/dsh-adapter/plugin.js +124 -14
  122. package/lib/types/dsh-adapter/plugins-info.d.ts +43 -0
  123. package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -0
  124. package/lib/types/dsh-adapter/plugins-info.js +276 -0
  125. package/lib/types/dsh-adapter/promptDebug.d.ts +0 -6
  126. package/lib/types/dsh-adapter/promptDebug.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/promptDebug.js +13 -4
  128. package/lib/types/dsh-adapter/renderers.d.ts +57 -0
  129. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -0
  130. package/lib/types/dsh-adapter/renderers.js +173 -0
  131. package/lib/types/dsh-adapter/sanitize.d.ts +36 -0
  132. package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -0
  133. package/lib/types/dsh-adapter/sanitize.js +65 -0
  134. package/lib/types/dsh-adapter/scenes.d.ts +15 -5
  135. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  136. package/lib/types/dsh-adapter/scenes.js +149 -35
  137. package/lib/types/dsh-adapter/settings-sections.d.ts +7 -3
  138. package/lib/types/dsh-adapter/settings-sections.d.ts.map +1 -1
  139. package/lib/types/dsh-adapter/settings-sections.js +106 -26
  140. package/lib/types/dsh-adapter/shortcuts.d.ts +107 -0
  141. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -0
  142. package/lib/types/dsh-adapter/shortcuts.js +340 -0
  143. package/lib/types/dsh-adapter/status.d.ts +74 -0
  144. package/lib/types/dsh-adapter/status.d.ts.map +1 -0
  145. package/lib/types/dsh-adapter/status.js +234 -0
  146. package/lib/types/dsh-adapter/workspaces.d.ts +16 -5
  147. package/lib/types/dsh-adapter/workspaces.d.ts.map +1 -1
  148. package/lib/types/dsh-adapter/workspaces.js +287 -170
  149. package/lib/types/extensions.d.ts +13 -0
  150. package/lib/types/extensions.d.ts.map +1 -0
  151. package/lib/types/extensions.js +21 -0
  152. package/lib/types/force-production-react.d.ts +1 -0
  153. package/lib/types/force-production-react.d.ts.map +1 -0
  154. package/lib/types/force-production-react.js +12 -0
  155. package/lib/types/i18n.d.ts +174 -20
  156. package/lib/types/i18n.d.ts.map +1 -1
  157. package/lib/types/i18n.js +56 -6
  158. package/lib/types/index.d.ts +1 -0
  159. package/lib/types/index.d.ts.map +1 -1
  160. package/lib/types/index.js +1 -0
  161. package/lib/types/ink/components/App.d.ts +2 -0
  162. package/lib/types/ink/components/App.d.ts.map +1 -1
  163. package/lib/types/ink/components/App.js +4 -0
  164. package/lib/types/ink/ink.d.ts +31 -3
  165. package/lib/types/ink/ink.d.ts.map +1 -1
  166. package/lib/types/ink/ink.js +90 -5
  167. package/lib/types/ink/log-update.d.ts.map +1 -1
  168. package/lib/types/ink/log-update.js +225 -56
  169. package/lib/types/ink/output.d.ts +35 -2
  170. package/lib/types/ink/output.d.ts.map +1 -1
  171. package/lib/types/ink/output.js +281 -14
  172. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  173. package/lib/types/ink/render-node-to-output.js +14 -6
  174. package/lib/types/ink/screen.d.ts +39 -0
  175. package/lib/types/ink/screen.d.ts.map +1 -1
  176. package/lib/types/ink/screen.js +117 -0
  177. package/lib/types/ink/terminal.d.ts.map +1 -1
  178. package/lib/types/ink/terminal.js +26 -6
  179. package/lib/types/ink/wrapAnsi.d.ts +1 -1
  180. package/lib/types/ink/wrapAnsi.d.ts.map +1 -1
  181. package/lib/types/ink/wrapAnsi.js +29 -2
  182. package/lib/types/plugin-host.d.ts +15 -0
  183. package/lib/types/plugin-host.d.ts.map +1 -0
  184. package/lib/types/plugin-host.js +17 -0
  185. package/lib/types/plugin-spec/negotiate.d.ts +11 -0
  186. package/lib/types/plugin-spec/negotiate.d.ts.map +1 -0
  187. package/lib/types/plugin-spec/negotiate.js +97 -0
  188. package/lib/types/plugin-spec/permission-scope.d.ts +32 -0
  189. package/lib/types/plugin-spec/permission-scope.d.ts.map +1 -0
  190. package/lib/types/plugin-spec/permission-scope.js +90 -0
  191. package/lib/types/plugin-spec/registry.d.ts +23 -0
  192. package/lib/types/plugin-spec/registry.d.ts.map +1 -0
  193. package/lib/types/plugin-spec/registry.js +252 -0
  194. package/lib/types/plugin-spec/schema-check.d.ts +17 -0
  195. package/lib/types/plugin-spec/schema-check.d.ts.map +1 -0
  196. package/lib/types/plugin-spec/schema-check.js +165 -0
  197. package/lib/types/plugin-spec/tui-extension.d.ts +16 -0
  198. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -0
  199. package/lib/types/plugin-spec/tui-extension.js +79 -0
  200. package/lib/types/plugin-spec/types.d.ts +114 -0
  201. package/lib/types/plugin-spec/types.d.ts.map +1 -0
  202. package/lib/types/plugin-spec/types.js +10 -0
  203. package/lib/types/plugin-spec/validate.d.ts +21 -0
  204. package/lib/types/plugin-spec/validate.d.ts.map +1 -0
  205. package/lib/types/plugin-spec/validate.js +138 -0
  206. package/lib/types/scenes.d.ts +2 -1
  207. package/lib/types/scenes.d.ts.map +1 -1
  208. package/lib/types/scenes.js +1 -1
  209. package/lib/types/screens/Chat.d.ts +14 -1
  210. package/lib/types/screens/Chat.d.ts.map +1 -1
  211. package/lib/types/screens/Chat.js +173 -49
  212. package/lib/types/screens/SessionBrowser.d.ts.map +1 -1
  213. package/lib/types/screens/SessionBrowser.js +10 -3
  214. package/lib/types/sessions/view.d.ts +3 -2
  215. package/lib/types/sessions/view.d.ts.map +1 -1
  216. package/lib/types/sessions/view.js +18 -0
  217. package/lib/types/settings-sections.d.ts +2 -1
  218. package/lib/types/settings-sections.d.ts.map +1 -1
  219. package/lib/types/settings-sections.js +1 -1
  220. package/lib/types/theme.d.ts +5 -0
  221. package/lib/types/theme.d.ts.map +1 -1
  222. package/lib/types/theme.js +15 -0
  223. package/lib/types/update.d.ts +20 -0
  224. package/lib/types/update.d.ts.map +1 -1
  225. package/lib/types/update.js +54 -7
  226. package/lib/types/utils/execFileNoThrow.d.ts.map +1 -1
  227. package/lib/types/utils/execFileNoThrow.js +3 -0
  228. package/lib/types/utils/loaded-context.d.ts +8 -3
  229. package/lib/types/utils/loaded-context.d.ts.map +1 -1
  230. package/lib/types/utils/loaded-context.js +39 -3
  231. package/lib/types/utils/modifiers.d.ts.map +1 -1
  232. package/lib/types/utils/modifiers.js +3 -2
  233. package/lib/types/working-activity.d.ts +12 -0
  234. package/lib/types/working-activity.d.ts.map +1 -1
  235. package/lib/types/working-activity.js +13 -0
  236. package/lib/types/workspaces.d.ts +2 -1
  237. package/lib/types/workspaces.d.ts.map +1 -1
  238. package/lib/types/workspaces.js +1 -1
  239. package/node_modules/@dsh-std/command/CHANGELOG.md +8 -0
  240. package/node_modules/@dsh-std/command/LICENSE +21 -0
  241. package/node_modules/@dsh-std/command/README.md +26 -0
  242. package/node_modules/@dsh-std/command/README.zh.md +28 -0
  243. package/node_modules/@dsh-std/command/lib/index.d.ts +144 -0
  244. package/node_modules/@dsh-std/command/lib/index.d.ts.map +1 -0
  245. package/node_modules/@dsh-std/command/lib/index.js +252 -0
  246. package/node_modules/@dsh-std/command/lib/index.js.map +1 -0
  247. package/node_modules/@dsh-std/command/package.json +22 -0
  248. package/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  249. package/node_modules/@dsh-std/connection/LICENSE +21 -0
  250. package/node_modules/@dsh-std/connection/README.md +42 -0
  251. package/node_modules/@dsh-std/connection/README.zh.md +42 -0
  252. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  253. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  254. package/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  255. package/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  256. package/node_modules/@dsh-std/connection/lib/index.js +158 -0
  257. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  258. package/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  259. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  260. package/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  261. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  262. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  263. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  264. package/node_modules/@dsh-std/connection/package.json +20 -0
  265. package/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  266. package/node_modules/@dsh-std/core/LICENSE +21 -0
  267. package/node_modules/@dsh-std/core/README.md +13 -0
  268. package/node_modules/@dsh-std/core/README.zh.md +13 -0
  269. package/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  270. package/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  271. package/node_modules/@dsh-std/core/lib/index.js +424 -0
  272. package/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  273. package/node_modules/@dsh-std/core/package.json +21 -0
  274. package/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  275. package/node_modules/@dsh-std/manifest/LICENSE +21 -0
  276. package/node_modules/@dsh-std/manifest/README.md +5 -0
  277. package/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  278. package/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  279. package/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  280. package/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  281. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  282. package/node_modules/@dsh-std/manifest/package.json +20 -0
  283. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  284. package/node_modules/@dsh-std/messages/CHANGELOG.md +8 -0
  285. package/node_modules/@dsh-std/messages/LICENSE +21 -0
  286. package/node_modules/@dsh-std/messages/README.md +5 -0
  287. package/node_modules/@dsh-std/messages/README.zh.md +7 -0
  288. package/node_modules/@dsh-std/messages/lib/index.d.ts +50 -0
  289. package/node_modules/@dsh-std/messages/lib/index.d.ts.map +1 -0
  290. package/node_modules/@dsh-std/messages/lib/index.js +151 -0
  291. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -0
  292. package/node_modules/@dsh-std/messages/package.json +20 -0
  293. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -0
  294. package/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  295. package/node_modules/@dsh-std/presentation/LICENSE +21 -0
  296. package/node_modules/@dsh-std/presentation/README.md +27 -0
  297. package/node_modules/@dsh-std/presentation/README.zh.md +29 -0
  298. package/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  299. package/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  300. package/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  301. package/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
  302. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  303. package/node_modules/@dsh-std/presentation/lib/index.js +550 -0
  304. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  305. package/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  306. package/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  307. package/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  308. package/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  309. package/node_modules/@dsh-std/presentation/package.json +25 -0
  310. package/node_modules/@dsh-std/storage/CHANGELOG.md +8 -0
  311. package/node_modules/@dsh-std/storage/LICENSE +21 -0
  312. package/node_modules/@dsh-std/storage/README.md +5 -0
  313. package/node_modules/@dsh-std/storage/README.zh.md +7 -0
  314. package/node_modules/@dsh-std/storage/lib/index.d.ts +48 -0
  315. package/node_modules/@dsh-std/storage/lib/index.d.ts.map +1 -0
  316. package/node_modules/@dsh-std/storage/lib/index.js +104 -0
  317. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -0
  318. package/node_modules/@dsh-std/storage/package.json +20 -0
  319. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -0
  320. package/package.json +110 -70
  321. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  322. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -0
  323. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -0
  324. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -0
  325. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  326. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  327. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  328. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  329. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js +158 -0
  330. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  331. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  332. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  333. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  334. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  335. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  336. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  337. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -0
  338. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  339. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -0
  340. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -0
  341. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -0
  342. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  343. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  344. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js +424 -0
  345. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  346. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -0
  347. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/CHANGELOG.md +9 -0
  348. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -0
  349. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -0
  350. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  351. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  352. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  353. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  354. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  355. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -0
  356. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  357. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/CHANGELOG.md +9 -0
  358. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -0
  359. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +27 -0
  360. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +29 -0
  361. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.d.ts +2 -0
  362. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js +179 -0
  363. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/callback.js.map +1 -0
  364. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +262 -0
  365. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  366. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +550 -0
  367. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  368. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  369. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  370. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  371. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  372. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +25 -0
  373. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  374. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -0
  375. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -0
  376. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -0
  377. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  378. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  379. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js +424 -0
  380. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  381. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -0
  382. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  383. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -0
  384. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -0
  385. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -0
  386. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  387. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  388. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js +424 -0
  389. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  390. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -0
  391. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  392. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -0
  393. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -0
  394. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -0
  395. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  396. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  397. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js +424 -0
  398. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  399. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -0
  400. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/CHANGELOG.md +8 -0
  401. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -0
  402. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -0
  403. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -0
  404. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  405. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  406. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  407. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  408. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js +158 -0
  409. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  410. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  411. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  412. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +258 -0
  413. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  414. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  415. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  416. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -0
  417. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  418. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -0
  419. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -0
  420. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -0
  421. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  422. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  423. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js +424 -0
  424. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  425. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -0
  426. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/CHANGELOG.md +8 -0
  427. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -0
  428. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -0
  429. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -0
  430. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  431. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  432. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js +424 -0
  433. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  434. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -0
@@ -0,0 +1,397 @@
1
+ /**
2
+ * storage.local contract surface (C-040, `storage.dsh/v1alpha1#LocalStorage`):
3
+ * per-plugin private persistence, mounted by the dsh-tui-plugin-host row as
4
+ * `ctx.tuiPluginStorage`.
5
+ *
6
+ * The plugin-facing API is `open(ctx)` → `{ get, set, delete }`:
7
+ *
8
+ * - VERIFIED IDENTITY: the namespace is derived from the admitted Component
9
+ * identity bound to the PASSED activation — there is no parameter to name
10
+ * another plugin, so cross-plugin access is rejected by construction.
11
+ * - GRANTS AT CALL TIME: `get` requires `storage.local.read`, `set`/`delete`
12
+ * require `storage.local.write` — checked per call against the live grant
13
+ * store (a revoked grant blocks the very next operation without restart).
14
+ * - Backend: `~/.dsh-tui/plugin-storage/<namespace>.json`, one flat JSON
15
+ * object per namespace. Writes go through dsh-atomic-write (`withFileLock`
16
+ * for the read-modify-write cycle + `writeFileAtomic` for the commit), and
17
+ * an in-process per-namespace promise chain serializes operations in
18
+ * invocation order (contract concurrency rule — the lock alone does not
19
+ * order contenders).
20
+ * - QUOTA (host-defined): 256 keys AND 256 KiB of serialized content per
21
+ * namespace; a `set` that would cross either fails with QUOTA_EXCEEDED and
22
+ * changes nothing.
23
+ * - Keys stay ordinary data even when they collide with Object.prototype
24
+ * names ("__proto__", "toString", "constructor"): namespace tables are
25
+ * null-prototype and membership is decided with Object.hasOwn. Values
26
+ * must be EXACT JSON values — inputs JSON.stringify would silently
27
+ * mutate (undefined, NaN/±Infinity, functions, class instances, sparse
28
+ * arrays, cycles) are rejected instead of round-tripping a lie.
29
+ * - A corrupt namespace file is NEVER overwritten silently: every operation
30
+ * fails with STORAGE_UNAVAILABLE and the bytes stay on disk for manual
31
+ * recovery. Closing a handle (plugin unload) retains data (contract
32
+ * cleanup rule); later calls on a closed handle fail STORAGE_UNAVAILABLE.
33
+ * - privacyClass: sensitive — keys AND values are never logged.
34
+ */
35
+ import { mkdirSync, readFileSync } from 'node:fs';
36
+ import { join } from 'node:path';
37
+ import { writeFileAtomic, withFileLock } from '@deepseek-ai/dsh-atomic-write';
38
+ import { Service } from '@deepseek-ai/cordis';
39
+ import { validateDeleteInput, validateGetInput, validateSetInput, validateGetOutput, validateSetOutput, validateDeleteOutput, } from '@dsh-std/storage';
40
+ import { DATA_DIR } from '../utils/paths.js';
41
+ import { activationContext, assertCallerContext, bindCallerEffect, compositionRoot, concreteService } from './host-access.js';
42
+ import { declaresPermission, requireComponentIdentity, requiresContract } from './component-identity.js';
43
+ import { readGrantStore } from './grants.js';
44
+ /** Quota: max distinct keys per namespace. */
45
+ export const STORAGE_MAX_KEYS = 256;
46
+ /** Quota: max serialized file size per namespace (bytes). */
47
+ export const STORAGE_MAX_BYTES = 256 * 1024;
48
+ /** Max key length (characters). */
49
+ export const STORAGE_KEY_MAX_LENGTH = 128;
50
+ /** Storage failure with the contract's error code on `.code`. */
51
+ export class PluginStorageError extends Error {
52
+ code;
53
+ constructor(code, message) {
54
+ super(message);
55
+ this.name = 'PluginStorageError';
56
+ this.code = code;
57
+ }
58
+ }
59
+ const storageStates = new WeakMap();
60
+ function storageStateFor(runtime) {
61
+ const state = storageStates.get(concreteService(runtime));
62
+ if (state === undefined)
63
+ throw new Error('tuiPluginStorage host state is unavailable');
64
+ return state;
65
+ }
66
+ /** Namespace directory under the data dir. */
67
+ export const PLUGIN_STORAGE_DIR = 'plugin-storage';
68
+ /**
69
+ * Plugin name → safe, collision-free file name. encodeURIComponent keeps
70
+ * alphanumerics and `- _ . ! ~ * ' ( )`, so scoped names like `@foo/bar`
71
+ * encode reversibly; pathological results ('', '.', '..') map to '_'.
72
+ */
73
+ export function storageFileName(plugin) {
74
+ const encoded = encodeURIComponent(plugin);
75
+ return encoded === '' || encoded === '.' || encoded === '..' ? '_' : encoded;
76
+ }
77
+ function assertKey(key) {
78
+ if (typeof key !== 'string' || key === '' || key.length > STORAGE_KEY_MAX_LENGTH || /[\x00-\x1f\x7f-\x9f]/.test(key)) {
79
+ throw new PluginStorageError('INVALID_KEY', 'storage keys are non-empty control-free strings of at most 128 characters');
80
+ }
81
+ }
82
+ function validateStorageInput(operation, input) {
83
+ try {
84
+ if (operation === 'get')
85
+ validateGetInput(input);
86
+ else if (operation === 'set')
87
+ validateSetInput(input);
88
+ else
89
+ validateDeleteInput(input);
90
+ }
91
+ catch (error) {
92
+ const message = error instanceof Error ? error.message : String(error);
93
+ if (operation !== 'set')
94
+ throw new PluginStorageError('INVALID_KEY', message);
95
+ // The standard validator intentionally reports exact-record failures in a
96
+ // single stream. Preserve the contract's distinction: malformed/missing
97
+ // `key` is INVALID_KEY; once the key itself is valid, every value/shape
98
+ // failure (including a missing `value`) is INVALID_VALUE.
99
+ let keyValid = false;
100
+ try {
101
+ const candidate = input;
102
+ assertKey(candidate?.key);
103
+ keyValid = true;
104
+ }
105
+ catch {
106
+ keyValid = false;
107
+ }
108
+ throw new PluginStorageError(keyValid ? 'INVALID_VALUE' : 'INVALID_KEY', message);
109
+ }
110
+ }
111
+ /**
112
+ * Exact JSON values only. JSON.stringify silently MUTATES edge inputs
113
+ * (NaN/±Infinity → null, undefined array items → null, undefined object
114
+ * properties dropped, class instances reduced to own fields) — a storage
115
+ * contract must not round-trip a value the caller never stored, so those
116
+ * inputs are rejected instead. Accepts: null, boolean, string, finite
117
+ * number, arrays (dense), and plain objects (Object.prototype or null
118
+ * prototype only). Every own property is inspected because JSON.stringify
119
+ * ignores array side-properties, symbols, and non-enumerable fields, while
120
+ * accessors/toJSON can replace the value being serialized. Those inputs,
121
+ * cycles, and anything whose inspection throws are rejected.
122
+ */
123
+ function assertJsonValue(value) {
124
+ let ok = false;
125
+ try {
126
+ ok = isJsonValue(value, new Set());
127
+ }
128
+ catch {
129
+ ok = false;
130
+ }
131
+ if (!ok) {
132
+ throw new PluginStorageError('INVALID_VALUE', 'storage values must be exact JSON values (no undefined, functions, symbols, BigInt, NaN/Infinity, class instances, sparse arrays, or cycles)');
133
+ }
134
+ }
135
+ function isJsonValue(value, seen) {
136
+ if (value === null)
137
+ return true;
138
+ switch (typeof value) {
139
+ case 'boolean':
140
+ case 'string':
141
+ return true;
142
+ case 'number':
143
+ return Number.isFinite(value);
144
+ case 'object': {
145
+ const object = value;
146
+ if (seen.has(object))
147
+ return false;
148
+ seen.add(object);
149
+ try {
150
+ if (Array.isArray(value)) {
151
+ // Array subclasses/custom prototypes can supply an inherited toJSON
152
+ // hook. JSON would serialize its replacement, not this array.
153
+ if (Object.getPrototypeOf(value) !== Array.prototype)
154
+ return false;
155
+ if (Object.getOwnPropertySymbols(value).length > 0)
156
+ return false;
157
+ const ownNames = Object.getOwnPropertyNames(value);
158
+ for (const name of ownNames) {
159
+ if (name === 'length')
160
+ continue;
161
+ const descriptor = Object.getOwnPropertyDescriptor(value, name);
162
+ // JSON arrays serialize only their indexed elements. Any other
163
+ // own property (especially toJSON) would be silently lost.
164
+ const index = Number(name);
165
+ if (descriptor === undefined || !('value' in descriptor) ||
166
+ !Number.isSafeInteger(index) || index < 0 || String(index) !== name || index >= value.length)
167
+ return false;
168
+ }
169
+ for (let index = 0; index < value.length; index++) {
170
+ // A sparse hole would serialize as null — a value never stored.
171
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
172
+ if (descriptor === undefined || !('value' in descriptor))
173
+ return false;
174
+ if (!isJsonValue(descriptor.value, seen))
175
+ return false;
176
+ }
177
+ return true;
178
+ }
179
+ const proto = Object.getPrototypeOf(value);
180
+ if (proto !== Object.prototype && proto !== null)
181
+ return false;
182
+ // A modified Object.prototype can add an inherited toJSON hook; own
183
+ // non-enumerable/accessor properties are likewise not exact JSON.
184
+ if (typeof value.toJSON === 'function')
185
+ return false;
186
+ if (Object.getOwnPropertySymbols(value).length > 0)
187
+ return false;
188
+ for (const name of Object.getOwnPropertyNames(value)) {
189
+ const descriptor = Object.getOwnPropertyDescriptor(value, name);
190
+ if (descriptor === undefined || !descriptor.enumerable || !('value' in descriptor))
191
+ return false;
192
+ if (!isJsonValue(descriptor.value, seen))
193
+ return false;
194
+ }
195
+ return true;
196
+ }
197
+ finally {
198
+ // DAGs (shared references without cycles) serialize fine — only
199
+ // reject a value reachable from ITSELF.
200
+ seen.delete(object);
201
+ }
202
+ }
203
+ default:
204
+ // undefined / function / symbol / bigint
205
+ return false;
206
+ }
207
+ }
208
+ /**
209
+ * `ctx.tuiPluginStorage` — storage.local contract surface. Mounted by the
210
+ * dsh-tui-plugin-host row; grants come from that row's store when present
211
+ * (normal path) or a private read otherwise (bare mounts in tests).
212
+ */
213
+ export class TuiPluginStorageRuntime extends Service {
214
+ constructor(ctx, options = {}) {
215
+ super(ctx, 'tuiPluginStorage');
216
+ storageStates.set(this, {
217
+ hostContext: compositionRoot(ctx),
218
+ grantsOption: options.grants,
219
+ fallbackGrants: readGrantStore(),
220
+ ledgerOption: options.ledger,
221
+ namespaces: new Map(),
222
+ dir: options.dir ?? join(DATA_DIR, PLUGIN_STORAGE_DIR),
223
+ });
224
+ }
225
+ /** Grants: the plugin-host row's store when mounted, else a private read.
226
+ * Resolved PER CALL — sibling services mounted later by the same apply()
227
+ * are not visible to constructors (cordis), so a constructor-time probe
228
+ * would silently stick to the fallback. */
229
+ grants() {
230
+ const state = storageStateFor(this);
231
+ return state.grantsOption ?? state.hostContext.get('tuiPluginHost')?.grants ?? state.fallbackGrants;
232
+ }
233
+ /** Optional observability; a bare mount (tests) simply records nothing. */
234
+ ledger() {
235
+ const state = storageStateFor(this);
236
+ return state.ledgerOption ?? state.hostContext.get('tuiEffectLedger');
237
+ }
238
+ /**
239
+ * Open the caller's private namespace. Identity = the PASSED context's
240
+ * verified Component identity (no way to name another plugin). The handle
241
+ * closes automatically when the caller's context unloads (idempotent
242
+ * disposer); data is retained (contract cleanup rule).
243
+ */
244
+ open(pluginCtx) {
245
+ const caller = activationContext(pluginCtx);
246
+ if (caller === undefined)
247
+ throw new PluginStorageError('STORAGE_UNAVAILABLE', 'storage.local.open requires a live activation context');
248
+ assertCallerContext(this.ctx, caller, 'storage.local.open', this);
249
+ const state = storageStateFor(this);
250
+ const identity = requireComponentIdentity(caller);
251
+ if (!requiresContract(identity, 'storage.dsh/v1alpha1', 'LocalStorage')) {
252
+ throw new PluginStorageError('PERMISSION_NOT_GRANTED', 'storage.local requires the storage.dsh/v1alpha1#LocalStorage contract in the admitted manifest');
253
+ }
254
+ const plugin = identity.componentId;
255
+ let namespace = state.namespaces.get(plugin);
256
+ if (namespace === undefined) {
257
+ namespace = { chain: Promise.resolve() };
258
+ state.namespaces.set(plugin, namespace);
259
+ }
260
+ this.ledger()?.record({ operation: 'create', resource: { kind: 'storage-namespace', id: plugin }, result: 'applied' }, caller);
261
+ // `closed` is PER HANDLE, not per namespace: unloading one fiber must not
262
+ // kill another handle opened on the same namespace. Close on unload;
263
+ // idempotent (a double-close stays harmless by design).
264
+ let closed = false;
265
+ const close = () => {
266
+ if (closed)
267
+ return;
268
+ closed = true;
269
+ this.ledger()?.record({ operation: 'release', resource: { kind: 'storage-namespace', id: plugin }, result: 'applied' }, caller);
270
+ };
271
+ bindCallerEffect(caller, close);
272
+ const file = join(state.dir, `${storageFileName(plugin)}.json`);
273
+ const grants = () => this.grants();
274
+ const ledger = () => this.ledger();
275
+ const enqueue = (operation) => {
276
+ if (closed) {
277
+ return Promise.reject(new PluginStorageError('STORAGE_UNAVAILABLE', `storage namespace "${plugin}" handle is closed`));
278
+ }
279
+ const run = namespace.chain.then(operation);
280
+ // Keep the chain alive after a failure without unhandled rejections.
281
+ namespace.chain = run.catch(() => { });
282
+ return run;
283
+ };
284
+ const readTable = () => {
285
+ let raw;
286
+ try {
287
+ raw = readFileSync(file, 'utf8');
288
+ }
289
+ catch (error) {
290
+ if (error.code === 'ENOENT')
291
+ return Object.create(null);
292
+ throw new PluginStorageError('STORAGE_UNAVAILABLE', `storage namespace "${plugin}" is unreadable; the file was left untouched for manual recovery`);
293
+ }
294
+ let parsed;
295
+ try {
296
+ parsed = JSON.parse(raw);
297
+ }
298
+ catch {
299
+ throw new PluginStorageError('STORAGE_UNAVAILABLE', `storage namespace "${plugin}" is corrupt on disk; the file was left untouched for manual recovery`);
300
+ }
301
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
302
+ throw new PluginStorageError('STORAGE_UNAVAILABLE', `storage namespace "${plugin}" holds a non-object document; the file was left untouched for manual recovery`);
303
+ }
304
+ // Rebuild on a NULL-PROTOTYPE table: key membership is decided with
305
+ // Object.hasOwn, and keys like "__proto__" / "toString" must behave
306
+ // as ordinary data (a prototype-chain read would leak host objects
307
+ // into plugin results and fake membership for never-stored keys).
308
+ const table = Object.create(null);
309
+ for (const [key, value] of Object.entries(parsed))
310
+ table[key] = value;
311
+ return table;
312
+ };
313
+ const requireGrant = (permission) => {
314
+ if (!declaresPermission(identity, permission, plugin)
315
+ || !grants().allows({ componentId: identity.componentId, activationId: identity.activationId }, permission, plugin)) {
316
+ ledger()?.record({
317
+ operation: 'bind',
318
+ resource: { kind: 'permission', id: permission },
319
+ result: 'failed',
320
+ errorCode: 'PERMISSION_NOT_GRANTED',
321
+ }, caller);
322
+ throw new PluginStorageError('PERMISSION_NOT_GRANTED', `storage.${permission.endsWith('.read') ? 'get' : 'set/delete'} from plugin "${plugin}" denied — grant "${permission}" ` +
323
+ `for "${plugin}" in ~/.dsh-tui/extension-grants.json first`);
324
+ }
325
+ };
326
+ // withFileLock creates its lock as a SIBLING of the file and does not
327
+ // create parent directories — ensure the tree exists before any write
328
+ // path (read paths tolerate ENOENT as an empty namespace and never
329
+ // materialize the tree).
330
+ const ensureDir = () => {
331
+ mkdirSync(state.dir, { recursive: true, mode: 0o700 });
332
+ };
333
+ return {
334
+ get: (input) => enqueue(async () => {
335
+ validateStorageInput('get', input);
336
+ const key = input.key;
337
+ assertKey(key);
338
+ requireGrant('storage.local.read');
339
+ const table = readTable();
340
+ const output = { value: Object.hasOwn(table, key) ? table[key] : null };
341
+ try {
342
+ validateGetOutput(output);
343
+ }
344
+ catch {
345
+ throw new PluginStorageError('STORAGE_UNAVAILABLE', `storage namespace "${plugin}" contains an invalid JSON value`);
346
+ }
347
+ return output;
348
+ }),
349
+ set: (input) => enqueue(async () => {
350
+ validateStorageInput('set', input);
351
+ const key = input.key;
352
+ const value = input.value;
353
+ assertKey(key);
354
+ assertJsonValue(value);
355
+ requireGrant('storage.local.write');
356
+ ensureDir();
357
+ return withFileLock(file, async () => {
358
+ const table = readTable();
359
+ const isNewKey = !Object.hasOwn(table, key);
360
+ if (isNewKey && Object.keys(table).length >= STORAGE_MAX_KEYS) {
361
+ throw new PluginStorageError('QUOTA_EXCEEDED', `storage namespace "${plugin}" holds ${STORAGE_MAX_KEYS} keys already`);
362
+ }
363
+ table[key] = value;
364
+ const content = JSON.stringify(table);
365
+ if (Buffer.byteLength(content, 'utf8') > STORAGE_MAX_BYTES) {
366
+ throw new PluginStorageError('QUOTA_EXCEEDED', `storage namespace "${plugin}" would exceed ${STORAGE_MAX_BYTES} bytes`);
367
+ }
368
+ // 0o600/0o700: the namespace is privacyClass sensitive.
369
+ await writeFileAtomic(file, content, { mode: 0o600, dirMode: 0o700 });
370
+ const output = { stored: true };
371
+ validateSetOutput(output);
372
+ return output;
373
+ });
374
+ }),
375
+ delete: (input) => enqueue(async () => {
376
+ validateStorageInput('delete', input);
377
+ const key = input.key;
378
+ assertKey(key);
379
+ requireGrant('storage.local.write');
380
+ ensureDir();
381
+ return withFileLock(file, async () => {
382
+ const table = readTable();
383
+ if (!Object.hasOwn(table, key)) {
384
+ const output = { deleted: false };
385
+ validateDeleteOutput(output);
386
+ return output;
387
+ }
388
+ delete table[key];
389
+ await writeFileAtomic(file, JSON.stringify(table), { mode: 0o600, dirMode: 0o700 });
390
+ const output = { deleted: true };
391
+ validateDeleteOutput(output);
392
+ return output;
393
+ });
394
+ }),
395
+ };
396
+ }
397
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AA8BnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAkgBvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/plugin.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,KAAK,EAAe,MAAM,wBAAwB,CAAA;AAGhE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAA;AAmCnC;;;;;;;;GAQG;AACH,wBAAsB,KAAK,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAmmBvE;AA6GD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE;IAAE,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;CAAE,GAAG;IACjF,UAAU,EAAE,CAAC,KAAK,CAAC,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,YAAY,EAAE,MAAM,IAAI,CAAA;CACzB,CAcA;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE;IACpC,YAAY,EAAE,MAAM,CAAA;IACpB,SAAS,EAAE,KAAK,GAAG,SAAS,CAAA;IAC5B,YAAY,EAAE,KAAK,CAAA;CACpB,GAAG,OAAO,CAQV"}
@@ -21,12 +21,17 @@ import { ensurePackagedPresets } from './packaged-presets.js';
21
21
  import { ensureLegacySessionEventTypes } from './compat/index.js';
22
22
  import { clearResumeTarget, writeResumeTarget } from '../sessionHistory.js';
23
23
  import { resolveSessionCwd } from '../utils/workspaceRoot.js';
24
- import { checkForTuiUpdate, installedTuiVersion, isVersionNewer, resolveDshProfileName, resolveTuiUpdateTarget, updateTuiAndRestart } from '../update.js';
25
- import { isLang, resolveStartupLang, setLang, t } from '../i18n.js';
24
+ import { checkForTuiUpdate, installedTuiVersion, isBootDeadlockTarget, isVersionNewer, resolveDshProfileName, resolveTuiUpdateTarget, updateTuiAndRestart } from '../update.js';
25
+ import { getLang, isLang, resolveStartupLang, setLang, t, writeLangPref } from '../i18n.js';
26
26
  import { detectLegacyEnv, migrateLegacyDataDir, RENAMED_ENV } from '../utils/paths.js';
27
27
  import { Chat } from '../screens/Chat.js';
28
+ import { getHostDialogStore } from './dialogs.js';
29
+ import { getHostStatusStore } from './status.js';
30
+ import { getHostShortcuts } from './shortcuts.js';
28
31
  import { attachSessionToWorkspace } from './workspace.js';
29
- import { createLocalWorkspaceRuntime } from './workspaces.js';
32
+ import { createLocalWorkspaceRuntime, getHostWorkspaceRuntime } from './workspaces.js';
33
+ import { getHostSettingsSections } from './settings-sections.js';
34
+ import { withHostRootCapability } from './host-access.js';
30
35
  import { render, ThemeProvider, AlternateScreen } from '../ui.js';
31
36
  import instances from '../ink/instances.js';
32
37
  import { cursorMove, DISABLE_KITTY_KEYBOARD, DISABLE_MODIFY_OTHER_KEYS, DISABLE_WIN32_INPUT_MODE } from '../ink/termio/csi.js';
@@ -66,10 +71,11 @@ export async function apply(ctx, config) {
66
71
  // already touches lang.json). Copy, not move — old launchers keep working
67
72
  // and the user deletes the legacy directory themselves.
68
73
  const migrated = migrateLegacyDataDir();
69
- // UI language resolution: DSH_TUI_LANG env var wins, then cordis.yml
70
- // `lang`, then the persisted `/lang` choice, then `zh`. Must settle
71
- // before the first render so every module resolves strings in the same
72
- // language.
74
+ // UI language resolution: DSH_TUI_LANG env var wins, then the
75
+ // settings.yaml `dsh-tui.lang` user layer (applied once the settings
76
+ // namespace registers below), then cordis.yml `lang`, then the
77
+ // persisted `/lang` choice, then `zh`. Must settle before the first
78
+ // render so every module resolves strings in the same language.
73
79
  const envLang = process.env.DSH_TUI_LANG;
74
80
  setLang(isLang(envLang) ? envLang : isLang(config.lang) ? config.lang : resolveStartupLang());
75
81
  // Rename notices must land before the first render — stderr writes break
@@ -211,7 +217,7 @@ export async function apply(ctx, config) {
211
217
  // without the service means the patch came from an older dsh-tui copy
212
218
  // than the running code — warn once so the skew is diagnosable. Bare
213
219
  // embedders (no --profile) take the same fallback by design, silently.
214
- const mountedWorkspaceService = ctx.get('tuiWorkspaces');
220
+ const mountedWorkspaceService = getHostWorkspaceRuntime(ctx.get('tuiWorkspaces'));
215
221
  if (mountedWorkspaceService === undefined && resolveDshProfileName() !== undefined) {
216
222
  ctx.logger.warn('dsh-tui: tuiWorkspaces service is not mounted; /workspace runs with the local-only fallback. ' +
217
223
  'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
@@ -225,6 +231,24 @@ export async function apply(ctx, config) {
225
231
  ctx.logger.warn('dsh-tui: tuiScenes service is not mounted; plugin scenes will never open. ' +
226
232
  'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
227
233
  }
234
+ // Same skew guard for the plugin-UI services (dsh-tui-extensions row):
235
+ // managed dialogs park unanswered, status contributions never render,
236
+ // shortcuts never match, and custom-entry renderers stay invisible when
237
+ // the row is absent — say why on profile launches.
238
+ if (ctx.get('tuiDialogs') === undefined && resolveDshProfileName() !== undefined) {
239
+ ctx.logger.warn('dsh-tui: tuiDialogs/tuiStatus/tuiShortcuts/tuiRenderers services are not mounted; plugin dialogs, status contributions, shortcuts and custom-entry renderers are off. ' +
240
+ 'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
241
+ }
242
+ // Same skew guard for the plugin-host row (dsh-tui-plugin-host): without
243
+ // it there is no runtime generation id, no unified grant store service,
244
+ // and no Host Descriptor — plugin interop surfaces degrade silently
245
+ // otherwise. The D-7 decision gate does NOT depend on this row (the
246
+ // channel installs its own), so interception gating stays intact either
247
+ // way — what breaks is everything that rides on tuiPluginHost.
248
+ if (ctx.get('tuiPluginHost') === undefined && resolveDshProfileName() !== undefined) {
249
+ ctx.logger.warn('dsh-tui: tuiPluginHost service is not mounted; plugin grant store, runtime generation and Host Descriptor are unavailable. ' +
250
+ 'The bundle patch is older than the installed dsh-tui package — update the globally installed dsh-tui launcher to match the profile (issue #183).');
251
+ }
228
252
  const initialWorkspace = requestedWorkspace === undefined
229
253
  ? undefined
230
254
  : await workspaceService.resolve(requestedWorkspace);
@@ -284,6 +308,7 @@ export async function apply(ctx, config) {
284
308
  // Edit/Write diff presentation (schema default 'auto'); the /settings
285
309
  // screen edits this key live through the dsh-tui namespace.
286
310
  diffLayout: config.diffLayout,
311
+ thinkingFold: config.thinkingFold,
287
312
  handle,
288
313
  });
289
314
  // Register the dsh-tui settings namespace so the /settings screen can
@@ -293,24 +318,69 @@ export async function apply(ctx, config) {
293
318
  ctx.inject(['settings'], (settingsCtx) => {
294
319
  const scope = settingsCtx.settings.register(settingsNamespace('dsh-tui'), Schema.object({
295
320
  diffLayout: Schema.union(['auto', 'split', 'unified']).default('auto'),
321
+ thinkingFold: Schema.union(['preview', 'full']).default('preview'),
322
+ // No default on purpose: an unset `lang` keeps the field showing
323
+ // the effective language (see the section's format below) and lets
324
+ // cordis.yml / lang.json keep their precedence.
325
+ lang: Schema.union(['zh', 'en']),
296
326
  }));
297
327
  const applyLayout = (value) => {
298
328
  channel.setDiffLayout(value.diffLayout ?? config.diffLayout ?? 'auto');
299
329
  };
300
- applyLayout(scope.get());
301
- scope.watch(next => {
330
+ // The /settings language field writes `lang` through the settings
331
+ // service (user layer): apply it live and mirror it to lang.json so
332
+ // the /lang command and next-boot resolution agree. DSH_TUI_LANG
333
+ // stays the top precedence — a pinned env is never overridden by the
334
+ // document.
335
+ const applyLang = (value) => {
336
+ if (!isLang(process.env.DSH_TUI_LANG) && isLang(value.lang)) {
337
+ setLang(value.lang);
338
+ writeLangPref(value.lang);
339
+ }
340
+ };
341
+ // thinkingFold rides the same namespace: /settings writes it live and
342
+ // the channel picks it up at the next step seal.
343
+ const applyThinkingFold = (value) => {
344
+ channel.setThinkingFold(value.thinkingFold ?? config.thinkingFold ?? 'preview');
345
+ };
346
+ const apply = (next) => {
302
347
  applyLayout(next);
348
+ applyLang(next);
349
+ applyThinkingFold(next);
350
+ };
351
+ apply(scope.get());
352
+ scope.watch(next => {
353
+ apply(next);
303
354
  });
304
355
  });
305
356
  // The /settings screen's own section: the dsh-tui namespace comes from
306
- // this plugin's Config schema, and the declared select writes diffLayout
307
- // back through the settings service's revision-fenced mutate.
308
- const settingsSections = ctx.get('tuiSettingsSections');
357
+ // the settings registration above, and the declared selects write `lang`
358
+ // and `diffLayout` back through the settings service's revision-fenced
359
+ // mutate (the watch applies both live).
360
+ const settingsSections = getHostSettingsSections(ctx.get('tuiSettingsSections'));
309
361
  if (settingsSections !== undefined) {
310
362
  const unregister = settingsSections.register({
311
363
  ns: 'dsh-tui',
312
364
  title: 'dsh-tui',
313
365
  fields: [
366
+ {
367
+ path: ['lang'],
368
+ label: 'Language',
369
+ descriptions: { zh: '界面语言' },
370
+ hint: 'UI language for the whole interface — applies immediately and is saved.',
371
+ hintDescriptions: { zh: '整个界面的显示语言——立即生效并保存。' },
372
+ kind: 'select',
373
+ options: [
374
+ { value: 'zh', label: '中文', descriptions: { zh: '中文' } },
375
+ { value: 'en', label: 'English', descriptions: { zh: '英文' } },
376
+ ],
377
+ format(value) {
378
+ // Unset in settings.yaml: show the effective UI language
379
+ // (env / cordis.yml / lang.json resolution) instead of a
380
+ // blank "unset" that hides the current choice.
381
+ return value === undefined || value === null ? getLang() : String(value);
382
+ },
383
+ },
314
384
  {
315
385
  path: ['diffLayout'],
316
386
  label: 'Diff layout',
@@ -324,6 +394,18 @@ export async function apply(ctx, config) {
324
394
  { value: 'unified', label: 'Unified', descriptions: { zh: '统一式' } },
325
395
  ],
326
396
  },
397
+ {
398
+ path: ['thinkingFold'],
399
+ label: 'Thinking display',
400
+ descriptions: { zh: '思考块展示' },
401
+ hint: 'Streaming thinking shows a 2-3 line live preview and each step folds when it settles; Full keeps thinking expanded until the turn ends.',
402
+ hintDescriptions: { zh: '流式时思考显示 2-3 行动态预览,每步落定后折叠;展开模式保持思考展开直到整轮结束。' },
403
+ kind: 'select',
404
+ options: [
405
+ { value: 'preview', label: 'Preview (2-3 lines)', descriptions: { zh: '预览(2-3 行)' } },
406
+ { value: 'full', label: 'Full until turn end', descriptions: { zh: '展开至轮末' } },
407
+ ],
408
+ },
327
409
  ],
328
410
  });
329
411
  ctx.effect(() => unregister);
@@ -428,6 +510,12 @@ export async function apply(ctx, config) {
428
510
  channel,
429
511
  questionStore,
430
512
  approvalStore,
513
+ // The dsh-tui-extensions row's services (managed dialogs, status line,
514
+ // shortcuts). Soft-consumed: absent the row (stale patch, bare embed),
515
+ // Chat falls back to inert stores and no shortcut registry.
516
+ extensionDialogs: getHostDialogStore(ctx.get('tuiDialogs')),
517
+ extensionStatus: getHostStatusStore(ctx.get('tuiStatus')),
518
+ extensionShortcuts: getHostShortcuts(ctx.get('tuiShortcuts')),
431
519
  // Full-screen surfaces inside Chat — the trajectory scene and the session
432
520
  // browser — enter the alt screen themselves in inline mode; in fullscreen
433
521
  // the tree is already wrapped below, so they must not nest.
@@ -453,6 +541,21 @@ export async function apply(ctx, config) {
453
541
  channel.notify(t('update-check-failed'));
454
542
  }
455
543
  else {
544
+ // 0.7.0/0.7.1 hard-inject tuiWorkspaces at the code level; under
545
+ // an older global launcher patch (no service row) that is a
546
+ // permanent boot deadlock (issues #183/#307, the exact report
547
+ // "pending (waiting for service: tuiWorkspaces)"). A stale mirror
548
+ // pinning /update onto that range must be refused, not installed.
549
+ if (isBootDeadlockTarget(target.latest)) {
550
+ channel.notify(t('update-refused-deadlock', {
551
+ latest: target.latest,
552
+ authoritative: target.authoritative ?? target.latest,
553
+ }), { color: 'warning' });
554
+ return;
555
+ }
556
+ if (target.authoritative !== undefined) {
557
+ channel.notify(t('update-mirror-lag', { latest: target.latest, authoritative: target.authoritative }));
558
+ }
456
559
  updateTargetVersion = target.latest;
457
560
  }
458
561
  channel.notify(t('update-starting'));
@@ -644,6 +747,11 @@ async function finishExit(ctx, instance, fullscreen, notice, stderrNotice, done)
644
747
  const cursor = fullscreen ? '' : cursorMoveToFrameEnd(runtime);
645
748
  try {
646
749
  runtime?.detachForShutdown?.();
750
+ // The /update continuation spawns children that inherit this stdin;
751
+ // strip the readable pump so the parent cannot swallow their input
752
+ // (issues #284/#307). Harmless on plain exits — the process exits
753
+ // right after this cleanup anyway.
754
+ runtime?.detachStdinForHandoff?.();
647
755
  }
648
756
  catch {
649
757
  ctx.logger.debug('dsh-tui: Ink shutdown detach failed; continuing with generic terminal cleanup');
@@ -678,6 +786,8 @@ function readInkShutdownState(value) {
678
786
  const candidate = value;
679
787
  if (candidate.detachForShutdown !== undefined && typeof candidate.detachForShutdown !== 'function')
680
788
  return undefined;
789
+ if (candidate.detachStdinForHandoff !== undefined && typeof candidate.detachStdinForHandoff !== 'function')
790
+ return undefined;
681
791
  if (candidate.frontFrame !== undefined && !isFrameState(candidate.frontFrame))
682
792
  return undefined;
683
793
  if (candidate.displayCursor !== undefined && candidate.displayCursor !== null && !isCursorState(candidate.displayCursor))
@@ -778,7 +888,7 @@ function resumeCommand(profile, sessionId) {
778
888
  function disposeRootAndThen(ctx, done, fallbackCode = 1) {
779
889
  const timer = setTimeout(() => process.exit(fallbackCode), 5000);
780
890
  timer.unref();
781
- void ctx.root.fiber.dispose().then(() => {
891
+ void withHostRootCapability(() => ctx.root.fiber.dispose()).then(() => {
782
892
  clearTimeout(timer);
783
893
  done();
784
894
  }, () => {