@deepseek-harness-tui/dsh-tui 0.8.0 → 0.8.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 (330) hide show
  1. package/README.md +13 -5
  2. package/cordis.patch.yml +21 -2
  3. package/cordis.yml +12 -1
  4. package/ecosystem-spec/registry/contracts/decision-events-v1alpha1.json +13 -0
  5. package/ecosystem-spec/registry/host-descriptor.tui.example.json +56 -0
  6. package/ecosystem-spec/registry/permissions-0.1.json +13 -0
  7. package/ecosystem-spec/registry/registry-0.15.json +57 -0
  8. package/ecosystem-spec/schemas/conformance-claim.schema.json +15 -0
  9. package/ecosystem-spec/schemas/effect-ledger-record.schema.json +16 -0
  10. package/ecosystem-spec/schemas/host-descriptor.schema.json +26 -0
  11. package/lib/types/cc/figures.d.ts +4 -0
  12. package/lib/types/cc/figures.d.ts.map +1 -1
  13. package/lib/types/cc/figures.js +4 -0
  14. package/lib/types/commands.d.ts.map +1 -1
  15. package/lib/types/commands.js +2 -0
  16. package/lib/types/components/ExtensionDialog.d.ts +40 -0
  17. package/lib/types/components/ExtensionDialog.d.ts.map +1 -0
  18. package/lib/types/components/ExtensionDialog.js +210 -0
  19. package/lib/types/components/HelpMenu.js +1 -1
  20. package/lib/types/components/LoadedContextPanel.d.ts +5 -10
  21. package/lib/types/components/LoadedContextPanel.d.ts.map +1 -1
  22. package/lib/types/components/LoadedContextPanel.js +7 -18
  23. package/lib/types/components/LogoV2.d.ts.map +1 -1
  24. package/lib/types/components/LogoV2.js +1 -2
  25. package/lib/types/components/MessageList.d.ts.map +1 -1
  26. package/lib/types/components/MessageList.js +25 -1
  27. package/lib/types/components/RewindPicker.d.ts +13 -1
  28. package/lib/types/components/RewindPicker.d.ts.map +1 -1
  29. package/lib/types/components/RewindPicker.js +26 -2
  30. package/lib/types/components/ToolUseLoader.d.ts +8 -3
  31. package/lib/types/components/ToolUseLoader.d.ts.map +1 -1
  32. package/lib/types/components/ToolUseLoader.js +43 -9
  33. package/lib/types/components/messages/AssistantToolUseMessage.d.ts.map +1 -1
  34. package/lib/types/components/messages/AssistantToolUseMessage.js +1 -1
  35. package/lib/types/dsh-adapter/channel.d.ts +30 -7
  36. package/lib/types/dsh-adapter/channel.d.ts.map +1 -1
  37. package/lib/types/dsh-adapter/channel.js +503 -21
  38. package/lib/types/dsh-adapter/command-attribution.d.ts +51 -0
  39. package/lib/types/dsh-adapter/command-attribution.d.ts.map +1 -0
  40. package/lib/types/dsh-adapter/command-attribution.js +93 -0
  41. package/lib/types/dsh-adapter/command-errors.d.ts +42 -0
  42. package/lib/types/dsh-adapter/command-errors.d.ts.map +1 -0
  43. package/lib/types/dsh-adapter/command-errors.js +63 -0
  44. package/lib/types/dsh-adapter/component-identity.d.ts +28 -0
  45. package/lib/types/dsh-adapter/component-identity.d.ts.map +1 -0
  46. package/lib/types/dsh-adapter/component-identity.js +108 -0
  47. package/lib/types/dsh-adapter/contract.d.ts +1 -1
  48. package/lib/types/dsh-adapter/contract.js +2 -2
  49. package/lib/types/dsh-adapter/decision-guard.d.ts +84 -0
  50. package/lib/types/dsh-adapter/decision-guard.d.ts.map +1 -0
  51. package/lib/types/dsh-adapter/decision-guard.js +354 -0
  52. package/lib/types/dsh-adapter/dialogs.d.ts +146 -0
  53. package/lib/types/dsh-adapter/dialogs.d.ts.map +1 -0
  54. package/lib/types/dsh-adapter/dialogs.js +262 -0
  55. package/lib/types/dsh-adapter/effect-ledger.d.ts +99 -0
  56. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -0
  57. package/lib/types/dsh-adapter/effect-ledger.js +231 -0
  58. package/lib/types/dsh-adapter/extension-events.d.ts +193 -0
  59. package/lib/types/dsh-adapter/extension-events.d.ts.map +1 -0
  60. package/lib/types/dsh-adapter/extension-events.js +169 -0
  61. package/lib/types/dsh-adapter/extensions.d.ts +32 -0
  62. package/lib/types/dsh-adapter/extensions.d.ts.map +1 -0
  63. package/lib/types/dsh-adapter/extensions.js +42 -0
  64. package/lib/types/dsh-adapter/grants.d.ts +25 -0
  65. package/lib/types/dsh-adapter/grants.d.ts.map +1 -0
  66. package/lib/types/dsh-adapter/grants.js +184 -0
  67. package/lib/types/dsh-adapter/host-descriptor.d.ts +24 -0
  68. package/lib/types/dsh-adapter/host-descriptor.d.ts.map +1 -0
  69. package/lib/types/dsh-adapter/host-descriptor.js +146 -0
  70. package/lib/types/dsh-adapter/message-observer.d.ts +178 -0
  71. package/lib/types/dsh-adapter/message-observer.d.ts.map +1 -0
  72. package/lib/types/dsh-adapter/message-observer.js +509 -0
  73. package/lib/types/dsh-adapter/plugin-host.d.ts +103 -0
  74. package/lib/types/dsh-adapter/plugin-host.d.ts.map +1 -0
  75. package/lib/types/dsh-adapter/plugin-host.js +270 -0
  76. package/lib/types/dsh-adapter/plugin-storage.d.ts +113 -0
  77. package/lib/types/dsh-adapter/plugin-storage.d.ts.map +1 -0
  78. package/lib/types/dsh-adapter/plugin-storage.js +385 -0
  79. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  80. package/lib/types/dsh-adapter/plugin.js +47 -1
  81. package/lib/types/dsh-adapter/plugins-info.d.ts +43 -0
  82. package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -0
  83. package/lib/types/dsh-adapter/plugins-info.js +276 -0
  84. package/lib/types/dsh-adapter/renderers.d.ts +65 -0
  85. package/lib/types/dsh-adapter/renderers.d.ts.map +1 -0
  86. package/lib/types/dsh-adapter/renderers.js +138 -0
  87. package/lib/types/dsh-adapter/sanitize.d.ts +36 -0
  88. package/lib/types/dsh-adapter/sanitize.d.ts.map +1 -0
  89. package/lib/types/dsh-adapter/sanitize.js +65 -0
  90. package/lib/types/dsh-adapter/scenes.d.ts +7 -1
  91. package/lib/types/dsh-adapter/scenes.d.ts.map +1 -1
  92. package/lib/types/dsh-adapter/scenes.js +17 -2
  93. package/lib/types/dsh-adapter/shortcuts.d.ts +109 -0
  94. package/lib/types/dsh-adapter/shortcuts.d.ts.map +1 -0
  95. package/lib/types/dsh-adapter/shortcuts.js +268 -0
  96. package/lib/types/dsh-adapter/status.d.ts +75 -0
  97. package/lib/types/dsh-adapter/status.d.ts.map +1 -0
  98. package/lib/types/dsh-adapter/status.js +168 -0
  99. package/lib/types/extensions.d.ts +8 -0
  100. package/lib/types/extensions.d.ts.map +1 -0
  101. package/lib/types/extensions.js +15 -0
  102. package/lib/types/i18n.d.ts +162 -16
  103. package/lib/types/i18n.d.ts.map +1 -1
  104. package/lib/types/i18n.js +53 -5
  105. package/lib/types/ink/ink.d.ts +11 -0
  106. package/lib/types/ink/ink.d.ts.map +1 -1
  107. package/lib/types/ink/ink.js +33 -0
  108. package/lib/types/ink/log-update.d.ts.map +1 -1
  109. package/lib/types/ink/log-update.js +101 -51
  110. package/lib/types/ink/output.d.ts +7 -0
  111. package/lib/types/ink/output.d.ts.map +1 -1
  112. package/lib/types/ink/output.js +125 -4
  113. package/lib/types/ink/render-node-to-output.d.ts.map +1 -1
  114. package/lib/types/ink/render-node-to-output.js +14 -6
  115. package/lib/types/ink/terminal.d.ts.map +1 -1
  116. package/lib/types/ink/terminal.js +18 -6
  117. package/lib/types/plugin-host.d.ts +13 -0
  118. package/lib/types/plugin-host.d.ts.map +1 -0
  119. package/lib/types/plugin-host.js +17 -0
  120. package/lib/types/plugin-spec/negotiate.d.ts +11 -0
  121. package/lib/types/plugin-spec/negotiate.d.ts.map +1 -0
  122. package/lib/types/plugin-spec/negotiate.js +97 -0
  123. package/lib/types/plugin-spec/permission-scope.d.ts +32 -0
  124. package/lib/types/plugin-spec/permission-scope.d.ts.map +1 -0
  125. package/lib/types/plugin-spec/permission-scope.js +90 -0
  126. package/lib/types/plugin-spec/registry.d.ts +23 -0
  127. package/lib/types/plugin-spec/registry.d.ts.map +1 -0
  128. package/lib/types/plugin-spec/registry.js +157 -0
  129. package/lib/types/plugin-spec/schema-check.d.ts +17 -0
  130. package/lib/types/plugin-spec/schema-check.d.ts.map +1 -0
  131. package/lib/types/plugin-spec/schema-check.js +165 -0
  132. package/lib/types/plugin-spec/tui-extension.d.ts +16 -0
  133. package/lib/types/plugin-spec/tui-extension.d.ts.map +1 -0
  134. package/lib/types/plugin-spec/tui-extension.js +77 -0
  135. package/lib/types/plugin-spec/types.d.ts +114 -0
  136. package/lib/types/plugin-spec/types.d.ts.map +1 -0
  137. package/lib/types/plugin-spec/types.js +10 -0
  138. package/lib/types/plugin-spec/validate.d.ts +21 -0
  139. package/lib/types/plugin-spec/validate.d.ts.map +1 -0
  140. package/lib/types/plugin-spec/validate.js +119 -0
  141. package/lib/types/screens/Chat.d.ts +14 -1
  142. package/lib/types/screens/Chat.d.ts.map +1 -1
  143. package/lib/types/screens/Chat.js +142 -39
  144. package/lib/types/theme.d.ts +5 -0
  145. package/lib/types/theme.d.ts.map +1 -1
  146. package/lib/types/theme.js +15 -0
  147. package/lib/types/update.d.ts +20 -0
  148. package/lib/types/update.d.ts.map +1 -1
  149. package/lib/types/update.js +54 -7
  150. package/lib/types/utils/loaded-context.d.ts +8 -3
  151. package/lib/types/utils/loaded-context.d.ts.map +1 -1
  152. package/lib/types/utils/loaded-context.js +39 -3
  153. package/lib/types/utils/modifiers.d.ts.map +1 -1
  154. package/lib/types/utils/modifiers.js +3 -2
  155. package/lib/types/working-activity.d.ts +12 -0
  156. package/lib/types/working-activity.d.ts.map +1 -1
  157. package/lib/types/working-activity.js +13 -0
  158. package/node_modules/@dsh-std/command/LICENSE +21 -0
  159. package/node_modules/@dsh-std/command/README.md +26 -0
  160. package/node_modules/@dsh-std/command/README.zh.md +28 -0
  161. package/node_modules/@dsh-std/command/lib/index.d.ts +144 -0
  162. package/node_modules/@dsh-std/command/lib/index.d.ts.map +1 -0
  163. package/node_modules/@dsh-std/command/lib/index.js +252 -0
  164. package/node_modules/@dsh-std/command/lib/index.js.map +1 -0
  165. package/node_modules/@dsh-std/command/package.json +22 -0
  166. package/node_modules/@dsh-std/connection/LICENSE +21 -0
  167. package/node_modules/@dsh-std/connection/README.md +42 -0
  168. package/node_modules/@dsh-std/connection/README.zh.md +42 -0
  169. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  170. package/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  171. package/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  172. package/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  173. package/node_modules/@dsh-std/connection/lib/index.js +158 -0
  174. package/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  175. package/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  176. package/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  177. package/node_modules/@dsh-std/connection/lib/memory.js +257 -0
  178. package/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  179. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  180. package/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  181. package/node_modules/@dsh-std/connection/package.json +20 -0
  182. package/node_modules/@dsh-std/core/LICENSE +21 -0
  183. package/node_modules/@dsh-std/core/README.md +13 -0
  184. package/node_modules/@dsh-std/core/README.zh.md +13 -0
  185. package/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  186. package/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  187. package/node_modules/@dsh-std/core/lib/index.js +424 -0
  188. package/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  189. package/node_modules/@dsh-std/core/package.json +21 -0
  190. package/node_modules/@dsh-std/manifest/LICENSE +21 -0
  191. package/node_modules/@dsh-std/manifest/README.md +5 -0
  192. package/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  193. package/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  194. package/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  195. package/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  196. package/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  197. package/node_modules/@dsh-std/manifest/package.json +20 -0
  198. package/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  199. package/node_modules/@dsh-std/messages/LICENSE +21 -0
  200. package/node_modules/@dsh-std/messages/README.md +5 -0
  201. package/node_modules/@dsh-std/messages/README.zh.md +7 -0
  202. package/node_modules/@dsh-std/messages/lib/index.d.ts +50 -0
  203. package/node_modules/@dsh-std/messages/lib/index.d.ts.map +1 -0
  204. package/node_modules/@dsh-std/messages/lib/index.js +151 -0
  205. package/node_modules/@dsh-std/messages/lib/index.js.map +1 -0
  206. package/node_modules/@dsh-std/messages/package.json +20 -0
  207. package/node_modules/@dsh-std/messages/schema/message-observer-envelope-0.15.schema.json +54 -0
  208. package/node_modules/@dsh-std/presentation/LICENSE +21 -0
  209. package/node_modules/@dsh-std/presentation/README.md +26 -0
  210. package/node_modules/@dsh-std/presentation/README.zh.md +28 -0
  211. package/node_modules/@dsh-std/presentation/lib/index.d.ts +219 -0
  212. package/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  213. package/node_modules/@dsh-std/presentation/lib/index.js +547 -0
  214. package/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  215. package/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  216. package/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  217. package/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  218. package/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  219. package/node_modules/@dsh-std/presentation/package.json +21 -0
  220. package/node_modules/@dsh-std/storage/LICENSE +21 -0
  221. package/node_modules/@dsh-std/storage/README.md +5 -0
  222. package/node_modules/@dsh-std/storage/README.zh.md +7 -0
  223. package/node_modules/@dsh-std/storage/lib/index.d.ts +48 -0
  224. package/node_modules/@dsh-std/storage/lib/index.d.ts.map +1 -0
  225. package/node_modules/@dsh-std/storage/lib/index.js +104 -0
  226. package/node_modules/@dsh-std/storage/lib/index.js.map +1 -0
  227. package/node_modules/@dsh-std/storage/package.json +20 -0
  228. package/node_modules/@dsh-std/storage/schema/local-storage-0.15.schema.json +56 -0
  229. package/package.json +105 -70
  230. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/LICENSE +21 -0
  231. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.md +42 -0
  232. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/README.zh.md +42 -0
  233. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  234. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  235. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  236. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  237. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js +158 -0
  238. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  239. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  240. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  241. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js +257 -0
  242. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  243. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  244. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  245. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/connection/package.json +20 -0
  246. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/LICENSE +21 -0
  247. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.md +13 -0
  248. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/README.zh.md +13 -0
  249. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  250. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  251. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js +424 -0
  252. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  253. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/core/package.json +21 -0
  254. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/LICENSE +21 -0
  255. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.md +5 -0
  256. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/README.zh.md +5 -0
  257. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts +182 -0
  258. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.d.ts.map +1 -0
  259. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js +625 -0
  260. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/lib/index.js.map +1 -0
  261. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/package.json +20 -0
  262. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/manifest/schema/dsh-plugin-0.15.schema.json +144 -0
  263. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/LICENSE +21 -0
  264. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.md +26 -0
  265. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/README.zh.md +28 -0
  266. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts +219 -0
  267. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.d.ts.map +1 -0
  268. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js +547 -0
  269. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/index.js.map +1 -0
  270. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.d.ts +2 -0
  271. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/interaction.js +2 -0
  272. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.d.ts +2 -0
  273. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/lib/operations.js +2 -0
  274. package/vendor/dsh-std/packages/command/node_modules/@dsh-std/presentation/package.json +21 -0
  275. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/LICENSE +21 -0
  276. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.md +13 -0
  277. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/README.zh.md +13 -0
  278. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  279. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  280. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js +424 -0
  281. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  282. package/vendor/dsh-std/packages/connection/node_modules/@dsh-std/core/package.json +21 -0
  283. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/LICENSE +21 -0
  284. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.md +13 -0
  285. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/README.zh.md +13 -0
  286. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  287. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  288. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js +424 -0
  289. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  290. package/vendor/dsh-std/packages/manifest/node_modules/@dsh-std/core/package.json +21 -0
  291. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/LICENSE +21 -0
  292. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.md +13 -0
  293. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/README.zh.md +13 -0
  294. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  295. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  296. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js +424 -0
  297. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  298. package/vendor/dsh-std/packages/messages/node_modules/@dsh-std/core/package.json +21 -0
  299. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/LICENSE +21 -0
  300. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.md +42 -0
  301. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/README.zh.md +42 -0
  302. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts +125 -0
  303. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/connection-CdGcSQnu.d.ts.map +1 -0
  304. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts +89 -0
  305. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.d.ts.map +1 -0
  306. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js +158 -0
  307. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/index.js.map +1 -0
  308. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts +24 -0
  309. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.d.ts.map +1 -0
  310. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js +257 -0
  311. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/memory.js.map +1 -0
  312. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js +226 -0
  313. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/lib/resolve-3X6ausgz.js.map +1 -0
  314. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/connection/package.json +20 -0
  315. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/LICENSE +21 -0
  316. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.md +13 -0
  317. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/README.zh.md +13 -0
  318. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  319. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  320. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js +424 -0
  321. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  322. package/vendor/dsh-std/packages/presentation/node_modules/@dsh-std/core/package.json +21 -0
  323. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/LICENSE +21 -0
  324. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.md +13 -0
  325. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/README.zh.md +13 -0
  326. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts +112 -0
  327. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.d.ts.map +1 -0
  328. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js +424 -0
  329. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/lib/index.js.map +1 -0
  330. package/vendor/dsh-std/packages/storage/node_modules/@dsh-std/core/package.json +21 -0
@@ -27,6 +27,102 @@ import { modeDisplayName, resolveSessionModes } from '../sessionModes.js';
27
27
  import { ActivityTracker } from 'dsh-working-activity/status';
28
28
  import { attachSessionToWorkspace } from './workspace.js';
29
29
  import { createLocalWorkspaceRuntime } from './workspaces.js';
30
+ import { dispatchTuiDecision, dispatchTuiNotification, normalizeCancelDecision } from './extension-events.js';
31
+ import { installDecisionGuard } from './decision-guard.js';
32
+ import { commandOwner } from './command-attribution.js';
33
+ import { readGrantStore } from './grants.js';
34
+ import { hasCommandErrorCode, mapCommandError } from './command-errors.js';
35
+ import { pluginsInfoLines } from './plugins-info.js';
36
+ import { cleanRenderText, cleanScalarText } from './sanitize.js';
37
+ /** `tui/input` return normalization: transform/handled/cancel or no opinion.
38
+ * A blank `{ text }` rewrite is NOT a decision — it is logged and the chain
39
+ * continues so a later veto listener still runs. */
40
+ function normalizeInputDecision(result, warn) {
41
+ if (result === undefined || result === null || result === false)
42
+ return undefined;
43
+ if (typeof result !== 'object') {
44
+ warn(`a non-object (${typeof result})`);
45
+ return undefined;
46
+ }
47
+ const record = result;
48
+ if (record.cancel === true) {
49
+ const reason = cleanScalarText(record.reason, NOTICE_CELLS);
50
+ return { cancel: true, ...(reason === '' ? {} : { reason }) };
51
+ }
52
+ if (record.handled === true) {
53
+ const notice = cleanScalarText(record.notice, NOTICE_CELLS);
54
+ return { handled: true, ...(notice === '' ? {} : { notice }) };
55
+ }
56
+ if (typeof record.text === 'string') {
57
+ if (record.text.trim() === '') {
58
+ warn('a blank {text} rewrite');
59
+ return undefined;
60
+ }
61
+ return { text: record.text };
62
+ }
63
+ warn('an unrecognized decision shape');
64
+ return undefined;
65
+ }
66
+ /** `tui/rewind-prompt` return normalization: cancel/modes or no opinion.
67
+ * Modes are COPIED with only validated, sanitized scalar fields — the raw
68
+ * plugin object must never reach the render path (a `description: {}` would
69
+ * crash ListItem's `.replace`, and control chars would corrupt the pane).
70
+ * Notices/reasons are toast-bound plugin text: sanitized too (see
71
+ * ./sanitize.js — the one implementation of the render-path contract). */
72
+ function normalizeRewindPromptDecision(result, warn) {
73
+ if (result === undefined || result === null || result === false)
74
+ return undefined;
75
+ if (typeof result !== 'object') {
76
+ warn(`a non-object (${typeof result})`);
77
+ return undefined;
78
+ }
79
+ const record = result;
80
+ if (record.cancel === true) {
81
+ const reason = cleanScalarText(record.reason, NOTICE_CELLS);
82
+ return { cancel: true, ...(reason === '' ? {} : { reason }) };
83
+ }
84
+ if (Array.isArray(record.modes)) {
85
+ const modes = [];
86
+ for (const raw of record.modes) {
87
+ if (modes.length >= 8)
88
+ break;
89
+ if (raw === null || typeof raw !== 'object')
90
+ continue;
91
+ const candidate = raw;
92
+ if (typeof candidate.id !== 'string' || candidate.id.trim() === '')
93
+ continue;
94
+ const label = typeof candidate.label === 'string' ? cleanRenderText(candidate.label, 120) : '';
95
+ if (label === '')
96
+ continue;
97
+ const description = typeof candidate.description === 'string' && candidate.description.trim() !== ''
98
+ ? cleanRenderText(candidate.description, 400)
99
+ : undefined;
100
+ modes.push({ id: candidate.id, label, ...(description === undefined ? {} : { description }) });
101
+ }
102
+ if (modes.length === 0) {
103
+ warn('an empty or invalid {modes} list');
104
+ return undefined;
105
+ }
106
+ return { modes };
107
+ }
108
+ warn('an unrecognized decision shape');
109
+ return undefined;
110
+ }
111
+ /** Toast-bound plugin text (veto reasons, handled notices, rewind summaries)
112
+ * is render-path data too: same sanitization, toast-width cap. */
113
+ const NOTICE_CELLS = 200;
114
+ /** `tui/rewind-done` return normalization: the first non-empty STRING is the
115
+ * summary; anything else is not a decision. */
116
+ function normalizeRewindDoneSummary(result, warn) {
117
+ if (result === undefined || result === null || result === false)
118
+ return undefined;
119
+ if (typeof result === 'string') {
120
+ const summary = cleanRenderText(result, NOTICE_CELLS);
121
+ return summary === '' ? undefined : summary;
122
+ }
123
+ warn('a non-string summary');
124
+ return undefined;
125
+ }
30
126
  /**
31
127
  * Delay before re-reading a skill catalog that reported an incomplete
32
128
  * observation (a provider whose directory watcher is still warming).
@@ -288,12 +384,29 @@ async function waitForTurnEnd(session, fromSeq, timeoutMs) {
288
384
  export function createChannel(ctx, initialAgent, options) {
289
385
  let agent = initialAgent;
290
386
  let currentHandle = options.handle;
387
+ // D-7 backstop: the extensions row installs the decision-subscription
388
+ // gate, but the channel IS the dispatch path — a stale patch without that
389
+ // row (or a bare embed mounting neither) would otherwise leave tui/input
390
+ // & friends subscribable by default, silently voiding the default-deny
391
+ // posture. Idempotent per cordis root, so the full-patch path installs
392
+ // exactly once whichever side runs first. One store instance serves both
393
+ // the gate and the invoke checkpoint below.
394
+ // Keep a private fallback for bare embedders, but resolve the host-owned
395
+ // store on every operation so a plugin-host row mounted later (or a custom
396
+ // live GrantStore) is not shadowed by an early snapshot.
397
+ const fallbackGrantStore = readGrantStore();
398
+ const currentGrantStore = () => ctx.get('tuiPluginHost')?.grants ?? fallbackGrantStore;
399
+ installDecisionGuard(ctx, currentGrantStore());
291
400
  // The DSH slash-command registry (optional service): /plan, /goal and
292
401
  // friends register here; the TUI merges their descriptors into the slash
293
402
  // menu and dispatches through `execute` (which logs the paired
294
403
  // command/run + command/done records). Absent the service, only the
295
404
  // built-in local commands exist.
296
405
  const commandService = ctx.get('commands');
406
+ // messages.observe broker (optional service, C-042): mounted by the
407
+ // dsh-tui-plugin-host row; absent the row, publish is a no-op and nothing
408
+ // else changes (soft degradation, #183).
409
+ const messageObserver = ctx.get('tuiMessageObserver');
297
410
  // Workspace registry runtime (optional service, issue #183): mounted by
298
411
  // the bundle patch's dsh-tui-workspaces row; absent the row (stale patch
299
412
  // or a bare embedder), degrade to the local-only runtime. plugin.ts owns
@@ -311,6 +424,10 @@ export function createChannel(ctx, initialAgent, options) {
311
424
  // tuiWorkspaces/tuiCommandTrees): mounted by the bundle patch's
312
425
  // dsh-tui-scenes row; absent the row, `pluginScene` simply stays undefined.
313
426
  const sceneRuntime = ctx.get('tuiScenes');
427
+ // Custom-entry text renderers (optional service, dsh-tui-extensions row):
428
+ // absent the row, unknown plugin event types stay invisible in the
429
+ // transcript, exactly as before the seam existed.
430
+ const rendererRuntime = ctx.get('tuiRenderers');
314
431
  // Shift+Tab session-mode cycle: cordis.yml `modes` wins; absent/empty/
315
432
  // atom-less → the built-in default/plan/full cycle (sessionModes.ts).
316
433
  const { modes: sessionModes, dropped: droppedModeIds } = resolveSessionModes(options.modes);
@@ -405,6 +522,142 @@ export function createChannel(ctx, initialAgent, options) {
405
522
  state.notify(t('send-failed', { err: message }), { color: 'error' });
406
523
  });
407
524
  };
525
+ /**
526
+ * RFC 0005 D-8: a flow parked on a plugin decision must be user-observable.
527
+ * Decisions normally resolve in milliseconds, so the notice only fires
528
+ * once the wait crosses a threshold — a slow plugin (e.g. one showing a
529
+ * managed dialog) then explains the pause instead of looking like the TUI
530
+ * ate the input.
531
+ */
532
+ const DECISION_PENDING_MS = 400;
533
+ const withDecisionPending = (name, pending) => {
534
+ let dismiss;
535
+ const timer = setTimeout(() => {
536
+ // Sticky (timeoutMs 0), D-8: the indicator must cover the WHOLE wait —
537
+ // an auto-expiring notice would vanish after ~4s while the decision,
538
+ // the delivery and every queued FIFO task behind them stay parked,
539
+ // leaving the user with no sign the flow is still waiting. It comes
540
+ // down only when the decision settles (finally below); a decision
541
+ // that never settles keeps its indicator up, which is the truthful
542
+ // state.
543
+ dismiss = state.notify(t('ext-decision-pending', { event: name }), { timeoutMs: 0 });
544
+ }, DECISION_PENDING_MS);
545
+ // Both exits are covered: a fast decision clears the timer before it
546
+ // fires; a slow one dismisses the indicator it raised.
547
+ return pending.finally(() => {
548
+ clearTimeout(timer);
549
+ dismiss?.();
550
+ });
551
+ };
552
+ /**
553
+ * The `tui/input` decision event (pi's `input` seam): the FIRST plugin
554
+ * returning a valid decision wins — transform the text, mark it handled,
555
+ * or cancel it. No listeners (or only crashing/malformed ones) means
556
+ * delivery proceeds unchanged, so a broken plugin can never wedge the
557
+ * input path — and can never skip a later veto listener either
558
+ * (dispatchTuiDecision isolates crashes and normalizes returns per
559
+ * listener instead of bailing on the first object).
560
+ *
561
+ * Decision AND delivery enter one FIFO chain in submission order: a slow
562
+ * listener on A parks A's delivery AND any later submissions behind it —
563
+ * without the chain, B's decision could resolve first and the model would
564
+ * receive B before A. Each submission binds its origin agent AT ENQUEUE,
565
+ * so a session switch landing before OR during its decision drops the
566
+ * stale text with a notice instead of sending the old conversation's
567
+ * words to the new session.
568
+ */
569
+ let inputChain = Promise.resolve();
570
+ const runUserTextDecision = async (text, placement, originAgent, originAgentId) => {
571
+ // Stale detection compares the AGENT REFERENCE, not the id: session ids
572
+ // are reusable (A → /new → /resume A lands back on the same id with a
573
+ // fresh agent), so an id check has an ABA hole. Both origin values are
574
+ // ENQUEUE-time captures (see dispatchUserText): a decision parked behind
575
+ // a slow predecessor must still be judged against the session its text
576
+ // was typed in, not whichever session is live when it finally runs.
577
+ const decision = await withDecisionPending('tui/input', dispatchTuiDecision(ctx, 'tui/input', {
578
+ text,
579
+ delivery: placement === 'steer' ? 'steer' : 'followup',
580
+ sessionId: originAgentId,
581
+ cwd: state.cwd,
582
+ }, normalizeInputDecision));
583
+ if (decision !== undefined) {
584
+ // Both intercepts toast — a bare {cancel}/{handled} must not make the
585
+ // typed line vanish silently (the host-localized fallback mirrors the
586
+ // other decision events' ext-action-cancelled handling).
587
+ if ('cancel' in decision) {
588
+ state.notify(decision.reason ?? t('ext-action-cancelled'), { color: 'warning', timeoutMs: 4000 });
589
+ return;
590
+ }
591
+ if ('handled' in decision) {
592
+ state.notify(decision.notice ?? t('ext-action-handled'), { timeoutMs: 4000 });
593
+ return;
594
+ }
595
+ text = decision.text.trim();
596
+ }
597
+ if (agent !== originAgent) {
598
+ state.notify(t('ext-stale-dropped'), { color: 'warning', timeoutMs: 4000 });
599
+ return;
600
+ }
601
+ deliverUserText(text, placement);
602
+ };
603
+ const dispatchUserText = (text, placement) => {
604
+ // D-6: bind the submission to the session it was typed in AT ENQUEUE
605
+ // TIME. The FIFO chain may park this task behind a slow predecessor
606
+ // while the user /new's away — capturing the agent at run time would
607
+ // adopt the NEW session as this text's origin and deliver the old
608
+ // conversation's words into it.
609
+ const originAgent = agent;
610
+ const originAgentId = state.agentId;
611
+ inputChain = inputChain.then(() => runUserTextDecision(text, placement, originAgent, originAgentId)).catch((error) => {
612
+ // The chain must survive a failed decision: log, then continue with
613
+ // the next queued submission.
614
+ ctx.logger.warn('dsh-tui: tui/input dispatch failed: %o', error);
615
+ });
616
+ };
617
+ /**
618
+ * The `tui/session-switch` decision event (pi's `session_before_switch`),
619
+ * fired before `/new` or `/resume` replaces the live session (rewind has
620
+ * its own prompt event). The first answering plugin may veto the switch;
621
+ * the reason is toasted here so the fallback string stays host-localized.
622
+ */
623
+ const sessionSwitchVetoed = async (kind, targetSessionId) => {
624
+ // D-6 stale detection captures the AGENT REFERENCE (session ids are
625
+ // reusable — ABA): a slow decision must not let an older /resume roll
626
+ // over a newer session the user already switched to mid-await.
627
+ const originAgent = agent;
628
+ const decision = await withDecisionPending('tui/session-switch', dispatchTuiDecision(ctx, 'tui/session-switch', {
629
+ kind,
630
+ ...(targetSessionId === undefined ? {} : { targetSessionId }),
631
+ sessionId: state.agentId,
632
+ cwd: state.cwd,
633
+ }, normalizeCancelDecision));
634
+ if (agent !== originAgent) {
635
+ // The world changed while the decision parked: drop the pending
636
+ // switch instead of replacing the user's newer session.
637
+ state.notify(t('ext-stale-dropped'), { color: 'warning', timeoutMs: 4000 });
638
+ return true;
639
+ }
640
+ if (decision !== undefined) {
641
+ state.notify(decision.reason ?? t('ext-action-cancelled'), { color: 'warning', timeoutMs: 4000 });
642
+ return true;
643
+ }
644
+ return false;
645
+ };
646
+ /** Fire-and-forget `tui/session-switched` (parallel): per-session plugin
647
+ * state rebinds here. Listener failures are logged, never propagated —
648
+ * the switch itself already succeeded. */
649
+ const notifySessionSwitched = (kind, sessionId, previousSessionId) => {
650
+ try {
651
+ void dispatchTuiNotification(ctx, 'tui/session-switched', { kind, sessionId, previousSessionId, cwd: state.cwd }).catch((error) => {
652
+ ctx.logger.warn('dsh-tui: tui/session-switched listener failed: %o', error);
653
+ });
654
+ }
655
+ catch (error) {
656
+ // A bare embedder's context may lack the event bus entirely; the
657
+ // switch itself already succeeded, so this stays a log line.
658
+ ctx.logger.warn('dsh-tui: tui/session-switched dispatch failed: %o', error);
659
+ }
660
+ };
408
661
  /** Monotonic token: only the latest `interruptAndDeliver` re-queues, so a
409
662
  * second interrupt while the abort settles cannot double-deliver. */
410
663
  let interruptSeq = 0;
@@ -519,6 +772,45 @@ export function createChannel(ctx, initialAgent, options) {
519
772
  const executeRegistryCommand = async (name, rawInput) => {
520
773
  if (!commandService)
521
774
  return undefined;
775
+ // Resolve the exact definition that execute() will select for this agent.
776
+ // Same names may exist in distinct agent scopes, so a name-only lookup can
777
+ // apply another scope's owner policy.
778
+ const definition = commandService.find(agent, name);
779
+ const owner = commandOwner(ctx, definition);
780
+ // The root checkpoint covers host/direct registrations. A built-in name
781
+ // is not necessarily a namespaced contribution id, so use a deterministic
782
+ // host scope for that case; legacy unscoped denies still conservatively
783
+ // revoke every valid command scope.
784
+ const rootScope = owner?.commandId
785
+ ?? (/^[a-z][a-z0-9]*(?:[.-][a-z0-9][a-z0-9-]*)+$/u.test(name)
786
+ ? name
787
+ : `dsh-tui.${name.toLowerCase().replace(/[^a-z0-9-]+/gu, '-').replace(/^-+|-+$/gu, '') || 'command'}`);
788
+ if (!currentGrantStore().allows({ componentId: 'root' }, 'commands.invoke', rootScope)) {
789
+ ctx.logger.warn('dsh-tui: registry command invocation denied (commands.invoke revoked for "root" in the grants file)');
790
+ ctx.get('tuiEffectLedger')?.record({
791
+ operation: 'bind',
792
+ resource: { kind: 'permission', id: `root:commands.invoke:${rootScope}` },
793
+ result: 'failed',
794
+ errorCode: 'PERMISSION_NOT_GRANTED',
795
+ }, ctx);
796
+ return t('command-invoke-denied');
797
+ }
798
+ // Per-owner gate (C-041): a command REGISTERED BY A PLUGIN through the
799
+ // plugin-host row's mediated registerCommand (see command-attribution.js)
800
+ // is additionally gated on the OWNER's grant, so a denies entry for the
801
+ // plugin closes the host-mediated invocation of ITS commands.
802
+ // Unattributed host/direct registrations remain inside the documented
803
+ // trusted-in-process boundary and have no plugin grant to evaluate.
804
+ if (owner !== undefined && !currentGrantStore().allows({ componentId: owner.componentId, activationId: owner.activationId }, 'commands.invoke', owner.commandId)) {
805
+ ctx.logger.warn(`dsh-tui: registry command "/${name}" invocation denied — owner Component "${owner.componentId}" lost commands.invoke for "${owner.commandId}"`);
806
+ ctx.get('tuiEffectLedger')?.record({
807
+ operation: 'bind',
808
+ resource: { kind: 'permission', id: `${owner.componentId}:commands.invoke:${owner.commandId}` },
809
+ result: 'failed',
810
+ errorCode: 'PERMISSION_NOT_GRANTED',
811
+ }, ctx);
812
+ return t('command-invoke-denied-owner', { name, owner: owner.componentId });
813
+ }
522
814
  try {
523
815
  const execution = await commandService.execute(agent, `/${name}${rawInput}`, new AbortController().signal);
524
816
  // `undefined` = not registered; a handler error surfaces as its
@@ -778,7 +1070,7 @@ export function createChannel(ctx, initialAgent, options) {
778
1070
  // The current session is being used — move it to the MRU front
779
1071
  // (/resume sorts by last-used).
780
1072
  touchSession(state.agentId);
781
- deliverUserText(trimmed, 'followup');
1073
+ void dispatchUserText(trimmed, 'followup');
782
1074
  },
783
1075
  /** Steer a message into the RUNNING turn (Codex/pi semantics): it is
784
1076
  * injected at the next step boundary of the current turn and the agent
@@ -788,11 +1080,13 @@ export function createChannel(ctx, initialAgent, options) {
788
1080
  if (!trimmed)
789
1081
  return;
790
1082
  touchSession(state.agentId);
791
- // Official dsh-agent rc.6: steer() is synchronous void — the message
792
- // enters the next-step inbox. A rejected step leaves it parked for the
793
- // next wake; the inbox events below retire the preview (claimed →
794
- // turn boundary, discarded → cancel).
795
- deliverUserText(trimmed, 'steer');
1083
+ // Same tui/input decision pass as submit; the delivery re-validates
1084
+ // the live agent after the await. Official dsh-agent rc.6: steer() is
1085
+ // synchronous void — the message enters the next-step inbox; a
1086
+ // rejected step leaves it parked for the next wake, and the inbox
1087
+ // events retire the preview (claimed → turn boundary, discarded →
1088
+ // cancel).
1089
+ void dispatchUserText(trimmed, 'steer');
796
1090
  },
797
1091
  /** Pull a pending message back out of the inbox (Alt+Up): it returns to
798
1092
  * the input for editing instead of being delivered. */
@@ -838,7 +1132,10 @@ export function createChannel(ctx, initialAgent, options) {
838
1132
  return;
839
1133
  for (const text of queued) {
840
1134
  touchSession(state.agentId);
841
- deliverUserText(text, 'followup');
1135
+ // Same tui/input decision pass as a typed submit: Ctrl+Enter must
1136
+ // not bypass a plugin's cancel/transform policy, and re-queued
1137
+ // texts keep submission order through the one FIFO chain.
1138
+ dispatchUserText(text, 'followup');
842
1139
  }
843
1140
  };
844
1141
  if (typeof whenIdle === 'function') {
@@ -851,7 +1148,43 @@ export function createChannel(ctx, initialAgent, options) {
851
1148
  }
852
1149
  return queued.length;
853
1150
  },
854
- async rewindTo(row) {
1151
+ /**
1152
+ * The `tui/rewind-prompt` decision event (pi's `session_before_fork`):
1153
+ * fired when the rewind picker confirms a message, before any fork
1154
+ * work. The first answering plugin may cancel the rewind (the picker
1155
+ * stays open; the reason is toasted here so the UI string stays
1156
+ * host-localized when absent) or offer extra modes rendered in the
1157
+ * confirm pane. Returns 'cancel', the modes, or null for "no opinion".
1158
+ */
1159
+ async promptRewind(row) {
1160
+ if (row.seq === undefined)
1161
+ return null;
1162
+ // D-6, same as the other decision points: a slow /new or /resume can
1163
+ // replace the agent while this decision parks. Without the identity
1164
+ // check the picker would go on to show the OLD session's row in the
1165
+ // confirm pane and rewindTo would cut the NEW session at the old
1166
+ // seq — a wrong rewind or a fork failure. Compare agent REFERENCES
1167
+ // (session ids are reusable — ABA) and stale-cancel.
1168
+ const originAgent = agent;
1169
+ const decision = await withDecisionPending('tui/rewind-prompt', dispatchTuiDecision(ctx, 'tui/rewind-prompt', {
1170
+ text: row.text,
1171
+ seq: row.seq,
1172
+ sessionId: state.agentId,
1173
+ cwd: state.cwd,
1174
+ }, normalizeRewindPromptDecision));
1175
+ if (agent !== originAgent) {
1176
+ state.notify(t('ext-stale-dropped'), { color: 'warning', timeoutMs: 4000 });
1177
+ return 'cancel';
1178
+ }
1179
+ if (decision === undefined)
1180
+ return null;
1181
+ if ('cancel' in decision) {
1182
+ state.notify(decision.reason ?? t('ext-action-cancelled'), { color: 'warning', timeoutMs: 4000 });
1183
+ return 'cancel';
1184
+ }
1185
+ return { modes: decision.modes };
1186
+ },
1187
+ async rewindTo(row, mode = null) {
855
1188
  if (row.seq === undefined)
856
1189
  return null;
857
1190
  const sessions = ctx.get('sessions');
@@ -984,6 +1317,7 @@ export function createChannel(ctx, initialAgent, options) {
984
1317
  renderEvent(event);
985
1318
  // Rebind subscriptions to the new agent, then free the old one.
986
1319
  const oldHandle = currentHandle;
1320
+ const sourceSessionId = String(agent.session.id);
987
1321
  agent = handle.agent;
988
1322
  currentHandle = handle;
989
1323
  bindAgent();
@@ -994,6 +1328,43 @@ export function createChannel(ctx, initialAgent, options) {
994
1328
  touchSession(childId);
995
1329
  state.emit();
996
1330
  void oldHandle?.dispose().catch(() => { });
1331
+ // Decision-event pair around the completed rewind: `tui/rewind-done`
1332
+ // (the first non-empty string is toasted as the post-rewind summary,
1333
+ // e.g. a plugin reporting restored files) and the generic
1334
+ // `tui/session-switched` notification. Listener failures are logged,
1335
+ // never surfaced — the rewind itself already succeeded.
1336
+ //
1337
+ // rewind-done is a post-hoc summary, NOT a gate, so it is dispatched
1338
+ // DECOUPLED from the return value: the picker is already closed and
1339
+ // PromptInput is live — awaiting a slow listener here would delay the
1340
+ // picked text's return to the draft, letting its late arrival
1341
+ // overwrite whatever the user typed meanwhile, and a listener that
1342
+ // never settles would park tui/session-switched forever. The summary
1343
+ // toasts whenever it lands.
1344
+ try {
1345
+ void dispatchTuiDecision(ctx, 'tui/rewind-done', {
1346
+ text: row.text,
1347
+ mode,
1348
+ boundarySeq: boundary,
1349
+ sourceSessionId,
1350
+ childSessionId: String(childId),
1351
+ sessionId: String(childId),
1352
+ cwd: state.cwd,
1353
+ }, normalizeRewindDoneSummary)
1354
+ .then(summary => {
1355
+ if (summary !== undefined)
1356
+ state.notify(summary, { timeoutMs: 6000 });
1357
+ })
1358
+ .catch((error) => {
1359
+ ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error);
1360
+ });
1361
+ }
1362
+ catch (error) {
1363
+ // A bare embedder's context may lack the event bus entirely; the
1364
+ // rewind itself already succeeded, so this stays a log line.
1365
+ ctx.logger.warn('dsh-tui: tui/rewind-done dispatch failed: %o', error);
1366
+ }
1367
+ notifySessionSwitched('rewind', String(childId), sourceSessionId);
997
1368
  return row.text;
998
1369
  },
999
1370
  async resumeTo(sessionId) {
@@ -1009,6 +1380,11 @@ export function createChannel(ctx, initialAgent, options) {
1009
1380
  state.notify(t('resume-unavailable'), { color: 'error' });
1010
1381
  return false;
1011
1382
  }
1383
+ // Plugin veto point (tui/session-switch): before any read of the
1384
+ // target — a veto leaves the live session and its transcript
1385
+ // untouched.
1386
+ if (await sessionSwitchVetoed('resume', sessionId))
1387
+ return false;
1012
1388
  let handle;
1013
1389
  // Compat boundary: register vouched-for legacy event types (e.g.
1014
1390
  // activity/status from pre-#143 logs) in every reachable dsh-session
@@ -1111,6 +1487,7 @@ export function createChannel(ctx, initialAgent, options) {
1111
1487
  settleStreaming();
1112
1488
  // Rebind subscriptions to the resumed agent, then free the old one.
1113
1489
  const oldHandle = currentHandle;
1490
+ const previousSessionId = String(agent.session.id);
1114
1491
  agent = handle.agent;
1115
1492
  currentHandle = handle;
1116
1493
  bindAgent();
@@ -1124,6 +1501,7 @@ export function createChannel(ctx, initialAgent, options) {
1124
1501
  state.emit();
1125
1502
  void oldHandle?.dispose().catch(() => { });
1126
1503
  clearStagedImages();
1504
+ notifySessionSwitched('resume', sessionId, previousSessionId);
1127
1505
  return true;
1128
1506
  },
1129
1507
  async newSession() {
@@ -1143,6 +1521,10 @@ export function createChannel(ctx, initialAgent, options) {
1143
1521
  });
1144
1522
  return false;
1145
1523
  }
1524
+ // Plugin veto point (tui/session-switch): no side effects have
1525
+ // happened yet — the session id below is not even allocated.
1526
+ if (await sessionSwitchVetoed('new'))
1527
+ return false;
1146
1528
  const sessionId = SessionId(randomUUID());
1147
1529
  let handle;
1148
1530
  // A fresh session composes the caller's DEFAULT preset: the cordis.yml
@@ -1234,6 +1616,7 @@ export function createChannel(ctx, initialAgent, options) {
1234
1616
  tools: 0,
1235
1617
  };
1236
1618
  const oldHandle = currentHandle;
1619
+ const previousSessionId = String(agent.session.id);
1237
1620
  agent = handle.agent;
1238
1621
  currentHandle = handle;
1239
1622
  bindAgent();
@@ -1245,6 +1628,7 @@ export function createChannel(ctx, initialAgent, options) {
1245
1628
  touchSession(handle.agent.id);
1246
1629
  void oldHandle?.dispose().catch(() => { });
1247
1630
  clearStagedImages();
1631
+ notifySessionSwitched('new', String(handle.agent.id), previousSessionId);
1248
1632
  return true;
1249
1633
  },
1250
1634
  listWorkspaces() {
@@ -1456,13 +1840,24 @@ export function createChannel(ctx, initialAgent, options) {
1456
1840
  };
1457
1841
  state.notifications.push(item);
1458
1842
  state.emit();
1459
- setTimeout(() => {
1843
+ const remove = () => {
1460
1844
  const index = state.notifications.indexOf(item);
1461
1845
  if (index >= 0) {
1462
1846
  state.notifications.splice(index, 1);
1463
1847
  state.emit();
1464
1848
  }
1465
- }, item.timeoutMs);
1849
+ };
1850
+ // timeoutMs 0 = sticky: no expiry timer, the dismiss handle is the
1851
+ // only way out (a decision-parked indicator must outlive the wait it
1852
+ // describes — auto-expiring it would hide a still-parked flow, D-8).
1853
+ const expire = item.timeoutMs > 0 ? setTimeout(remove, item.timeoutMs) : undefined;
1854
+ // Early-dismiss handle for flows that know their notice went stale
1855
+ // (e.g. a decision-parked indicator whose decision just landed).
1856
+ return () => {
1857
+ if (expire !== undefined)
1858
+ clearTimeout(expire);
1859
+ remove();
1860
+ };
1466
1861
  },
1467
1862
  setDiffLayout(layout) {
1468
1863
  if (layout === state.diffLayout)
@@ -1839,16 +2234,49 @@ export function createChannel(ctx, initialAgent, options) {
1839
2234
  state.notify(t('compact-while-working'), { color: 'warning' });
1840
2235
  return;
1841
2236
  }
1842
- const signal = new AbortController().signal;
1843
- state.notify(t('compact-working'));
1844
- void compactService
1845
- .compactNow(agent, signal)
1846
- .then((result) => {
1847
- state.notify(result ? t('compact-done') : t('compact-nothing'));
1848
- })
1849
- .catch((error) => {
1850
- state.notify(t('compact-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'error', timeoutMs: 8000 });
1851
- });
2237
+ // Plugin veto point (tui/compact): the first answering plugin may
2238
+ // cancel the compaction before anything runs.
2239
+ const originAgentId = state.agentId;
2240
+ // Compare the AGENT REFERENCE after the await, not the id — session
2241
+ // ids are reusable (A → /new → /resume A returns the same id on a new
2242
+ // agent), so an id comparison has an ABA hole that would hand the new
2243
+ // agent to the OLD scope's compaction service.
2244
+ const originAgent = agent;
2245
+ void (async () => {
2246
+ const decision = await withDecisionPending('tui/compact', dispatchTuiDecision(ctx, 'tui/compact', {
2247
+ sessionId: originAgentId,
2248
+ cwd: state.cwd,
2249
+ }, normalizeCancelDecision));
2250
+ if (decision !== undefined) {
2251
+ state.notify(decision.reason ?? t('ext-action-cancelled'), { color: 'warning', timeoutMs: 4000 });
2252
+ return;
2253
+ }
2254
+ // Stale-drop (same rule as tui/input): the await parked us while the
2255
+ // user switched sessions — `compactService` was resolved through the
2256
+ // OLD agent's scope chain, and the mutable `agent` now points at the
2257
+ // new session. Running now would hand the new agent to the old
2258
+ // service (or call into an unloaded one).
2259
+ if (agent !== originAgent) {
2260
+ state.notify(t('ext-compact-stale'), { color: 'warning', timeoutMs: 4000 });
2261
+ return;
2262
+ }
2263
+ if (state.working) {
2264
+ // The await above gave a queued turn time to start; compacting
2265
+ // mid-turn now would be the same race the check upfront avoided.
2266
+ state.notify(t('compact-while-working'), { color: 'warning' });
2267
+ return;
2268
+ }
2269
+ const signal = new AbortController().signal;
2270
+ state.notify(t('compact-working'));
2271
+ void compactService
2272
+ .compactNow(agent, signal)
2273
+ .then((result) => {
2274
+ state.notify(result ? t('compact-done') : t('compact-nothing'));
2275
+ })
2276
+ .catch((error) => {
2277
+ state.notify(t('compact-failed', { err: error instanceof Error ? error.message : String(error) }), { color: 'error', timeoutMs: 8000 });
2278
+ });
2279
+ })();
1852
2280
  },
1853
2281
  runExternalCommand(name, rawInput) {
1854
2282
  return executeRegistryCommand(name, rawInput);
@@ -2017,8 +2445,23 @@ export function createChannel(ctx, initialAgent, options) {
2017
2445
  if (existsSync(LEGACY_DATA_DIR)) {
2018
2446
  lines.push(t('doctor-legacy-dir'));
2019
2447
  }
2448
+ // Plugin-spec diagnostics (v0.15): the runtime generation and the
2449
+ // vendored registry self-check, both soft-probed (#183 discipline).
2450
+ const pluginHost = ctx.get('tuiPluginHost');
2451
+ lines.push(t('doctor-plugin-generation', { id: pluginHost?.generationId ?? t('doctor-plugin-host-missing') }));
2452
+ const violations = pluginHost?.selfCheck();
2453
+ lines.push(t('doctor-plugin-registry', {
2454
+ state: violations === undefined ? t('doctor-plugin-host-missing') : violations.length === 0 ? '✓' : `✗ ${violations.length}`,
2455
+ }));
2020
2456
  return lines;
2021
2457
  },
2458
+ pluginsInfo(args) {
2459
+ const host = ctx.get('tuiPluginHost');
2460
+ return pluginsInfoLines(args, {
2461
+ grants: host?.grants ?? currentGrantStore(),
2462
+ host: host?.describe(),
2463
+ });
2464
+ },
2022
2465
  async listSubagents() {
2023
2466
  const subagents = ctx.get('subagents');
2024
2467
  if (!subagents)
@@ -2372,10 +2815,21 @@ export function createChannel(ctx, initialAgent, options) {
2372
2815
  },
2373
2816
  });
2374
2817
  skillCommands.set(name, { dispose, description });
2818
+ ctx.get('tuiEffectLedger')?.record({ operation: 'create', resource: { kind: 'command', id: name }, result: 'applied' }, ctx);
2375
2819
  }
2376
2820
  catch (error) {
2821
+ // C-041: a duplicate registration arrives as a plain-message Error
2822
+ // from dsh-commands; map it onto the contract code before handling
2823
+ // (the refusal path itself is unchanged).
2824
+ const mapped = mapCommandError(error);
2377
2825
  skillCommandsRefused.add(name);
2378
- ctx.logger.warn(`skill commands: "${name}" not registrable: %o`, error);
2826
+ ctx.logger.warn(`skill commands: "${name}" not registrable%s: %o`, hasCommandErrorCode(mapped, 'DUPLICATE_CONTRIBUTION_ID') ? ' (DUPLICATE_CONTRIBUTION_ID)' : '', mapped);
2827
+ ctx.get('tuiEffectLedger')?.record({
2828
+ operation: 'create',
2829
+ resource: { kind: 'command', id: name },
2830
+ result: 'failed',
2831
+ errorCode: hasCommandErrorCode(mapped, 'DUPLICATE_CONTRIBUTION_ID') ? 'DUPLICATE_CONTRIBUTION_ID' : 'COMMAND_FAILED',
2832
+ }, ctx);
2379
2833
  }
2380
2834
  }
2381
2835
  };
@@ -2976,6 +3430,30 @@ ${output}
2976
3430
  text: t('agent-preset-switched', { preset: data.agentPreset ?? 'unknown' }),
2977
3431
  });
2978
3432
  nextRowId += 1;
3433
+ break;
3434
+ }
3435
+ // Custom plugin events (tuiRenderers seam): a registered renderer
3436
+ // maps the payload to text rows — title as a local row, body as
3437
+ // preview-clipped local-output rows, same shape pushLocal uses.
3438
+ // Runs on the live stream AND on replay (resume/rewind), so the
3439
+ // projection must stay total; the runtime isolates renderer
3440
+ // crashes per type.
3441
+ if (rendererRuntime !== undefined) {
3442
+ const rendered = rendererRuntime.render(event.type, event.data);
3443
+ if (rendered !== undefined) {
3444
+ if (rendered.title !== undefined && rendered.title !== '') {
3445
+ state.rows.push({ id: nextRowId, kind: 'local', text: rendered.title });
3446
+ nextRowId += 1;
3447
+ }
3448
+ for (const line of rendered.lines) {
3449
+ state.rows.push({
3450
+ id: nextRowId,
3451
+ kind: 'local-output',
3452
+ text: preview(String(line), LOCAL_OUTPUT_LIMIT),
3453
+ });
3454
+ nextRowId += 1;
3455
+ }
3456
+ }
2979
3457
  }
2980
3458
  break;
2981
3459
  }
@@ -3113,6 +3591,10 @@ ${output}
3113
3591
  ctx.on('session/event', (session, event) => {
3114
3592
  if (session !== agent.session)
3115
3593
  return;
3594
+ // Observation broker (C-042): maps user/message + assistant/message
3595
+ // into grant-gated envelopes; every other event type is a no-op, and
3596
+ // publish never throws into this arm.
3597
+ messageObserver?.publish(session, event);
3116
3598
  activityTracker.onSessionEvent(event);
3117
3599
  renderWorkingActivity();
3118
3600
  // Mode-affecting atoms fold into the Shift+Tab mode indicator the