@deepseek-harness-tui/dsh-tui 0.15.0 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (301) hide show
  1. package/README.md +92 -5
  2. package/guide/dsh-tui-guide/codex-backend.en.md +13 -3
  3. package/guide/dsh-tui-guide/codex-backend.md +9 -2
  4. package/guide/dsh-tui-guide/configuration.en.md +39 -2
  5. package/guide/dsh-tui-guide/configuration.md +34 -2
  6. package/guide/dsh-tui-guide/getting-started.en.md +15 -0
  7. package/guide/dsh-tui-guide/getting-started.md +15 -0
  8. package/guide/dsh-tui-guide/interaction.en.md +8 -1
  9. package/guide/dsh-tui-guide/interaction.md +8 -1
  10. package/guide/dsh-tui-guide/plugins.en.md +66 -5
  11. package/guide/dsh-tui-guide/plugins.md +37 -3
  12. package/guide/dsh-tui-guide/user-guide.en.md +3 -2
  13. package/guide/dsh-tui-guide/user-guide.md +3 -2
  14. package/lib/settings.json +1 -1
  15. package/lib/types/adapter/channel/projection.d.ts.map +1 -1
  16. package/lib/types/adapter/channel/projection.js +1 -0
  17. package/lib/types/adapter/channel/ui-policy.d.ts +1 -1
  18. package/lib/types/adapter/channel/ui-policy.d.ts.map +1 -1
  19. package/lib/types/adapter/channel/ui-policy.js +1 -0
  20. package/lib/types/adapter/ports/channel-catalog.d.ts +5 -1
  21. package/lib/types/adapter/ports/channel-catalog.d.ts.map +1 -1
  22. package/lib/types/adapter/ports/channel-settings.d.ts +5 -3
  23. package/lib/types/adapter/ports/channel-settings.d.ts.map +1 -1
  24. package/lib/types/adapter/ports/channel-ui.d.ts +7 -1
  25. package/lib/types/adapter/ports/channel-ui.d.ts.map +1 -1
  26. package/lib/types/adapter/ports/channel-view.d.ts +47 -0
  27. package/lib/types/adapter/ports/channel-view.d.ts.map +1 -1
  28. package/lib/types/adapter/ports/channel.d.ts +2 -0
  29. package/lib/types/adapter/ports/channel.d.ts.map +1 -1
  30. package/lib/types/agent/events.d.ts +3 -2
  31. package/lib/types/agent/events.d.ts.map +1 -1
  32. package/lib/types/backends/codex/contract.d.ts +3 -0
  33. package/lib/types/backends/codex/contract.d.ts.map +1 -1
  34. package/lib/types/backends/codex/contract.js +3 -0
  35. package/lib/types/backends/codex/session/async-questions.d.ts +17 -0
  36. package/lib/types/backends/codex/session/async-questions.d.ts.map +1 -0
  37. package/lib/types/backends/codex/session/async-questions.js +89 -0
  38. package/lib/types/backends/codex/session/session.d.ts.map +1 -1
  39. package/lib/types/backends/codex/session/session.js +35 -9
  40. package/lib/types/backends/codex/text.d.ts +3 -3
  41. package/lib/types/backends/codex/text.js +1 -1
  42. package/lib/types/backends/codex/translate/items.d.ts.map +1 -1
  43. package/lib/types/backends/codex/translate/items.js +3 -1
  44. package/lib/types/backends/codex/translate/question-reply.d.ts +11 -0
  45. package/lib/types/backends/codex/translate/question-reply.d.ts.map +1 -0
  46. package/lib/types/backends/codex/translate/question-reply.js +53 -0
  47. package/lib/types/channel/projection.d.ts.map +1 -1
  48. package/lib/types/channel/projection.js +10 -0
  49. package/lib/types/commands.d.ts +10 -0
  50. package/lib/types/commands.d.ts.map +1 -1
  51. package/lib/types/commands.js +11 -2
  52. package/lib/types/components/CommandSuggestions.d.ts.map +1 -1
  53. package/lib/types/components/CommandSuggestions.js +4 -1
  54. package/lib/types/components/LogoV2.d.ts +4 -1
  55. package/lib/types/components/LogoV2.d.ts.map +1 -1
  56. package/lib/types/components/LogoV2.js +5 -3
  57. package/lib/types/components/MessageList.d.ts.map +1 -1
  58. package/lib/types/components/MessageList.js +95 -14
  59. package/lib/types/components/PermissionsPicker.d.ts +10 -1
  60. package/lib/types/components/PermissionsPicker.d.ts.map +1 -1
  61. package/lib/types/components/PermissionsPicker.js +24 -16
  62. package/lib/types/components/PluginInstallPane.d.ts +21 -0
  63. package/lib/types/components/PluginInstallPane.d.ts.map +1 -0
  64. package/lib/types/components/PluginInstallPane.js +84 -0
  65. package/lib/types/components/PluginManagePane.d.ts +21 -0
  66. package/lib/types/components/PluginManagePane.d.ts.map +1 -0
  67. package/lib/types/components/PluginManagePane.js +69 -0
  68. package/lib/types/components/PromptInput.d.ts.map +1 -1
  69. package/lib/types/components/PromptInput.js +7 -3
  70. package/lib/types/components/SubagentCard.d.ts +13 -1
  71. package/lib/types/components/SubagentCard.d.ts.map +1 -1
  72. package/lib/types/components/SubagentCard.js +27 -12
  73. package/lib/types/components/SubagentDashboard.d.ts +8 -1
  74. package/lib/types/components/SubagentDashboard.d.ts.map +1 -1
  75. package/lib/types/components/SubagentDashboard.js +58 -16
  76. package/lib/types/components/launchpadLayout.d.ts +14 -1
  77. package/lib/types/components/launchpadLayout.d.ts.map +1 -1
  78. package/lib/types/components/launchpadLayout.js +36 -17
  79. package/lib/types/components/messages/TeamMessageBubble.d.ts +20 -0
  80. package/lib/types/components/messages/TeamMessageBubble.d.ts.map +1 -0
  81. package/lib/types/components/messages/TeamMessageBubble.js +15 -0
  82. package/lib/types/components/sidePanel/PanelHost.d.ts.map +1 -1
  83. package/lib/types/components/sidePanel/PanelHost.js +5 -1
  84. package/lib/types/components/sidePanel/PanelStore.d.ts +8 -0
  85. package/lib/types/components/sidePanel/PanelStore.d.ts.map +1 -1
  86. package/lib/types/components/sidePanel/PanelStore.js +11 -0
  87. package/lib/types/components/sidePanel/SidePanelColumn.d.ts.map +1 -1
  88. package/lib/types/components/sidePanel/SidePanelColumn.js +13 -1
  89. package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts +7 -0
  90. package/lib/types/components/sidePanel/SidePanelRuntimeContext.d.ts.map +1 -1
  91. package/lib/types/components/sidePanel/SidePanelRuntimeContext.js +4 -0
  92. package/lib/types/components/sidePanel/btw/BtwComposer.d.ts +3 -0
  93. package/lib/types/components/sidePanel/btw/BtwComposer.d.ts.map +1 -1
  94. package/lib/types/components/sidePanel/btw/BtwComposer.js +20 -8
  95. package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts +2 -1
  96. package/lib/types/components/sidePanel/btw/BtwPanelAdapter.d.ts.map +1 -1
  97. package/lib/types/components/sidePanel/btw/BtwPanelAdapter.js +104 -13
  98. package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts +6 -2
  99. package/lib/types/components/sidePanel/btw/BtwThreadScene.d.ts.map +1 -1
  100. package/lib/types/components/sidePanel/btw/BtwThreadScene.js +61 -15
  101. package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts +14 -4
  102. package/lib/types/components/sidePanel/btw/BtwThreadView.d.ts.map +1 -1
  103. package/lib/types/components/sidePanel/btw/BtwThreadView.js +90 -5
  104. package/lib/types/components/sidePanel/btw/threads.d.ts +18 -0
  105. package/lib/types/components/sidePanel/btw/threads.d.ts.map +1 -1
  106. package/lib/types/components/sidePanel/btw/threads.js +51 -0
  107. package/lib/types/components/sidePanel/builtinPanels.d.ts.map +1 -1
  108. package/lib/types/components/sidePanel/builtinPanels.js +5 -1
  109. package/lib/types/components/sidePanel/companion/SplashMascot.d.ts +5 -1
  110. package/lib/types/components/sidePanel/companion/SplashMascot.d.ts.map +1 -1
  111. package/lib/types/components/sidePanel/companion/SplashMascot.js +19 -4
  112. package/lib/types/components/sidePanel/companion/skins.d.ts +8 -0
  113. package/lib/types/components/sidePanel/companion/skins.d.ts.map +1 -1
  114. package/lib/types/components/sidePanel/companion/skins.js +57 -12
  115. package/lib/types/components/sidePanel/types.d.ts +6 -0
  116. package/lib/types/components/sidePanel/types.d.ts.map +1 -1
  117. package/lib/types/components/splashArtRows.d.ts +29 -0
  118. package/lib/types/components/splashArtRows.d.ts.map +1 -0
  119. package/lib/types/components/splashArtRows.js +31 -0
  120. package/lib/types/components/teamRoster.d.ts +70 -0
  121. package/lib/types/components/teamRoster.d.ts.map +1 -0
  122. package/lib/types/components/teamRoster.js +78 -0
  123. package/lib/types/dsh-adapter/admission-loader.d.ts +82 -0
  124. package/lib/types/dsh-adapter/admission-loader.d.ts.map +1 -1
  125. package/lib/types/dsh-adapter/admission-loader.js +73 -28
  126. package/lib/types/dsh-adapter/agent-view.d.ts.map +1 -1
  127. package/lib/types/dsh-adapter/agent-view.js +3 -1
  128. package/lib/types/dsh-adapter/approvals.d.ts +7 -0
  129. package/lib/types/dsh-adapter/approvals.d.ts.map +1 -1
  130. package/lib/types/dsh-adapter/approvals.js +45 -1
  131. package/lib/types/dsh-adapter/backend/translate.d.ts.map +1 -1
  132. package/lib/types/dsh-adapter/backend/translate.js +68 -8
  133. package/lib/types/dsh-adapter/backend-assembly.d.ts +31 -0
  134. package/lib/types/dsh-adapter/backend-assembly.d.ts.map +1 -0
  135. package/lib/types/dsh-adapter/backend-assembly.js +225 -0
  136. package/lib/types/dsh-adapter/backend-contribution.d.ts +36 -0
  137. package/lib/types/dsh-adapter/backend-contribution.d.ts.map +1 -1
  138. package/lib/types/dsh-adapter/backend-contribution.js +41 -0
  139. package/lib/types/dsh-adapter/backend-registry.d.ts.map +1 -1
  140. package/lib/types/dsh-adapter/backend-registry.js +22 -3
  141. package/lib/types/dsh-adapter/backends.d.ts.map +1 -1
  142. package/lib/types/dsh-adapter/backends.js +4 -5
  143. package/lib/types/dsh-adapter/bundled-presets.d.ts.map +1 -1
  144. package/lib/types/dsh-adapter/bundled-presets.js +60 -3
  145. package/lib/types/dsh-adapter/channel/binding-events.d.ts +1 -0
  146. package/lib/types/dsh-adapter/channel/binding-events.d.ts.map +1 -1
  147. package/lib/types/dsh-adapter/channel/binding-events.js +1 -0
  148. package/lib/types/dsh-adapter/channel/command-completions.d.ts.map +1 -1
  149. package/lib/types/dsh-adapter/channel/command-completions.js +64 -7
  150. package/lib/types/dsh-adapter/channel/compaction.d.ts +7 -2
  151. package/lib/types/dsh-adapter/channel/compaction.d.ts.map +1 -1
  152. package/lib/types/dsh-adapter/channel/compaction.js +100 -26
  153. package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts +3 -0
  154. package/lib/types/dsh-adapter/channel/core/binding-feed.d.ts.map +1 -1
  155. package/lib/types/dsh-adapter/channel/core/binding-feed.js +2 -1
  156. package/lib/types/dsh-adapter/channel/core/compose.d.ts.map +1 -1
  157. package/lib/types/dsh-adapter/channel/core/compose.js +12 -0
  158. package/lib/types/dsh-adapter/channel/extensions.d.ts.map +1 -1
  159. package/lib/types/dsh-adapter/channel/extensions.js +68 -8
  160. package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts +6 -0
  161. package/lib/types/dsh-adapter/channel/mode-permission-actions.d.ts.map +1 -1
  162. package/lib/types/dsh-adapter/channel/mode-permission-actions.js +104 -12
  163. package/lib/types/dsh-adapter/channel/model-actions.d.ts.map +1 -1
  164. package/lib/types/dsh-adapter/channel/model-actions.js +8 -1
  165. package/lib/types/dsh-adapter/channel/permissions.d.ts.map +1 -1
  166. package/lib/types/dsh-adapter/channel/permissions.js +4 -1
  167. package/lib/types/dsh-adapter/channel/projection-helpers.d.ts.map +1 -1
  168. package/lib/types/dsh-adapter/channel/projection-helpers.js +2 -0
  169. package/lib/types/dsh-adapter/channel/session-metadata.d.ts.map +1 -1
  170. package/lib/types/dsh-adapter/channel/session-metadata.js +4 -3
  171. package/lib/types/dsh-adapter/channel/session-reset.js +2 -2
  172. package/lib/types/dsh-adapter/channel/session-resume.d.ts.map +1 -1
  173. package/lib/types/dsh-adapter/channel/session-resume.js +7 -0
  174. package/lib/types/dsh-adapter/channel/settings-host.d.ts +3 -0
  175. package/lib/types/dsh-adapter/channel/settings-host.d.ts.map +1 -1
  176. package/lib/types/dsh-adapter/channel/settings-host.js +135 -5
  177. package/lib/types/dsh-adapter/channel/state.d.ts +6 -0
  178. package/lib/types/dsh-adapter/channel/state.d.ts.map +1 -1
  179. package/lib/types/dsh-adapter/channel/subagent-projection.d.ts.map +1 -1
  180. package/lib/types/dsh-adapter/channel/subagent-projection.js +18 -6
  181. package/lib/types/dsh-adapter/channel/transcript.d.ts +7 -0
  182. package/lib/types/dsh-adapter/channel/transcript.d.ts.map +1 -1
  183. package/lib/types/dsh-adapter/channel/transcript.js +19 -2
  184. package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts +55 -0
  185. package/lib/types/dsh-adapter/compat/agent-team-0.2.0.d.ts.map +1 -0
  186. package/lib/types/dsh-adapter/compat/agent-team-0.2.0.js +96 -0
  187. package/lib/types/dsh-adapter/contract.d.ts +4 -4
  188. package/lib/types/dsh-adapter/contract.d.ts.map +1 -1
  189. package/lib/types/dsh-adapter/contract.js +5 -5
  190. package/lib/types/dsh-adapter/effect-ledger.d.ts +7 -0
  191. package/lib/types/dsh-adapter/effect-ledger.d.ts.map +1 -1
  192. package/lib/types/dsh-adapter/effect-ledger.js +8 -1
  193. package/lib/types/dsh-adapter/entry-slot.d.ts +3 -1
  194. package/lib/types/dsh-adapter/entry-slot.d.ts.map +1 -1
  195. package/lib/types/dsh-adapter/entry-slot.js +2 -2
  196. package/lib/types/dsh-adapter/fresh-agent.d.ts.map +1 -1
  197. package/lib/types/dsh-adapter/fresh-agent.js +6 -1
  198. package/lib/types/dsh-adapter/host-access.d.ts.map +1 -1
  199. package/lib/types/dsh-adapter/host-access.js +95 -0
  200. package/lib/types/dsh-adapter/host-contract.d.ts +1 -1
  201. package/lib/types/dsh-adapter/host-contract.d.ts.map +1 -1
  202. package/lib/types/dsh-adapter/host-contract.js +3 -2
  203. package/lib/types/dsh-adapter/host-dsh.d.ts +8 -2
  204. package/lib/types/dsh-adapter/host-dsh.d.ts.map +1 -1
  205. package/lib/types/dsh-adapter/host-dsh.js +45 -30
  206. package/lib/types/dsh-adapter/host-entry.js +35 -10
  207. package/lib/types/dsh-adapter/index.d.ts +9 -10
  208. package/lib/types/dsh-adapter/index.d.ts.map +1 -1
  209. package/lib/types/dsh-adapter/index.js +8 -6
  210. package/lib/types/dsh-adapter/invariant.d.ts +7 -2
  211. package/lib/types/dsh-adapter/invariant.d.ts.map +1 -1
  212. package/lib/types/dsh-adapter/invariant.js +6 -3
  213. package/lib/types/dsh-adapter/migrate/index.js +2 -2
  214. package/lib/types/dsh-adapter/panels.d.ts.map +1 -1
  215. package/lib/types/dsh-adapter/panels.js +6 -0
  216. package/lib/types/dsh-adapter/plugin-declaration.d.ts +99 -1
  217. package/lib/types/dsh-adapter/plugin-declaration.d.ts.map +1 -1
  218. package/lib/types/dsh-adapter/plugin-declaration.js +132 -2
  219. package/lib/types/dsh-adapter/plugin-feed.d.ts +94 -0
  220. package/lib/types/dsh-adapter/plugin-feed.d.ts.map +1 -0
  221. package/lib/types/dsh-adapter/plugin-feed.js +135 -0
  222. package/lib/types/dsh-adapter/plugin-guard.d.ts +115 -0
  223. package/lib/types/dsh-adapter/plugin-guard.d.ts.map +1 -0
  224. package/lib/types/dsh-adapter/plugin-guard.js +108 -0
  225. package/lib/types/dsh-adapter/plugin-install.d.ts +160 -0
  226. package/lib/types/dsh-adapter/plugin-install.d.ts.map +1 -0
  227. package/lib/types/dsh-adapter/plugin-install.js +545 -0
  228. package/lib/types/dsh-adapter/plugin-manage.d.ts +178 -0
  229. package/lib/types/dsh-adapter/plugin-manage.d.ts.map +1 -0
  230. package/lib/types/dsh-adapter/plugin-manage.js +468 -0
  231. package/lib/types/dsh-adapter/plugin-manager.d.ts +78 -2
  232. package/lib/types/dsh-adapter/plugin-manager.d.ts.map +1 -1
  233. package/lib/types/dsh-adapter/plugin-manager.js +32 -0
  234. package/lib/types/dsh-adapter/plugin.d.ts.map +1 -1
  235. package/lib/types/dsh-adapter/plugin.js +49 -2
  236. package/lib/types/dsh-adapter/plugins-info.d.ts +87 -1
  237. package/lib/types/dsh-adapter/plugins-info.d.ts.map +1 -1
  238. package/lib/types/dsh-adapter/plugins-info.js +131 -26
  239. package/lib/types/dsh-adapter/process-exit.d.ts +1 -1
  240. package/lib/types/dsh-adapter/process-exit.d.ts.map +1 -1
  241. package/lib/types/dsh-adapter/process-exit.js +1 -1
  242. package/lib/types/dsh-adapter/session-projection-store.d.ts +66 -0
  243. package/lib/types/dsh-adapter/session-projection-store.d.ts.map +1 -0
  244. package/lib/types/dsh-adapter/session-projection-store.js +149 -0
  245. package/lib/types/dsh-adapter/settingsEditor.js +2 -2
  246. package/lib/types/dsh-adapter/sideQuestion.d.ts +21 -0
  247. package/lib/types/dsh-adapter/sideQuestion.d.ts.map +1 -1
  248. package/lib/types/dsh-adapter/sideQuestion.js +53 -0
  249. package/lib/types/dsh-adapter/subagents.d.ts.map +1 -1
  250. package/lib/types/dsh-adapter/subagents.js +2 -1
  251. package/lib/types/dsh-adapter/team-mode.d.ts +74 -0
  252. package/lib/types/dsh-adapter/team-mode.d.ts.map +1 -0
  253. package/lib/types/dsh-adapter/team-mode.js +188 -0
  254. package/lib/types/dsh-adapter/team-roster.d.ts +67 -0
  255. package/lib/types/dsh-adapter/team-roster.d.ts.map +1 -0
  256. package/lib/types/dsh-adapter/team-roster.js +136 -0
  257. package/lib/types/dsh-adapter/team-tools.d.ts +58 -0
  258. package/lib/types/dsh-adapter/team-tools.d.ts.map +1 -0
  259. package/lib/types/dsh-adapter/team-tools.js +208 -0
  260. package/lib/types/dsh-adapter/types.d.ts +1 -1
  261. package/lib/types/dsh-adapter/types.d.ts.map +1 -1
  262. package/lib/types/i18n.d.ts +607 -16
  263. package/lib/types/i18n.d.ts.map +1 -1
  264. package/lib/types/i18n.js +295 -9
  265. package/lib/types/ink/components/ScrollBox.d.ts +9 -0
  266. package/lib/types/ink/components/ScrollBox.d.ts.map +1 -1
  267. package/lib/types/ink/components/ScrollBox.js +23 -0
  268. package/lib/types/ink/dom.d.ts.map +1 -1
  269. package/lib/types/kernelPrefs.d.ts +1 -1
  270. package/lib/types/kernelPrefs.d.ts.map +1 -1
  271. package/lib/types/native-ts/yoga-layout/index.d.ts.map +1 -1
  272. package/lib/types/native-ts/yoga-layout/index.js +8 -1
  273. package/lib/types/permissionPresetLabels.d.ts +25 -0
  274. package/lib/types/permissionPresetLabels.d.ts.map +1 -0
  275. package/lib/types/permissionPresetLabels.js +33 -0
  276. package/lib/types/preset-patch.d.ts +22 -0
  277. package/lib/types/preset-patch.d.ts.map +1 -0
  278. package/lib/types/preset-patch.js +14 -0
  279. package/lib/types/screens/Chat.d.ts +33 -1
  280. package/lib/types/screens/Chat.d.ts.map +1 -1
  281. package/lib/types/screens/Chat.js +301 -9
  282. package/lib/types/screens/Launchpad.d.ts +7 -1
  283. package/lib/types/screens/Launchpad.d.ts.map +1 -1
  284. package/lib/types/screens/Launchpad.js +19 -4
  285. package/lib/types/screens/Settings.d.ts.map +1 -1
  286. package/lib/types/screens/Settings.js +1 -0
  287. package/lib/types/screens/chat/usePluginWriteFlow.d.ts +68 -0
  288. package/lib/types/screens/chat/usePluginWriteFlow.d.ts.map +1 -0
  289. package/lib/types/screens/chat/usePluginWriteFlow.js +92 -0
  290. package/lib/types/screens/chatOverlay.d.ts +19 -1
  291. package/lib/types/screens/chatOverlay.d.ts.map +1 -1
  292. package/lib/types/sessionModes.d.ts +5 -3
  293. package/lib/types/sessionModes.d.ts.map +1 -1
  294. package/lib/types/sessionModes.js +9 -3
  295. package/lib/types/tips.js +2 -2
  296. package/lib/types/utils/paths.d.ts +11 -0
  297. package/lib/types/utils/paths.d.ts.map +1 -1
  298. package/lib/types/utils/paths.js +13 -0
  299. package/package.json +126 -120
  300. package/tui-profile/feed/README.md +63 -0
  301. package/tui-profile/feed/first-party.json +10 -0
@@ -39,10 +39,10 @@ import { t } from '../i18n.js';
39
39
  import { loadSpecData } from '../adapter/standard/registry.js';
40
40
  import { createContractIndex, validatePlugin, negotiate } from '../adapter/standard/admission.js';
41
41
  import { cleanScalarText } from './sanitize.js';
42
- import { isCompositionOwned } from './plugin-declaration.js';
42
+ import { isCompositionOwned, pluginDeclarationIn } from './plugin-declaration.js';
43
43
  import { extraBundles, localizedPluginText } from './plugin-manager.js';
44
44
  import { PLUGIN_STORAGE_DIR } from './plugin-storage.js';
45
- import { EFFECT_LEDGER_FILE } from './effect-ledger.js';
45
+ import { EFFECT_LEDGER_NAME } from './effect-ledger.js';
46
46
  /** How many plugins the matrix shows before an overflow note. */
47
47
  export const PLUGINS_MATRIX_MAX_ROWS = 20;
48
48
  /** How many ledger records the tail section shows. */
@@ -100,9 +100,15 @@ function redactText(value) {
100
100
  return redacted === match ? match.replace(USERINFO_AT_START, '$1') : redacted;
101
101
  });
102
102
  }
103
- /** One report cell: credentials redacted first, then sanitized, then capped —
104
- * truncation comes last, or a cut URL keeps the head of its own secret. */
105
- const cell = (value) => cleanScalarText(redactText(String(value ?? '')), LINE_CELLS);
103
+ /**
104
+ * One report cell: credentials redacted first, then sanitized, then capped —
105
+ * truncation comes last, or a cut URL keeps the head of its own secret.
106
+ *
107
+ * Exported for the install flow (C-4): pnpm's output and the service's error
108
+ * text are foreign input reaching the same transcript, and one renderer means
109
+ * the two surfaces cannot disagree about what a cell is.
110
+ */
111
+ export const cell = (value) => cleanScalarText(redactText(String(value ?? '')), LINE_CELLS);
106
112
  /** One URL field as a report cell: credentials stripped, then capped. */
107
113
  const urlCell = (value) => cell(redactUrl(value));
108
114
  /** Parse the ledger file tolerantly (skip corrupt lines, never throw). */
@@ -132,10 +138,10 @@ function readLedgerRecords(file) {
132
138
  /** The plugin set for the matrix: grants-file keys ∪ ledger pluginIds ∪
133
139
  * storage namespaces ('host'/'undeclared' ledger identities are not
134
140
  * plugins and are excluded). */
135
- function footprintPlugins(deps, dataDir, ledgerFile, storageDir) {
141
+ export function pluginFootprints(paths) {
136
142
  const found = new Set();
137
143
  try {
138
- const raw = JSON.parse(readFileSync(join(dataDir, 'extension-grants.json'), 'utf8'));
144
+ const raw = JSON.parse(readFileSync(join(paths.dataDir, 'extension-grants.json'), 'utf8'));
139
145
  if (raw !== null && typeof raw === 'object') {
140
146
  const table = raw;
141
147
  for (const section of [table.grants, table.denies]) {
@@ -150,13 +156,13 @@ function footprintPlugins(deps, dataDir, ledgerFile, storageDir) {
150
156
  // Missing/corrupt grants file: the GrantStore already answers deny;
151
157
  // the matrix simply has no rows from this source.
152
158
  }
153
- for (const record of readLedgerRecords(ledgerFile)) {
159
+ for (const record of readLedgerRecords(paths.ledgerFile)) {
154
160
  if (typeof record.pluginId === 'string' && record.pluginId !== '' && record.pluginId !== 'host' && record.pluginId !== 'undeclared') {
155
161
  found.add(record.pluginId);
156
162
  }
157
163
  }
158
164
  try {
159
- for (const entry of readdirSync(storageDir)) {
165
+ for (const entry of readdirSync(paths.storageDir)) {
160
166
  if (!entry.endsWith('.json'))
161
167
  continue;
162
168
  const namespace = decodeURIComponent(entry.slice(0, -'.json'.length));
@@ -167,9 +173,29 @@ function footprintPlugins(deps, dataDir, ledgerFile, storageDir) {
167
173
  catch {
168
174
  // No storage directory yet: no rows from this source.
169
175
  }
170
- void deps;
171
176
  return [...found].sort();
172
177
  }
178
+ /**
179
+ * The three footprint sources, resolved the way the diagnostics resolve them
180
+ * (test overrides first, else the packaged locations under `~/.dsh-tui`).
181
+ */
182
+ export function pluginFootprintPaths(overrides = {}) {
183
+ const dataDir = overrides.dataDir ?? DATA_DIR;
184
+ return {
185
+ dataDir,
186
+ ledgerFile: overrides.ledgerFile ?? join(dataDir, EFFECT_LEDGER_NAME),
187
+ storageDir: overrides.storageDir ?? join(dataDir, PLUGIN_STORAGE_DIR),
188
+ };
189
+ }
190
+ /**
191
+ * Whether one plugin id still has a footprint in this profile: a grants-file
192
+ * key, an effect-ledger record, or a storage namespace. The same three sources
193
+ * the matrix unions — a removal that left something behind is read here rather
194
+ * than by a second reader that could disagree with the report (C-5).
195
+ */
196
+ export function pluginFootprintContains(pluginId, overrides = {}) {
197
+ return pluginFootprints(pluginFootprintPaths(overrides)).includes(pluginId);
198
+ }
173
199
  /** `/plugin check <path>`: official parse/projection + profile validation + negotiation. */
174
200
  function checkManifestLines(pathArg, deps) {
175
201
  const lines = [];
@@ -283,6 +309,14 @@ export function parsePluginArgs(args) {
283
309
  return { kind: 'info', spec: rest.join(' ') };
284
310
  if (sub === 'registries')
285
311
  return { kind: 'registries' };
312
+ if (sub === 'install')
313
+ return { kind: 'install', spec: rest.join(' ') };
314
+ if (sub === 'remove')
315
+ return { kind: 'remove', target: rest.join(' ') };
316
+ if (sub === 'enable')
317
+ return { kind: 'enable', target: rest.join(' ') };
318
+ if (sub === 'disable')
319
+ return { kind: 'disable', target: rest.join(' ') };
286
320
  return { kind: 'unknown', sub: sub ?? '' };
287
321
  }
288
322
  /** The `/plugin` overview lines (banner always first). */
@@ -314,9 +348,20 @@ export function pluginsInfoLines(args, deps) {
314
348
  }
315
349
  if (parsed.kind === 'registries')
316
350
  return lines;
317
- const dataDir = deps.dataDir ?? DATA_DIR;
318
- const ledgerFile = deps.ledgerFile ?? EFFECT_LEDGER_FILE;
319
- const storageDir = deps.storageDir ?? join(dataDir, PLUGIN_STORAGE_DIR);
351
+ // `install <spec>` is the write path (C-4): a bare one is a usage error, and
352
+ // the UI drives the rest — this half stays the diagnostics it always was.
353
+ if (parsed.kind === 'install') {
354
+ if (parsed.spec === '')
355
+ lines.push(t('plugins-install-usage'));
356
+ return lines;
357
+ }
358
+ // The three lifecycle forms (C-5) report themselves as they run, like an
359
+ // install: a bare one is a usage error, and this half stays the diagnostics.
360
+ if (parsed.kind === 'remove' || parsed.kind === 'enable' || parsed.kind === 'disable') {
361
+ if (parsed.target === '')
362
+ lines.push(t('plugins-manage-usage', { sub: parsed.kind }));
363
+ return lines;
364
+ }
320
365
  // ── Host Descriptor ──
321
366
  if (deps.host === undefined) {
322
367
  lines.push(t('plugins-host-unavailable'));
@@ -336,7 +381,7 @@ export function pluginsInfoLines(args, deps) {
336
381
  }
337
382
  }
338
383
  // ── 授权矩阵 ──
339
- const plugins = footprintPlugins(deps, dataDir, ledgerFile, storageDir);
384
+ const plugins = pluginFootprints(pluginFootprintPaths(deps));
340
385
  lines.push(t('plugins-matrix-note'));
341
386
  const permissions = deps.grants.knownPermissions();
342
387
  if (permissions.length === 0) {
@@ -368,6 +413,7 @@ export function pluginsInfoLines(args, deps) {
368
413
  lines.push(` ${t('plugins-grant-hint')}`);
369
414
  }
370
415
  // ── 台账尾 ──
416
+ const ledgerFile = pluginFootprintPaths(deps).ledgerFile;
371
417
  const records = readLedgerRecords(ledgerFile);
372
418
  if (records.length === 0) {
373
419
  lines.push(t('plugins-ledger-empty'));
@@ -382,38 +428,89 @@ export function pluginsInfoLines(args, deps) {
382
428
  }
383
429
  return lines;
384
430
  }
385
- /** Evaluate a package's declaration exactly as `check <path>` does. */
386
- function declarationVerdict(deps, packageName) {
387
- const declaration = deps.declaration?.(packageName);
388
- if (declaration === undefined)
389
- return { kind: 'undeclared' };
431
+ /**
432
+ * A declaration this profile can use: parsed, projected and validated against
433
+ * the admission catalog — the half `check <path>` and the list tags share. One
434
+ * implementation, so every surface that judges a declaration is judging the
435
+ * same thing; the caller decides what to do with the answer (the tag negotiates
436
+ * it further, the pre-install notice does not).
437
+ */
438
+ function admitDeclaration(declaration) {
390
439
  const data = loadSpecData();
391
440
  if (data === undefined)
392
- return { kind: 'invalid', error: t('plugins-check-spec-unavailable') };
441
+ return { error: t('plugins-check-spec-unavailable') };
393
442
  let manifest;
394
443
  try {
395
444
  manifest = parseManifest(declaration.source, { source: declaration.path });
396
445
  projectManifest(manifest);
397
446
  }
398
447
  catch (error) {
399
- return { kind: 'invalid', error: cell(error instanceof Error ? error.message : String(error)) };
448
+ return { error: cell(error instanceof Error ? error.message : String(error)) };
400
449
  }
401
450
  const index = createContractIndex(data.registry, data.permissions);
402
451
  try {
403
452
  validatePlugin(index, manifest);
404
453
  }
405
454
  catch (error) {
406
- return { kind: 'invalid', error: cell(error instanceof Error ? error.message : String(error)) };
455
+ return { error: cell(error instanceof Error ? error.message : String(error)) };
407
456
  }
457
+ return { manifest, index };
458
+ }
459
+ /** Evaluate one declaration exactly as `check <path>` does. */
460
+ function verdictOfDeclaration(deps, declaration) {
461
+ const admitted = admitDeclaration(declaration);
462
+ if ('error' in admitted)
463
+ return { kind: 'invalid', error: admitted.error };
408
464
  const host = deps.host;
409
465
  if (host === undefined)
410
466
  return { kind: 'invalid', error: t('plugins-check-host-unavailable') };
411
- const granted = manifest.permissions.map(request => ({
467
+ const granted = admitted.manifest.permissions.map(request => ({
412
468
  name: request.name,
413
469
  scope: request.scope,
414
- granted: deps.grants.allows({ componentId: manifest.id }, request.name, request.scope),
470
+ granted: deps.grants.allows({ componentId: admitted.manifest.id }, request.name, request.scope),
415
471
  }));
416
- return { kind: 'decided', decision: negotiate(index, manifest, host.descriptor, granted) };
472
+ return { kind: 'decided', decision: negotiate(admitted.index, admitted.manifest, host.descriptor, granted) };
473
+ }
474
+ /** Evaluate a package's declaration exactly as `check <path>` does. */
475
+ function declarationVerdict(deps, packageName) {
476
+ const declaration = deps.declaration?.(packageName);
477
+ if (declaration === undefined)
478
+ return { kind: 'undeclared' };
479
+ return verdictOfDeclaration(deps, declaration);
480
+ }
481
+ /**
482
+ * Gate 1's pre-install statement about an install target (C-4), as data.
483
+ *
484
+ * It runs the same parse/project/validate pipeline `check <path>` and the list
485
+ * tags run — one implementation, so the surfaces cannot disagree — over the
486
+ * declaration of the target directory the caller resolved. It deliberately stops
487
+ * before `negotiate`: admission's five states are a statement about a *running*
488
+ * host, and the standing tag in `/plugin` is where they are reported after the
489
+ * install. What a person needs before installing is narrower and decidable: is
490
+ * there a declaration at all, and does this profile's catalog accept it.
491
+ *
492
+ * The evidence is the target directory and nothing else. A declaration resolved
493
+ * by package name would be the profile's already-installed copy of that name —
494
+ * a different artifact from the one being installed (another version, or a local
495
+ * directory), which is how a stale same-name package used to answer for a new
496
+ * target.
497
+ *
498
+ * The two empty answers are not the same thing, and that is the point:
499
+ *
500
+ * - a directory the caller resolved (`install <path>` specs) either carries a
501
+ * package-root `dsh-plugin.json` or does not, so no file is `undeclared`;
502
+ * - a target with no directory to read (a registry name, a tarball, a git spec)
503
+ * ships no file this machine can read, so an empty answer there is `unjudged`
504
+ * — the flow cannot honestly claim a package it has never seen fails gate 1.
505
+ */
506
+ export function declarationNoticeFor(target) {
507
+ if (target.directory === undefined)
508
+ return { kind: 'unjudged' };
509
+ const declaration = pluginDeclarationIn(target.directory);
510
+ if (declaration === undefined)
511
+ return { kind: 'undeclared' };
512
+ const admitted = admitDeclaration(declaration);
513
+ return 'error' in admitted ? { kind: 'invalid', error: admitted.error } : { kind: 'declared' };
417
514
  }
418
515
  /**
419
516
  * The gate-1 tag for one listed package, or `undefined` when it needs none.
@@ -586,8 +683,16 @@ export async function pluginServiceLines(args, deps, manager) {
586
683
  return [];
587
684
  if (parsed.kind === 'info' && parsed.spec === '')
588
685
  return [];
686
+ // With no service the report is the diagnostics plus the reason and the CLI
687
+ // route, whatever the form asked for — the write path included: the flow that
688
+ // would run it lives in the entry, and without a service the entry hands back
689
+ // no flow, so this is the sentence the person reads.
589
690
  if (manager === undefined)
590
- return [t('plugins-service-unavailable')];
691
+ return [t('plugins-service-unavailable'), t('plugins-service-unavailable-route')];
692
+ // The write paths report themselves line by line as they run (C-4 / C-5): with
693
+ // a service present, this half has nothing to add to them.
694
+ if (parsed.kind === 'install' || parsed.kind === 'remove' || parsed.kind === 'enable' || parsed.kind === 'disable')
695
+ return [];
591
696
  // The service belongs to the host: a refusal or a crash there must not take
592
697
  // the diagnostics half down with it. Every call funnels into one line, so a
593
698
  // rejected promise still leaves a readable report instead of an unhandled
@@ -23,7 +23,7 @@ export type ExitRequestAnswer = 'exiting' | 'pending' | 'supervising' | 'refused
23
23
  export interface ProcessExitSeam {
24
24
  request?: (request: ExitRequest) => ExitRequestAnswer;
25
25
  }
26
- /** dsh `profile-boot` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.0-rc.2): the dispose bound. */
26
+ /** dsh `process-shutdown` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.1-rc.2): the dispose bound. */
27
27
  export declare const PROCESS_SHUTDOWN_TIMEOUT_MS = 5000;
28
28
  /**
29
29
  * End the process by `signal`. Only the entry's own listeners come off, so the
@@ -1 +1 @@
1
- {"version":3,"file":"process-exit.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/process-exit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC/D,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAAoC,CAAA;AAEhG,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE;AACjE,yEAAyE;GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD;;+CAE+C;AAC/C,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;AAEjF,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAA;CACtD;AAED,sFAAsF;AACtF,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAQ/C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAQ3D;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACvE;AAED,wBAAwB;AACxB,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA4BrE"}
1
+ {"version":3,"file":"process-exit.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/process-exit.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAC/D,eAAO,MAAM,mBAAmB,EAAE,SAAS,iBAAiB,EAAoC,CAAA;AAEhG,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,CAAA;CAAE;AACjE,yEAAyE;GACvE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD;;+CAE+C;AAC/C,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,GAAG,SAAS,CAAA;AAEjF,MAAM,WAAW,eAAe;IAC9B,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,WAAW,KAAK,iBAAiB,CAAA;CACtD;AAED,0FAA0F;AAC1F,eAAO,MAAM,2BAA2B,OAAO,CAAA;AAQ/C;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI,CAQ3D;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAA;IAC9B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAA;IAC5C,sEAAsE;IACtE,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACvE;AAED,wBAAwB;AACxB,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,kBAAkB,GAAG,IAAI,CA4BrE"}
@@ -6,7 +6,7 @@
6
6
  * tmux and service managers); a second signal forces the exit at once.
7
7
  */
8
8
  export const TERMINATION_SIGNALS = ['SIGTERM', 'SIGHUP', 'SIGINT'];
9
- /** dsh `profile-boot` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.0-rc.2): the dispose bound. */
9
+ /** dsh `process-shutdown` PROCESS_SHUTDOWN_TIMEOUT_MS (0.2.1-rc.2): the dispose bound. */
10
10
  export const PROCESS_SHUTDOWN_TIMEOUT_MS = 5000;
11
11
  /** Last-resort bound for a funnel exit: outlasts its terminal cleanup plus the dispose bound. */
12
12
  const BACKSTOP_MS = PROCESS_SHUTDOWN_TIMEOUT_MS + 2000;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * One host session-projection key, folded for the session on screen.
3
+ *
4
+ * The host projection feed is process-wide and carries whole values, so a
5
+ * read side needs the same three pieces every time: drop other keys, narrow
6
+ * the wire value with a local guard (the producer package is never imported),
7
+ * and read one baseline snapshot when a session binds — a value is only
8
+ * published on change, so a resumed session would otherwise wait for its next
9
+ * event. The store keeps only the watched session's value: a background
10
+ * session must not overwrite what the screen shows.
11
+ *
12
+ * @module
13
+ */
14
+ import type { Context } from '@deepseek-ai/cordis';
15
+ import type { ProjectionRegistryLike } from './activity-store.js';
16
+ /** The session whose projection value the session on screen shows. */
17
+ export interface WatchedSession {
18
+ readonly id: string;
19
+ /** The live session object, when there is one to read a baseline from. */
20
+ readonly session?: unknown;
21
+ }
22
+ export interface SessionProjectionStoreOptions<T> {
23
+ /** Projection key the value is published under. */
24
+ readonly key: string;
25
+ /** Narrow one wire value of a session; anything else is dropped, never half-rendered. */
26
+ narrow(value: unknown, sessionId: string): T | undefined;
27
+ /** Map the bound session to the session it reads (default: itself). */
28
+ watch?(session: unknown): WatchedSession;
29
+ /** Read-failure sink, reported once per attached registry (the composition root's logger). */
30
+ warn?(message: string): void;
31
+ }
32
+ export interface SessionProjectionStore<T> {
33
+ /** Remember the host registry; a late registry seeds the bound session. */
34
+ attachRegistry(registry: ProjectionRegistryLike): void;
35
+ /** Forget a registry that left; nothing is read from it again and the value clears. */
36
+ detachRegistry(registry: ProjectionRegistryLike): void;
37
+ /** Bind the session on screen and read its watched value once. */
38
+ seed(session: unknown): void;
39
+ /** Record one value from the change feed. */
40
+ update(sessionId: string, value: unknown): void;
41
+ /**
42
+ * Read the bound session's baseline again. For a change the feed does not
43
+ * carry: a projection definition that left (its key reads as absent) or
44
+ * came back.
45
+ */
46
+ reseed(): void;
47
+ /** The bound session's id, once a session bound. */
48
+ readonly boundId: string | undefined;
49
+ /** The watched session's id, once a session bound. */
50
+ readonly watchedId: string | undefined;
51
+ /** The watched session's current value, stable between updates. */
52
+ read(): T | undefined;
53
+ subscribe(listener: () => void): () => void;
54
+ noteReadFailure(error: unknown): void;
55
+ }
56
+ export declare function createSessionProjectionStore<T>(options: SessionProjectionStoreOptions<T>): SessionProjectionStore<T>;
57
+ /**
58
+ * Wire one store to the host's projection registry once it exists. The
59
+ * registry and the unit that publishes the key are mounted by other plugins,
60
+ * so both may arrive later — or never, which leaves the store empty.
61
+ * @param ctx - Host context of the composition root.
62
+ * @param key - Projection key the store folds.
63
+ * @param store - Store to fill.
64
+ */
65
+ export declare function attachSessionProjection(ctx: Context, key: string, store: SessionProjectionStore<unknown>): void;
66
+ //# sourceMappingURL=session-projection-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-projection-store.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/session-projection-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAClD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAA;AAEjE,sEAAsE;AACtE,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,0EAA0E;IAC1E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,WAAW,6BAA6B,CAAC,CAAC;IAC9C,mDAAmD;IACnD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,yFAAyF;IACzF,MAAM,CAAC,KAAK,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,CAAA;IACxD,uEAAuE;IACvE,KAAK,CAAC,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAAA;IACxC,8FAA8F;IAC9F,IAAI,CAAC,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7B;AAED,MAAM,WAAW,sBAAsB,CAAC,CAAC;IACvC,2EAA2E;IAC3E,cAAc,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAA;IACtD,uFAAuF;IACvF,cAAc,CAAC,QAAQ,EAAE,sBAAsB,GAAG,IAAI,CAAA;IACtD,kEAAkE;IAClE,IAAI,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAA;IAC5B,6CAA6C;IAC7C,MAAM,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;IAC/C;;;;OAIG;IACH,MAAM,IAAI,IAAI,CAAA;IACd,oDAAoD;IACpD,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,sDAAsD;IACtD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAAA;IACtC,mEAAmE;IACnE,IAAI,IAAI,CAAC,GAAG,SAAS,CAAA;IACrB,SAAS,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAAA;IAC3C,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI,CAAA;CACtC;AASD,wBAAgB,4BAA4B,CAAC,CAAC,EAAE,OAAO,EAAE,6BAA6B,CAAC,CAAC,CAAC,GAAG,sBAAsB,CAAC,CAAC,CAAC,CAsFpH;AAED;;;;;;;GAOG;AACH,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,sBAAsB,CAAC,OAAO,CAAC,GAAG,IAAI,CAmB/G"}
@@ -0,0 +1,149 @@
1
+ /**
2
+ * One host session-projection key, folded for the session on screen.
3
+ *
4
+ * The host projection feed is process-wide and carries whole values, so a
5
+ * read side needs the same three pieces every time: drop other keys, narrow
6
+ * the wire value with a local guard (the producer package is never imported),
7
+ * and read one baseline snapshot when a session binds — a value is only
8
+ * published on change, so a resumed session would otherwise wait for its next
9
+ * event. The store keeps only the watched session's value: a background
10
+ * session must not overwrite what the screen shows.
11
+ *
12
+ * @module
13
+ */
14
+ /** A raw value no snapshot can return: the next update always narrows. */
15
+ const UNREAD = Symbol('unread');
16
+ function sessionIdOf(session) {
17
+ return String(session.id);
18
+ }
19
+ export function createSessionProjectionStore(options) {
20
+ const listeners = new Set();
21
+ let registry;
22
+ let bound;
23
+ let watched;
24
+ let raw;
25
+ let value;
26
+ let warned = false;
27
+ const emit = () => {
28
+ for (const listener of [...listeners])
29
+ listener();
30
+ };
31
+ const store = {
32
+ attachRegistry(next) {
33
+ registry = next;
34
+ warned = false;
35
+ if (bound !== undefined)
36
+ store.seed(bound);
37
+ },
38
+ detachRegistry(left) {
39
+ if (registry !== left)
40
+ return;
41
+ registry = undefined;
42
+ raw = undefined;
43
+ if (value === undefined)
44
+ return;
45
+ value = undefined;
46
+ emit();
47
+ },
48
+ seed(session) {
49
+ if (session === null || session === undefined)
50
+ return;
51
+ bound = session;
52
+ const next = options.watch?.(session) ?? { id: sessionIdOf(session), session };
53
+ if (next.id !== watched?.id) {
54
+ raw = undefined;
55
+ if (value !== undefined) {
56
+ value = undefined;
57
+ emit();
58
+ }
59
+ }
60
+ watched = next;
61
+ if (registry === undefined || next.session === undefined)
62
+ return;
63
+ try {
64
+ store.update(next.id, registry.snapshot(next.session, [options.key]).values[options.key]);
65
+ }
66
+ catch (error) {
67
+ store.noteReadFailure(error);
68
+ }
69
+ },
70
+ update(sessionId, next) {
71
+ if (sessionId !== watched?.id)
72
+ return;
73
+ // The host reuses the value reference while its state is unchanged, so
74
+ // identity is the whole change test.
75
+ if (next === raw)
76
+ return;
77
+ raw = next;
78
+ const narrowed = options.narrow(next, sessionId);
79
+ if (narrowed === undefined && value === undefined)
80
+ return;
81
+ value = narrowed;
82
+ emit();
83
+ },
84
+ reseed() {
85
+ if (bound === undefined)
86
+ return;
87
+ // The narrow may read host state the value reference does not track
88
+ // (the team's service presence), so an unchanged value, an absent one
89
+ // included, is re-narrowed.
90
+ const held = raw;
91
+ raw = UNREAD;
92
+ store.seed(bound);
93
+ // No snapshot was read (no registry, the watched session not live, or a
94
+ // failed read): narrow the value already held instead.
95
+ if (raw === UNREAD && held !== UNREAD && watched !== undefined)
96
+ store.update(watched.id, held);
97
+ },
98
+ get boundId() {
99
+ return bound === undefined ? undefined : sessionIdOf(bound);
100
+ },
101
+ get watchedId() {
102
+ return watched?.id;
103
+ },
104
+ read: () => value,
105
+ subscribe(listener) {
106
+ listeners.add(listener);
107
+ return () => { listeners.delete(listener); };
108
+ },
109
+ noteReadFailure(error) {
110
+ if (warned)
111
+ return;
112
+ warned = true;
113
+ const detail = error instanceof Error ? error.message : String(error);
114
+ options.warn?.(`dsh-tui: ${options.key} projection read failed: ${detail}`);
115
+ },
116
+ };
117
+ return store;
118
+ }
119
+ /**
120
+ * Wire one store to the host's projection registry once it exists. The
121
+ * registry and the unit that publishes the key are mounted by other plugins,
122
+ * so both may arrive later — or never, which leaves the store empty.
123
+ * @param ctx - Host context of the composition root.
124
+ * @param key - Projection key the store folds.
125
+ * @param store - Store to fill.
126
+ */
127
+ export function attachSessionProjection(ctx, key, store) {
128
+ ctx.inject(['sessionProjections'], ((projectionCtx) => {
129
+ const registry = projectionCtx.sessionProjections;
130
+ if (registry === undefined)
131
+ return;
132
+ store.attachRegistry(registry);
133
+ let offFeed;
134
+ try {
135
+ offFeed = registry.onChanged((session, changed, value) => {
136
+ if (changed === key)
137
+ store.update(sessionIdOf(session), value);
138
+ });
139
+ }
140
+ catch (error) {
141
+ store.noteReadFailure(error);
142
+ }
143
+ // On the injected fiber: a re-provided registry re-runs this callback.
144
+ projectionCtx.effect(() => () => {
145
+ offFeed?.();
146
+ store.detachRegistry(registry);
147
+ }, `dsh-tui ${key} projection feed`);
148
+ }));
149
+ }
@@ -183,7 +183,7 @@ export class SettingsForm {
183
183
  if (field.secret !== undefined) {
184
184
  // Blank drafts write nothing; typed drafts replace the credential.
185
185
  if (staged.text !== '')
186
- secrets.push({ ref: field.secret.ref, text: staged.text });
186
+ secrets.push({ ref: field.secret.ref, text: staged.text, field });
187
187
  continue;
188
188
  }
189
189
  const write = staged.clear ? { kind: 'clear' } : defaultParse(field, staged.text);
@@ -210,7 +210,7 @@ export class SettingsForm {
210
210
  }
211
211
  }
212
212
  for (const secret of secrets) {
213
- await this.host.writeCredential(secret.ref, secret.text);
213
+ await this.host.writeCredential(secret.ref, secret.text, secret.field.secret?.authorization);
214
214
  }
215
215
  for (const [key, edit] of snapshot) {
216
216
  if (this.edits.get(key) === edit)
@@ -6,6 +6,7 @@
6
6
  *
7
7
  * @module
8
8
  */
9
+ import type { Agent } from '@deepseek-ai/dsh-agent';
9
10
  import { type ContentBlock, type Message, type StreamChunk } from '@deepseek-ai/dsh-llm';
10
11
  import type { SessionEvent } from '@deepseek-ai/dsh-session';
11
12
  type ToolCall = Extract<ContentBlock, {
@@ -71,5 +72,25 @@ export declare function runSideQuestion(params: {
71
72
  /** Cancellation: aborting yields `{answer: null}` with no error text. */
72
73
  signal?: AbortSignal;
73
74
  }): Promise<SideQuestionOutcome>;
75
+ /** The `agents` service as {@link runAsSideBranch} needs it. */
76
+ export interface InitiatorScope {
77
+ withInitiator<T>(agent: Agent, operation: () => T): T;
78
+ }
79
+ /**
80
+ * Run a side question with a seeded fork of `agent` as its initiator. The
81
+ * call replays the session's history without entering it, which DSH models as
82
+ * a fork (`parentSession` + `isSeeded` on the child header). Initiator-keyed
83
+ * consumers then see a branch of the conversation rather than an agentless
84
+ * call: a compressing proxy (billion-context) adopts the parent's state for
85
+ * the branch instead of sending the raw replay, which overflows long sessions.
86
+ * Without the service, or once its scope is closing, the call runs as before.
87
+ *
88
+ * The view is no stored Session: `session.id` carries the branch identity
89
+ * while `header.id` deliberately keeps the parent's (a stored fork's header id
90
+ * mirrors its own session). The verified consumer path reads `session.id`;
91
+ * aligning `header.id` changes what a parent-resolving consumer sees and needs
92
+ * the same real-host recheck. scripts/verify-preset-patch.ts pins this shape.
93
+ */
94
+ export declare function runAsSideBranch<T>(agents: Partial<InitiatorScope> | undefined, agent: Agent, operation: () => Promise<T>): Promise<T>;
74
95
  export {};
75
96
  //# sourceMappingURL=sideQuestion.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sideQuestion.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/sideQuestion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACxG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAI5D,KAAK,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAA;AAK5D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,QAAQ,EAAO,GAAG,MAAM,CAG5F;AAeD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG;IACpG,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,OAAO,EAAE,QAAQ,EAAE,CAAA;CACpB,CA2BA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAUhF;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,yEAAyE;IACzE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC,WAAW,CAAC,CAAA;IACvD,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,yEAAyE;IACzE,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB/B"}
1
+ {"version":3,"file":"sideQuestion.d.ts","sourceRoot":"","sources":["../../../src/dsh-adapter/sideQuestion.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACnD,OAAO,EAAkB,KAAK,YAAY,EAAE,KAAK,OAAO,EAAE,KAAK,WAAW,EAAE,MAAM,sBAAsB,CAAA;AACxG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAK5D,KAAK,QAAQ,GAAG,OAAO,CAAC,YAAY,EAAE;IAAE,IAAI,EAAE,WAAW,CAAA;CAAE,CAAC,CAAA;AAK5D;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,GAAE,SAAS,QAAQ,EAAO,GAAG,MAAM,CAG5F;AAeD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,wBAAwB,CAAC,QAAQ,EAAE,SAAS,OAAO,EAAE,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG;IACpG,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAA;IAC5B,OAAO,EAAE,QAAQ,EAAE,CAAA;CACpB,CA2BA;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,SAAS,YAAY,EAAE,GAAG,GAAG,CAAC,MAAM,CAAC,CAUhF;AAED,0EAA0E;AAC1E,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAA;IACrB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAED;;;;;GAKG;AACH,wBAAsB,eAAe,CAAC,MAAM,EAAE;IAC5C,yEAAyE;IACzE,MAAM,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,aAAa,CAAC,WAAW,CAAC,CAAA;IACvD,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAA;IACf,iDAAiD;IACjD,MAAM,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAA;IAChC,yEAAyE;IACzE,MAAM,CAAC,EAAE,WAAW,CAAA;CACrB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAuB/B;AAED,gEAAgE;AAChE,MAAM,WAAW,cAAc;IAC7B,aAAa,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,CAAA;CACtD;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,cAAc,CAAC,GAAG,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAOrI"}
@@ -7,6 +7,7 @@
7
7
  * @module
8
8
  */
9
9
  import { BlockAssembler } from '@deepseek-ai/dsh-llm';
10
+ import { randomUUID } from 'node:crypto';
10
11
  import { sideQuestionPrompt } from '../channel/side-prompts.js';
11
12
  import { answeredToolCallIds } from './compat/messages.js';
12
13
  /** Per-call argument budget in the pending-calls note; a file write can carry megabytes. */
@@ -143,3 +144,55 @@ export async function runSideQuestion(params) {
143
144
  return { answer: null, error: 'No response received' };
144
145
  return { answer };
145
146
  }
147
+ /**
148
+ * Run a side question with a seeded fork of `agent` as its initiator. The
149
+ * call replays the session's history without entering it, which DSH models as
150
+ * a fork (`parentSession` + `isSeeded` on the child header). Initiator-keyed
151
+ * consumers then see a branch of the conversation rather than an agentless
152
+ * call: a compressing proxy (billion-context) adopts the parent's state for
153
+ * the branch instead of sending the raw replay, which overflows long sessions.
154
+ * Without the service, or once its scope is closing, the call runs as before.
155
+ *
156
+ * The view is no stored Session: `session.id` carries the branch identity
157
+ * while `header.id` deliberately keeps the parent's (a stored fork's header id
158
+ * mirrors its own session). The verified consumer path reads `session.id`;
159
+ * aligning `header.id` changes what a parent-resolving consumer sees and needs
160
+ * the same real-host recheck. scripts/verify-preset-patch.ts pins this shape.
161
+ */
162
+ export function runAsSideBranch(agents, agent, operation) {
163
+ if (typeof agents?.withInitiator !== 'function')
164
+ return operation();
165
+ try {
166
+ return agents.withInitiator(sideBranchOf(agent), operation);
167
+ }
168
+ catch {
169
+ return operation();
170
+ }
171
+ }
172
+ function sideBranchOf(agent) {
173
+ const session = agent.session;
174
+ const surface = session.surface;
175
+ const last = surface?.nodes?.at(-1);
176
+ const generation = surface?.replaceGeneration;
177
+ const id = last === undefined || generation === undefined
178
+ ? `${session.id}-btw-${randomUUID()}`
179
+ : `${session.id}-btw-${generation}-${last}`;
180
+ // header.id stays the parent's on purpose; see runAsSideBranch.
181
+ const header = { ...session.header, parentSession: session.id, isSeeded: true };
182
+ return readThrough(agent, { session: readThrough(session, { id, header }) });
183
+ }
184
+ /**
185
+ * A view of `target` with some properties replaced. Everything else, methods
186
+ * included, still acts on `target` (a mutating call reaches the real
187
+ * session); the view lives only for one side call's stream.
188
+ */
189
+ function readThrough(target, overrides) {
190
+ return new Proxy(target, {
191
+ get(object, key) {
192
+ if (Object.hasOwn(overrides, key))
193
+ return overrides[key];
194
+ const value = Reflect.get(object, key, object);
195
+ return typeof value === 'function' ? value.bind(object) : value;
196
+ },
197
+ });
198
+ }