@deepseek-insight/dsi 0.5.5

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 (285) hide show
  1. package/LICENSE +204 -0
  2. package/README.md +98 -0
  3. package/bin/dsi.mjs +147 -0
  4. package/build/client/_app/immutable/assets/0.DqFt8jIM.css +2 -0
  5. package/build/client/_app/immutable/assets/2.YlW45hiM.css +1 -0
  6. package/build/client/_app/immutable/assets/codicon.Brq4_Ui5.ttf +0 -0
  7. package/build/client/_app/immutable/assets/editor.jjEx9u7D.css +1 -0
  8. package/build/client/_app/immutable/assets/settings-monaco.C1CMdMvw.css +1 -0
  9. package/build/client/_app/immutable/chunks/-g7BcI22.js +4 -0
  10. package/build/client/_app/immutable/chunks/08VXUWAP.js +1 -0
  11. package/build/client/_app/immutable/chunks/2irkVCIh.js +10 -0
  12. package/build/client/_app/immutable/chunks/43DmfD33.js +1 -0
  13. package/build/client/_app/immutable/chunks/6GKQw8m6.js +99 -0
  14. package/build/client/_app/immutable/chunks/6RTg0lbw.js +1 -0
  15. package/build/client/_app/immutable/chunks/9TvT6fta.js +120 -0
  16. package/build/client/_app/immutable/chunks/B1YSaoU7.js +96 -0
  17. package/build/client/_app/immutable/chunks/B2J2w5EN.js +1 -0
  18. package/build/client/_app/immutable/chunks/B5-_aFwp.js +2 -0
  19. package/build/client/_app/immutable/chunks/B8-Q2PIv.js +1 -0
  20. package/build/client/_app/immutable/chunks/B8J7ngcE.js +1 -0
  21. package/build/client/_app/immutable/chunks/B9JPFI3C.js +2 -0
  22. package/build/client/_app/immutable/chunks/BAZKJuQw.js +1 -0
  23. package/build/client/_app/immutable/chunks/BBKpNcUe.js +1 -0
  24. package/build/client/_app/immutable/chunks/BEEn4gHE.js +1 -0
  25. package/build/client/_app/immutable/chunks/BEtHBSE6.js +1 -0
  26. package/build/client/_app/immutable/chunks/BFoDCU3G.js +1 -0
  27. package/build/client/_app/immutable/chunks/BHtPwB2y.js +847 -0
  28. package/build/client/_app/immutable/chunks/BI-MrBd-.js +1 -0
  29. package/build/client/_app/immutable/chunks/BI4pMaIG.js +1 -0
  30. package/build/client/_app/immutable/chunks/BJL7RCQE.js +4 -0
  31. package/build/client/_app/immutable/chunks/BKxtCVv1.js +1 -0
  32. package/build/client/_app/immutable/chunks/BOfSXrr7.js +1 -0
  33. package/build/client/_app/immutable/chunks/BOotOW0E.js +1 -0
  34. package/build/client/_app/immutable/chunks/BTzV9O1M.js +3 -0
  35. package/build/client/_app/immutable/chunks/BW-FRVGs.js +1 -0
  36. package/build/client/_app/immutable/chunks/BWPQTe0t.js +1 -0
  37. package/build/client/_app/immutable/chunks/BYgofb2h.js +1 -0
  38. package/build/client/_app/immutable/chunks/Ba7i2nA0.js +1 -0
  39. package/build/client/_app/immutable/chunks/Bb19NAkg.js +1 -0
  40. package/build/client/_app/immutable/chunks/Bc_sGQ0U.js +1 -0
  41. package/build/client/_app/immutable/chunks/BeGwcela.js +1 -0
  42. package/build/client/_app/immutable/chunks/BeZqd5lB.js +1 -0
  43. package/build/client/_app/immutable/chunks/Beo7Z46k.js +7 -0
  44. package/build/client/_app/immutable/chunks/BhNEBkk_.js +39 -0
  45. package/build/client/_app/immutable/chunks/BifhSqXX.js +1 -0
  46. package/build/client/_app/immutable/chunks/Bjy-W4x2.js +81 -0
  47. package/build/client/_app/immutable/chunks/BlltJnm0.js +1 -0
  48. package/build/client/_app/immutable/chunks/Bn9NRi0R.js +168 -0
  49. package/build/client/_app/immutable/chunks/BnF5Jm-h.js +1 -0
  50. package/build/client/_app/immutable/chunks/Bp4uuUjI.js +3 -0
  51. package/build/client/_app/immutable/chunks/BrTsmS-o.js +57 -0
  52. package/build/client/_app/immutable/chunks/Bri6UV-V.js +1 -0
  53. package/build/client/_app/immutable/chunks/Btk-1DaX.js +1 -0
  54. package/build/client/_app/immutable/chunks/BwWrilGY.js +1 -0
  55. package/build/client/_app/immutable/chunks/Bxkupp3x.js +1 -0
  56. package/build/client/_app/immutable/chunks/Bz4Y3fRF.js +1 -0
  57. package/build/client/_app/immutable/chunks/C-OQMol4.js +1 -0
  58. package/build/client/_app/immutable/chunks/C-y9NEjX.js +1 -0
  59. package/build/client/_app/immutable/chunks/C1JXY9b5.js +30 -0
  60. package/build/client/_app/immutable/chunks/C2x7inlt.js +321 -0
  61. package/build/client/_app/immutable/chunks/C531GsRP.js +2 -0
  62. package/build/client/_app/immutable/chunks/C5TAQhFO.js +1 -0
  63. package/build/client/_app/immutable/chunks/C5_-GSwl.js +1 -0
  64. package/build/client/_app/immutable/chunks/C6lv30UQ.js +1 -0
  65. package/build/client/_app/immutable/chunks/C7h46wYY.js +1 -0
  66. package/build/client/_app/immutable/chunks/C8Ef5oNN.js +1 -0
  67. package/build/client/_app/immutable/chunks/CA_WNCsl.js +41 -0
  68. package/build/client/_app/immutable/chunks/CC2rA5mh.js +1 -0
  69. package/build/client/_app/immutable/chunks/CHdTS7qa.js +15 -0
  70. package/build/client/_app/immutable/chunks/CIPQ_RoX.js +1 -0
  71. package/build/client/_app/immutable/chunks/CJ_zvTBg.js +72 -0
  72. package/build/client/_app/immutable/chunks/CPEyWost.js +1 -0
  73. package/build/client/_app/immutable/chunks/CPgSxomx.js +1 -0
  74. package/build/client/_app/immutable/chunks/CRfGWb83.js +1 -0
  75. package/build/client/_app/immutable/chunks/CTgm3pse.js +1 -0
  76. package/build/client/_app/immutable/chunks/CVaqCwHa.js +1 -0
  77. package/build/client/_app/immutable/chunks/CWKM3f5q.js +1 -0
  78. package/build/client/_app/immutable/chunks/CWVpRRWx.js +162 -0
  79. package/build/client/_app/immutable/chunks/CXMH3UgJ.js +257 -0
  80. package/build/client/_app/immutable/chunks/CZ-RNlot.js +1 -0
  81. package/build/client/_app/immutable/chunks/CZXl7qUb.js +70 -0
  82. package/build/client/_app/immutable/chunks/C_N-eONP.js +1 -0
  83. package/build/client/_app/immutable/chunks/Cb1pwoev.js +1 -0
  84. package/build/client/_app/immutable/chunks/CcL1k2On.js +11 -0
  85. package/build/client/_app/immutable/chunks/CcWV7GYH.js +1 -0
  86. package/build/client/_app/immutable/chunks/CcwsVR0C.js +1 -0
  87. package/build/client/_app/immutable/chunks/Cd6K7Y8m.js +1 -0
  88. package/build/client/_app/immutable/chunks/CfSJPG6W.js +1 -0
  89. package/build/client/_app/immutable/chunks/CfZ7uSQe.js +38 -0
  90. package/build/client/_app/immutable/chunks/Cimd5fb3.js +1 -0
  91. package/build/client/_app/immutable/chunks/Cj-Zm41R.js +36 -0
  92. package/build/client/_app/immutable/chunks/CjZ_QSbm.js +1 -0
  93. package/build/client/_app/immutable/chunks/CkrcOjFY.js +1 -0
  94. package/build/client/_app/immutable/chunks/ClamHrr6.js +1 -0
  95. package/build/client/_app/immutable/chunks/Cm7r79vr.js +1 -0
  96. package/build/client/_app/immutable/chunks/CmbEK4CY.js +84 -0
  97. package/build/client/_app/immutable/chunks/CmvnEPtD.js +7 -0
  98. package/build/client/_app/immutable/chunks/CopQ2nXW.js +1 -0
  99. package/build/client/_app/immutable/chunks/CpzslTdj.js +1 -0
  100. package/build/client/_app/immutable/chunks/CsHVnZCH.js +1 -0
  101. package/build/client/_app/immutable/chunks/CsQS60Ue.js +1 -0
  102. package/build/client/_app/immutable/chunks/CuRJVwEq.js +1 -0
  103. package/build/client/_app/immutable/chunks/D-oSqn_W.js +1 -0
  104. package/build/client/_app/immutable/chunks/D-ptJ9y-.js +1 -0
  105. package/build/client/_app/immutable/chunks/D05T4iGw.js +1 -0
  106. package/build/client/_app/immutable/chunks/D0I62FkS.js +1 -0
  107. package/build/client/_app/immutable/chunks/D16vkywU.js +1 -0
  108. package/build/client/_app/immutable/chunks/D1qzYj9m.js +1 -0
  109. package/build/client/_app/immutable/chunks/D3hlyOfw.js +1 -0
  110. package/build/client/_app/immutable/chunks/D4gq_dik.js +1 -0
  111. package/build/client/_app/immutable/chunks/D7fX-3xy.js +1 -0
  112. package/build/client/_app/immutable/chunks/D8-5qLJg.js +231 -0
  113. package/build/client/_app/immutable/chunks/D8hlhfM3.js +1 -0
  114. package/build/client/_app/immutable/chunks/DAS1CLuE.js +24 -0
  115. package/build/client/_app/immutable/chunks/DGarjVZC.js +1 -0
  116. package/build/client/_app/immutable/chunks/DGd2pJF5.js +1 -0
  117. package/build/client/_app/immutable/chunks/DHpiXF5c.js +1 -0
  118. package/build/client/_app/immutable/chunks/DHwB1DNv.js +161 -0
  119. package/build/client/_app/immutable/chunks/DIMkf-bt.js +3 -0
  120. package/build/client/_app/immutable/chunks/DISqw1ZD.js +1 -0
  121. package/build/client/_app/immutable/chunks/DITEY8D_.js +62 -0
  122. package/build/client/_app/immutable/chunks/DL0yNkAA.js +1 -0
  123. package/build/client/_app/immutable/chunks/DL1Hc2zs.js +1 -0
  124. package/build/client/_app/immutable/chunks/DLOOos_8.js +292 -0
  125. package/build/client/_app/immutable/chunks/DMUM7wLl.js +1 -0
  126. package/build/client/_app/immutable/chunks/DPKol_4Z.js +139 -0
  127. package/build/client/_app/immutable/chunks/DS17s2tU.js +1 -0
  128. package/build/client/_app/immutable/chunks/DT7zqm-g.js +1 -0
  129. package/build/client/_app/immutable/chunks/DTj74zXo.js +1 -0
  130. package/build/client/_app/immutable/chunks/DV3o63ZY.js +1 -0
  131. package/build/client/_app/immutable/chunks/DVXVVWkX.js +129 -0
  132. package/build/client/_app/immutable/chunks/DWDfcLfy.js +206 -0
  133. package/build/client/_app/immutable/chunks/DXeGLmed.js +8 -0
  134. package/build/client/_app/immutable/chunks/DYqsupvX.js +1 -0
  135. package/build/client/_app/immutable/chunks/DZ6u0Z-K.js +1 -0
  136. package/build/client/_app/immutable/chunks/DZFCIeNp.js +1 -0
  137. package/build/client/_app/immutable/chunks/D_Lu-MrJ.js +1 -0
  138. package/build/client/_app/immutable/chunks/DauUninz.js +1 -0
  139. package/build/client/_app/immutable/chunks/DbInun42.js +1 -0
  140. package/build/client/_app/immutable/chunks/DbcKN35_.js +3 -0
  141. package/build/client/_app/immutable/chunks/DcZe4Woe.js +1 -0
  142. package/build/client/_app/immutable/chunks/DdL9SqIa.js +1 -0
  143. package/build/client/_app/immutable/chunks/De2hgUGE.js +1 -0
  144. package/build/client/_app/immutable/chunks/DezsRK8O.js +1 -0
  145. package/build/client/_app/immutable/chunks/Dg_UWGX7.js +24 -0
  146. package/build/client/_app/immutable/chunks/Dj2t5yNg.js +1 -0
  147. package/build/client/_app/immutable/chunks/DkqhCAOL.js +1 -0
  148. package/build/client/_app/immutable/chunks/DlQrSOw4.js +7 -0
  149. package/build/client/_app/immutable/chunks/DmHpPYUd.js +1 -0
  150. package/build/client/_app/immutable/chunks/Dmn-tsie.js +1 -0
  151. package/build/client/_app/immutable/chunks/DpFanUEy.js +298 -0
  152. package/build/client/_app/immutable/chunks/DqwfDHZH.js +89 -0
  153. package/build/client/_app/immutable/chunks/Dr0pcX_F.js +1 -0
  154. package/build/client/_app/immutable/chunks/DrH0SRf2.js +1 -0
  155. package/build/client/_app/immutable/chunks/DsF7k-Jl.js +1 -0
  156. package/build/client/_app/immutable/chunks/DtBGId5S.js +2 -0
  157. package/build/client/_app/immutable/chunks/DuM1hSCa.js +1 -0
  158. package/build/client/_app/immutable/chunks/Du_ODUAO.js +1 -0
  159. package/build/client/_app/immutable/chunks/DvCMcJ3P.js +1 -0
  160. package/build/client/_app/immutable/chunks/Dy-Kn_BL.js +1 -0
  161. package/build/client/_app/immutable/chunks/DzVMo6mB.js +62 -0
  162. package/build/client/_app/immutable/chunks/ESmLJICu.js +1 -0
  163. package/build/client/_app/immutable/chunks/FvmIjYaQ.js +1 -0
  164. package/build/client/_app/immutable/chunks/GBaNblmo.js +1 -0
  165. package/build/client/_app/immutable/chunks/HclGiUj8.js +1 -0
  166. package/build/client/_app/immutable/chunks/Ju5jF6hy.js +1 -0
  167. package/build/client/_app/immutable/chunks/KythIMN8.js +1 -0
  168. package/build/client/_app/immutable/chunks/LWre5GKl.js +40 -0
  169. package/build/client/_app/immutable/chunks/NEE52Syq.js +1 -0
  170. package/build/client/_app/immutable/chunks/No6lfiAw.js +1 -0
  171. package/build/client/_app/immutable/chunks/OmE6SGvs.js +106 -0
  172. package/build/client/_app/immutable/chunks/P_ok0Z_j.js +2 -0
  173. package/build/client/_app/immutable/chunks/PaZBB01u.js +1 -0
  174. package/build/client/_app/immutable/chunks/Rh9cy7xg.js +2 -0
  175. package/build/client/_app/immutable/chunks/SOo6toE5.js +1 -0
  176. package/build/client/_app/immutable/chunks/TNLUqJaC.js +1 -0
  177. package/build/client/_app/immutable/chunks/UNxFjlik.js +1 -0
  178. package/build/client/_app/immutable/chunks/UmdltqoE.js +1 -0
  179. package/build/client/_app/immutable/chunks/Z5eAx3Zn.js +1 -0
  180. package/build/client/_app/immutable/chunks/bTuwJoIa.js +1 -0
  181. package/build/client/_app/immutable/chunks/fmXr3nnJ.js +1 -0
  182. package/build/client/_app/immutable/chunks/fybaL0A-.js +1 -0
  183. package/build/client/_app/immutable/chunks/gp-XZpBa.js +3 -0
  184. package/build/client/_app/immutable/chunks/h-p5ZdY8.js +1 -0
  185. package/build/client/_app/immutable/chunks/hxYEUgOo.js +1 -0
  186. package/build/client/_app/immutable/chunks/i0VQdJtv.js +1 -0
  187. package/build/client/_app/immutable/chunks/i0X4ZdIN.js +1 -0
  188. package/build/client/_app/immutable/chunks/mVHw7KEX.js +7 -0
  189. package/build/client/_app/immutable/chunks/nDMCpPkF.js +1 -0
  190. package/build/client/_app/immutable/chunks/nr791fC2.js +1 -0
  191. package/build/client/_app/immutable/chunks/onAF5SnQ.js +1 -0
  192. package/build/client/_app/immutable/chunks/oz_6vUea.js +1 -0
  193. package/build/client/_app/immutable/chunks/qEGLx2Wc.js +78 -0
  194. package/build/client/_app/immutable/chunks/raVGrKQy.js +44 -0
  195. package/build/client/_app/immutable/chunks/sBoK_t0W.js +1 -0
  196. package/build/client/_app/immutable/chunks/sIEmSkn6.js +34 -0
  197. package/build/client/_app/immutable/chunks/wFIeY1Cf.js +88 -0
  198. package/build/client/_app/immutable/chunks/xihTtKlq.js +1 -0
  199. package/build/client/_app/immutable/chunks/zpcgJjUg.js +127 -0
  200. package/build/client/_app/immutable/entry/app.B2QxMn0t.js +2 -0
  201. package/build/client/_app/immutable/entry/start.BR3A4iFi.js +1 -0
  202. package/build/client/_app/immutable/nodes/0.B_ZEZy7b.js +1 -0
  203. package/build/client/_app/immutable/nodes/1.By3Gd2cs.js +1 -0
  204. package/build/client/_app/immutable/nodes/2.1TAwiWw1.js +69 -0
  205. package/build/client/_app/immutable/workers/css.worker-DRH2OcAG.js +93 -0
  206. package/build/client/_app/immutable/workers/html.worker-DQLddT9h.js +506 -0
  207. package/build/client/_app/immutable/workers/json.worker-D_Dqrls5.js +62 -0
  208. package/build/client/_app/immutable/workers/settings-editor-worker-D9zwrD0f.js +30 -0
  209. package/build/client/_app/immutable/workers/ts.worker-DkYoA6bF.js +67738 -0
  210. package/build/client/_app/immutable/workers/yaml.worker-BTGFEDOA.js +449 -0
  211. package/build/client/_app/version.json +1 -0
  212. package/build/client/favicon.svg +7 -0
  213. package/build/env.js +70 -0
  214. package/build/handler.js +23 -0
  215. package/build/index.js +146 -0
  216. package/build/server/chunks/chunks/a2a-protocol.js-C77mVg2Q.js +52 -0
  217. package/build/server/chunks/chunks/app-config.svelte.js-Co9jacqT.js +52 -0
  218. package/build/server/chunks/chunks/config.js-BBYogCe7.js +123 -0
  219. package/build/server/chunks/chunks/db.js-DcmO3JAx.js +120 -0
  220. package/build/server/chunks/chunks/db2.js-DBltVUGg.js +717 -0
  221. package/build/server/chunks/chunks/dsh-connection.js-DwihVtk_.js +1957 -0
  222. package/build/server/chunks/chunks/dsh-events.js-BtDxaV6Q.js +706 -0
  223. package/build/server/chunks/chunks/env.js-l0sNRNKZ.js +2 -0
  224. package/build/server/chunks/chunks/exports.js-DMiPee3x.js +263 -0
  225. package/build/server/chunks/chunks/goal-projection.js-0_34t60K.js +44 -0
  226. package/build/server/chunks/chunks/image-limits.js-BMPZ55kJ.js +186 -0
  227. package/build/server/chunks/chunks/insight-config.js-CfMPTA6Z.js +352 -0
  228. package/build/server/chunks/chunks/internal.js-CQzEjdyk.js +747 -0
  229. package/build/server/chunks/chunks/internal2.js-D-DAuSVh.js +22 -0
  230. package/build/server/chunks/chunks/server.js-DoCd0fEo.js +4104 -0
  231. package/build/server/chunks/chunks/shared.js-C4-Py8s_.js +775 -0
  232. package/build/server/chunks/chunks/state.js-CpmOS0a2.js +114 -0
  233. package/build/server/chunks/chunks/uneval.js-BJ9Qq2SF.js +488 -0
  234. package/build/server/chunks/chunks/utils.js-tOWCe9Wk.js +933 -0
  235. package/build/server/chunks/chunks/watcher.js-BelxjMOP.js +473 -0
  236. package/build/server/chunks/entries/endpoints/api/a2a/_id_/_server.ts.js-B4HUNFZv.js +37 -0
  237. package/build/server/chunks/entries/endpoints/api/a2a/_server.ts.js-JDV_oqbX.js +61 -0
  238. package/build/server/chunks/entries/endpoints/api/a2a/register/_server.ts.js-Dxwc-7-5.js +99 -0
  239. package/build/server/chunks/entries/endpoints/api/config/_server.ts.js-D8i35bGH.js +35 -0
  240. package/build/server/chunks/entries/endpoints/api/dsh/directory/_server.ts.js-CeBU1G85.js +43 -0
  241. package/build/server/chunks/entries/endpoints/api/dsh/pick-directory/_server.ts.js-CyrVUxv3.js +36 -0
  242. package/build/server/chunks/entries/endpoints/api/dsh/presets/_server.ts.js-qeO_1htT.js +33 -0
  243. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/attachment/_server.ts.js-DnHffohz.js +44 -0
  244. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/cancel/_server.ts.js-fm6MFyjP.js +33 -0
  245. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/catalog/_server.ts.js-wJ9V_Ue7.js +42 -0
  246. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/command/_server.ts.js-o83L4GUY.js +85 -0
  247. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/events/_server.ts.js-DLCHSC1M.js +212 -0
  248. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/fork/_server.ts.js-Ca6NU9HO.js +58 -0
  249. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/goal/_server.ts.js-RteR85vn.js +138 -0
  250. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/history/_server.ts.js-CEKDkK7v.js +55 -0
  251. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/models/_server.ts.js-D7ziyW3d.js +35 -0
  252. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/permission/_server.ts.js-DbkD4JZb.js +92 -0
  253. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/prompt/_server.ts.js-eOLNlf1-.js +94 -0
  254. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/rename/_server.ts.js-txQRocK9.js +57 -0
  255. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/respond/_server.ts.js-D3WGYzkE.js +69 -0
  256. package/build/server/chunks/entries/endpoints/api/dsh/session/_sessionId_/select-model/_server.ts.js-g2GtvLTs.js +69 -0
  257. package/build/server/chunks/entries/endpoints/api/dsh/sessions/_server.ts.js-17yKojU_.js +85 -0
  258. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/_server.ts.js-D16pEhvd.js +99 -0
  259. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file/save/_server.ts.js-BXpZNLK8.js +110 -0
  260. package/build/server/chunks/entries/endpoints/api/dsh/workspace-file-bytes/_server.ts.js-hBlyjq-x.js +113 -0
  261. package/build/server/chunks/entries/endpoints/api/dsh/workspace-tree/_server.ts.js-KWRnsAkm.js +59 -0
  262. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_server.ts.js-DzNvvCxK.js +55 -0
  263. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/delete/_server.ts.js-MK5925CY.js +45 -0
  264. package/build/server/chunks/entries/endpoints/api/dsh/workspaces/_workspaceId_/rename/_server.ts.js-w3Mo0nf7.js +63 -0
  265. package/build/server/chunks/entries/endpoints/api/prompts/_id_/_server.ts.js-CInfjM-i.js +64 -0
  266. package/build/server/chunks/entries/endpoints/api/prompts/_server.ts.js-C1lqP1OV.js +87 -0
  267. package/build/server/chunks/entries/endpoints/api/prompts/use/_server.ts.js-B36YMKMt.js +54 -0
  268. package/build/server/chunks/entries/endpoints/api/settings/_server.ts.js-CHvHNzeB.js +101 -0
  269. package/build/server/chunks/entries/pages/_error.svelte.js-BE3-BWDd.js +41 -0
  270. package/build/server/chunks/entries/pages/_layout.svelte.js-EFiihTdF.js +18 -0
  271. package/build/server/chunks/entries/pages/_layout.ts.js-CNSo-Z1U.js +22 -0
  272. package/build/server/chunks/entries/pages/_page.server.ts.js-Bk8UuUXA.js +85 -0
  273. package/build/server/chunks/entries/pages/_page.svelte.js-CfmKQYLK.js +13883 -0
  274. package/build/server/chunks/handler-BshnD4Y4.js +1735 -0
  275. package/build/server/chunks/index.js-I_pMeIwQ.js +3864 -0
  276. package/build/server/chunks/manifest.js-DwhpAk_y.js +273 -0
  277. package/build/server/chunks/nodes/0.js-DCyDo8-C.js +12 -0
  278. package/build/server/chunks/nodes/1.js-Dx7z_VWN.js +9 -0
  279. package/build/server/chunks/nodes/2.js-CLw9pxho.js +24 -0
  280. package/build/shims.js +33 -0
  281. package/package.json +65 -0
  282. package/template/.dsi/a2a.sqlite +0 -0
  283. package/template/.dsi/config.json +42 -0
  284. package/template/.dsi/prompts.sqlite +0 -0
  285. package/template/.dsi/settings.yaml +33 -0
@@ -0,0 +1,1957 @@
1
+ import { p as dshBaseUrl } from './config.js-BBYogCe7.js';
2
+ import { r as readDshPresetEnglishConfig, a as readDshAuthConfig, b as readServerConfig } from './insight-config.js-CfMPTA6Z.js';
3
+
4
+ //#region src/lib/server/dsh-rpc.ts
5
+ /**
6
+ * dsh-rpc — the single choke point for DSH wire bytes (BC-6).
7
+ *
8
+ * Every RPC fact below is pinned by the gateway guide
9
+ * (dev/gateways/dsh-gateway-live-streaming.md) and the harness sources it
10
+ * cites (packages/client/connection/src/client/rpc.ts — envelope;
11
+ * packages/host/apiproxy/src/api/rpc-map.ts — POST /api/{method} paths;
12
+ * api/rpc.ts — RpcResult ok/false error union; api/sessions.ts — payloads).
13
+ *
14
+ * Live-verified 2026-08-21: session.list and session.history round-trip,
15
+ * events.mux open → session/subscribed baseline with lastSeq.
16
+ */
17
+ /**
18
+ * Wire endpoints this app calls (0.1.2-alpha.1 wire: slash paths, args
19
+ * envelopes). The closed set grows only by spec change (BC-6). Arg names are
20
+ * each endpoint's declared parameter name — exact-match validated by the
21
+ * host descriptor (extra/missing keys → arguments-invalid).
22
+ *
23
+ * Live-verified 2026-08-29 against dsh-0.1.2-alpha.1 (cd5ef81481):
24
+ * session/list, session/create, session/prompt, session/rename,
25
+ * session/selectModel, session/modelCatalog, session/page, session/cancel,
26
+ * session/attachment, agentPresets/list, workspace/create, commands/list
27
+ * (verified on the wire, never called by DSI — no DSH_METHODS entry),
28
+ * commands/execute, directoryPicker/pick. DSH_METHODS below is the canonical
29
+ * set this app calls.
30
+ *
31
+ * Grown by spec 2026-08-30 (BC-6): the Slash Menu ADR
32
+ * (dev/architectural-decission/2026-08-30 - The Slash Menu — The Composer
33
+ * Reads the Host's Own Directory.md) adds commandsList + skillsList — DSI
34
+ * becomes a reader of the host's command/skill catalogs. Both endpoints
35
+ * live-pinned 2026-08-30 against dsh web 0.1.2 (see tests/unit/dsh-rpc.test.ts
36
+ * — the recorded round-trip: envelope payload:{args}, skills 10 rows,
37
+ * commands 5 rows).
38
+ */
39
+ var DSH_METHODS = {
40
+ /** args `{}` — `_request` is reserved-empty (acceptsUndefined). */
41
+ list: "session/list",
42
+ /** args `{request:{address,throughSeq,beforeSeq?,maxMessages?}}` → `{records,hasMore}`. */
43
+ page: "session/page",
44
+ prompt: "session/prompt",
45
+ cancel: "session/cancel",
46
+ create: "session/create",
47
+ /**
48
+ * args `{request:{sessionId,atSeq?}}` → `{sessionId}` (the fork child).
49
+ * Host contract (harness packages/api/session-controller/src/types.ts
50
+ * SessionForkRequest/Value): `atSeq` omitted cuts at the source's last
51
+ * completed turn; a given `atSeq` anchors to the first `turn/end` at or
52
+ * after it — an anchor inside an open turn refuses `session/fork-unavailable`,
53
+ * never clips. Refusals: `session/not-found`, `session/fork-unavailable`,
54
+ * `session/workspace-attach-failed`. Live-pinned 2026-09-01 against
55
+ * dsh-0.1.2-alpha.3: the args descriptor exact-matches `{request:{sessionId}}`
56
+ * (a bare sessionId → gateway/arguments-invalid: missing "request") and a
57
+ * turn-less source refuses fork-unavailable ("has no completed turn to
58
+ * fork from"). The success path (child id) is pinned by the harness
59
+ * client fixture (connection/src/client/fixture.ts case 'session/fork').
60
+ */
61
+ fork: "session/fork",
62
+ presets: "agentPresets/list",
63
+ rename: "session/rename",
64
+ /** args `{}` → ModelCatalog {default,routableProviders,groups,failures}. */
65
+ modelCatalog: "session/modelCatalog",
66
+ selectModel: "session/selectModel",
67
+ workspaceCreate: "workspace/create",
68
+ /**
69
+ * args `{request:{workspaceId,title}}` → Workspace view. Refusals:
70
+ * `workspace/not-found`, `workspace/name-conflict` (details carry the
71
+ * conflicting name). A no-op title (trimmed-equal) succeeds silently and
72
+ * emits NO follow frame — semantics pinned against the harness client
73
+ * fixture (connection fixture.client.spec.ts:1187-1216). Chip Menu ADR D3.
74
+ */
75
+ workspaceRename: "workspace/rename",
76
+ /**
77
+ * args `{request:{workspaceId}}` → removed workspaceId. Removes ONLY the
78
+ * registry row (sessions survive, ungrouped) and emits a remove frame on
79
+ * workspace/follow — pinned at connection fixture.client.spec.ts:1292-1303.
80
+ * Chip Menu ADR D3.
81
+ */
82
+ workspaceDelete: "workspace/delete",
83
+ listDirectory: "directoryPicker/list",
84
+ pickDirectory: "directoryPicker/pick",
85
+ /**
86
+ * args `{agentId,line,submittedAttachments}` → CommandExecution | null
87
+ * (agentId = sessionId). 0.1.3-alpha.1 renamed the wire arg — the Typert
88
+ * descriptor binds the implementation's parameter name, and
89
+ * CommandRuntime.execute's third parameter became `submittedAttachments`
90
+ * (mixed image/file submissions); the old `images` envelope now rejects
91
+ * `gateway/arguments-invalid` (probed live 2026-09-05).
92
+ */
93
+ commandExecute: "commands/execute",
94
+ /** args `{agentId}` → DshCommandRow[] name-sorted (Slash Menu ADR §2; live-pinned 2026-08-30). */
95
+ commandsList: "commands/list",
96
+ /** args `{request:{sessionId}}` → DshSkillList (user-invocable only; Slash Menu ADR §2; live-pinned 2026-08-30). */
97
+ skillsList: "skills/list",
98
+ attachment: "session/attachment",
99
+ /**
100
+ * args `{workspaceFileScopeId, path, range?}` - WorkspaceFileText
101
+ * {offset,text,lines,eof,absolutePath,version,bytes?} (Workspace Explorer
102
+ * W2 task 2.1). The scope wire key was `agentId` until the host's
103
+ * 2026-09-09 session-roots fix (7214a97723, first shipped 0.1.5-alpha.2)
104
+ * registered the `workspaceFileScope` lookup with
105
+ * `wire: 'workspaceFileScopeId'` (packages/api/workspace-files/src/index.ts:204
106
+ * at 0.1.5-rc.1) — sending `agentId` now rejects gateway/arguments-invalid
107
+ * (observed live 2026-09-10). Refusals (RemoteErrorDetailsMap, types.ts):
108
+ * workspace-file/not-found, outside-workspace, too-large, not-text,
109
+ * not-regular-file.
110
+ */
111
+ workspaceFileRead: "workspaceFiles/read",
112
+ /**
113
+ * args `{workspaceFileScopeId, path, range}` → WorkspaceFileBytes —
114
+ * readBytes(workspaceFileScope, path, range) on the same descriptor: raw
115
+ * bytes (base64 on the wire),
116
+ * no text decoding and no binary refusal. Binary previews (images)
117
+ * read through this (2026-09-10): `read` refuses non-text with
118
+ * workspace-file/not-text. Same refusals as read.
119
+ */
120
+ workspaceFileReadBytes: "workspaceFiles/readBytes",
121
+ workspaceFileList: "workspaceFiles/list"
122
+ };
123
+ /** Args builders — one per endpoint, pinned to the live-probed shapes. */
124
+ var DSH_ARGS = {
125
+ list: () => ({ _request: {} }),
126
+ page: (request) => ({ request }),
127
+ prompt: (request) => ({ request }),
128
+ cancel: (request) => ({ request }),
129
+ create: (request) => ({ request }),
130
+ fork: (request) => ({ request }),
131
+ presets: () => ({}),
132
+ rename: (request) => ({ request }),
133
+ modelCatalog: () => ({}),
134
+ selectModel: (request) => ({ request }),
135
+ workspaceCreate: (request) => ({ request }),
136
+ workspaceRename: (workspaceId, title) => ({ request: {
137
+ workspaceId,
138
+ title
139
+ } }),
140
+ workspaceDelete: (workspaceId) => ({ request: { workspaceId } }),
141
+ listDirectory: (path) => path === void 0 ? {} : { path },
142
+ pickDirectory: () => ({}),
143
+ commandExecute: (agentId, line, submittedAttachments) => ({
144
+ agentId,
145
+ line,
146
+ submittedAttachments
147
+ }),
148
+ commandsList: (agentId) => ({ agentId }),
149
+ skillsList: (sessionId) => ({ request: { sessionId } }),
150
+ attachment: (request) => ({ request }),
151
+ /** Flat args — the descriptor has NO request wrapper: scope wire
152
+ * workspaceFileScopeId (2026-09-09 rename, was agentId), json path/range. */
153
+ workspaceFileRead: (sessionId, path, range) => ({
154
+ workspaceFileScopeId: sessionId,
155
+ path,
156
+ range: range ?? {}
157
+ }),
158
+ /** Flat args like the read — the descriptor scopes workspaceFileScopeId on the wire. */
159
+ workspaceFileList: (sessionId, path) => ({
160
+ workspaceFileScopeId: sessionId,
161
+ path
162
+ }),
163
+ /** Flat args like the read — WorkspaceByteRange {offset, length}, 0-based
164
+ * bytes; `{}` = the host's window defaults. */
165
+ workspaceFileReadBytes: (sessionId, path, range) => ({
166
+ workspaceFileScopeId: sessionId,
167
+ path,
168
+ range: range ?? {}
169
+ })
170
+ };
171
+ /**
172
+ * The ONE stream carrier (0.1.2): `/api/remote.mux` WebSocket multiplexing
173
+ * logical streams — `{type:'open',streamId,endpoint,payload}` opens one,
174
+ * `{type:'item'|'end'|'error',streamId,…}` frames carry it (stream-protocol.ts).
175
+ * Cookie-authenticated like every /api surface. The old events.mux/events.host
176
+ * pair is gone (404).
177
+ */
178
+ var DSH_REMOTE_MUX_WS_PATH = "/api/remote.mux";
179
+ /** Forwarded-event logical stream endpoint ($events — approvals/questions ride it). */
180
+ var REMOTE_EVENT_STREAM_ENDPOINT = "$events";
181
+ /** Empty standard payload that opens the $events stream (stream-protocol.ts). */
182
+ var REMOTE_EVENT_STREAM_PAYLOAD = { args: {} };
183
+ /** Waterfall-result unary endpoint answering a forwarded event ($events/result). */
184
+ var REMOTE_EVENT_RESULT_ENDPOINT = "$events/result";
185
+ /** Error carrying the DSH RpcError (ok:false result). */
186
+ var DshRpcError = class extends Error {
187
+ code;
188
+ details;
189
+ constructor(code, message, details) {
190
+ super(`DSH RPC ${code}: ${message}`);
191
+ this.name = "DshRpcError";
192
+ this.code = code;
193
+ this.details = details;
194
+ }
195
+ };
196
+ /**
197
+ * api-dsh error mapping (Module Map: dsh-rpc owns error mapping; routes stay DRY
198
+ * with no cross-route imports). DshRpcError → 502 with the host's code; anything
199
+ * else (transport down) → 503 host-unreachable.
200
+ */
201
+ function statusFor(err) {
202
+ return err instanceof DshRpcError ? 502 : 503;
203
+ }
204
+ /**
205
+ * Host codes for the subagent-ownership family (session-controller
206
+ * validateAddress + apiSessionSubagentOwnershipError): the request reached the
207
+ * host and was REJECTED by session policy — the host is fine, the address form
208
+ * or ownership is the problem. Never surfaces as "cannot reach the host".
209
+ * 0.1.2-alpha.2+ wire vocabulary: `<domain>/<reason>` codes (the 2026-08-28
210
+ * RemoteError note in the harness repo renamed them wholesale).
211
+ */
212
+ var SUBAGENT_REJECTION_CODES = /* @__PURE__ */ new Set([
213
+ "session/agent-busy",
214
+ "subagent/unauthorized",
215
+ "subagent/not-found",
216
+ "subagent/catalog-diagnostic"
217
+ ]);
218
+ /** True when the error is a host subagent-ownership rejection (see the code set). */
219
+ function isSubagentRejection(err) {
220
+ return err instanceof DshRpcError && SUBAGENT_REJECTION_CODES.has(err.code);
221
+ }
222
+ /** Error body for api-dsh routes: machine code + user-facing message.
223
+ * DshRpcError details ride along when the host sent them — the native
224
+ * picker fallback branches on details.capability. */
225
+ function mapRpcFailure(err) {
226
+ if (err instanceof DshRpcError) return {
227
+ ok: false,
228
+ error: {
229
+ code: err.code,
230
+ message: err.message,
231
+ ...err.details !== void 0 ? { details: err.details } : {}
232
+ }
233
+ };
234
+ return {
235
+ ok: false,
236
+ error: {
237
+ code: "host-unreachable",
238
+ message: err instanceof Error ? err.message : String(err)
239
+ }
240
+ };
241
+ }
242
+ /**
243
+ * Wave 2 (task 2.1) — the typed prompt-image surface. Raster media types
244
+ * the version-one wire accepts, pinned to the host schema
245
+ * (imageMediaTypeSchema); the server lane owns its own whitelist — client
246
+ * admission (attachment-service) is a different trust boundary and is
247
+ * never imported here.
248
+ */
249
+ var PROMPT_IMAGE_MEDIA_TYPES = [
250
+ "image/png",
251
+ "image/jpeg",
252
+ "image/webp",
253
+ "image/gif"
254
+ ];
255
+ /** Type guard for the wire's raster whitelist (route validation entry). */
256
+ function isPromptImageMediaType(value) {
257
+ return PROMPT_IMAGE_MEDIA_TYPES.includes(value);
258
+ }
259
+ /**
260
+ * Build ordered session.prompt content — images first, text last (the
261
+ * host composer's own order); the text part is omitted when empty, so an
262
+ * attachments-only send is exactly the image parts (DSH sendSession: the
263
+ * text part appears only for non-empty text).
264
+ *
265
+ * @param text - prompt text, sent verbatim as one text block when non-empty.
266
+ * @param images - validated image parts, in draft order.
267
+ * @returns the content array for the session.prompt payload.
268
+ */
269
+ function buildPromptContent(text, images) {
270
+ return [...images, ...text === "" ? [] : [{
271
+ type: "text",
272
+ text
273
+ }]];
274
+ }
275
+ /**
276
+ * Parse + validate a server-response envelope against the expected rpcId.
277
+ * Throws: DshRpcError when the result is ok:false; Error on envelope/rpcId mismatch.
278
+ */
279
+ function parseResponse(raw, expectedRpcId) {
280
+ let parsed;
281
+ try {
282
+ parsed = JSON.parse(raw);
283
+ } catch (cause) {
284
+ throw new Error(`dsh-rpc: response is not JSON: ${String(cause)}`);
285
+ }
286
+ if (parsed?.type !== "server-response") throw new Error(`dsh-rpc: expected server-response, got ${JSON.stringify(parsed?.type)}`);
287
+ if (parsed.rpcId !== expectedRpcId) throw new Error(`dsh-rpc: rpcId mismatch — sent ${expectedRpcId}, got ${parsed.rpcId}`);
288
+ const result = parsed.result;
289
+ if (!result || typeof result !== "object" || !("ok" in result)) throw new Error("dsh-rpc: response result is not an RpcResult");
290
+ if (result.ok) return result.value;
291
+ const err = result.error;
292
+ throw new DshRpcError(err.code, err.message, err.details);
293
+ }
294
+ /** Build the RPC endpoint URL: POST {base}/api/{method} (rpc-map.ts key = wire path). */
295
+ function rpcUrl(base, method) {
296
+ return `${(base ?? dshBaseUrl()).replace(/\/+$/, "")}/api/${method}`;
297
+ }
298
+ /**
299
+ * DshAuth — the 0.1.2 cookie carrier (server-side singleton state).
300
+ *
301
+ * The upgraded host requires a signed session cookie (30d, HttpOnly,
302
+ * SameSite=Strict, authority-bound) on every /api call and stream; the cookie
303
+ * is minted by ONE GET / with the launch token (the `?token=*** URL `dsh web`
304
+ * prints). The token is per-process; the minted cookie survives host
305
+ * restarts, so the exchange runs once and again only on a 401 (fresh host
306
+ * process). Single-flight: concurrent 401s share one mint.
307
+ */
308
+ var DshAuth = class {
309
+ cookie = null;
310
+ minting;
311
+ baseUrl;
312
+ fetchFn;
313
+ log;
314
+ constructor(opts = {}) {
315
+ this.baseUrl = opts.baseUrl ?? dshBaseUrl();
316
+ this.fetchFn = opts.fetchFn ?? fetch;
317
+ this.log = opts.log ?? (() => {});
318
+ }
319
+ /** Current cookie pair, or null before the first successful mint. */
320
+ current() {
321
+ return this.cookie;
322
+ }
323
+ /**
324
+ * Ensure a cookie exists: reuse the minted one, else exchange the operator
325
+ * token from readDshAuthConfig. No token configured → null (hosts without
326
+ * the 0.1.2 fence never send 401 and never reach here).
327
+ */
328
+ async ensureCookie() {
329
+ if (this.cookie !== null) return this.cookie;
330
+ this.minting ??= this.mint();
331
+ try {
332
+ return await this.minting;
333
+ } finally {
334
+ this.minting = void 0;
335
+ }
336
+ }
337
+ /** Forget the cookie (next call re-mints — a 401 means a new host process). */
338
+ invalidate() {
339
+ this.cookie = null;
340
+ }
341
+ /** Cookie header value for fetch/WS calls; undefined before any mint. */
342
+ cookieHeader() {
343
+ return this.cookie ?? void 0;
344
+ }
345
+ async mint() {
346
+ const { authToken } = readDshAuthConfig();
347
+ if (authToken === null) {
348
+ this.log("dsh-auth: no token configured — calling the host unauthenticated");
349
+ return null;
350
+ }
351
+ const url = new URL(this.baseUrl);
352
+ url.pathname = "/";
353
+ url.search = "";
354
+ const exchange = `${url.origin}${url.pathname}?token=${encodeURIComponent(authToken)}`;
355
+ try {
356
+ const response = await this.fetchFn(exchange, { redirect: "manual" });
357
+ const setCookie = response.headers.get("set-cookie");
358
+ if (response.status === 303 && setCookie !== null) {
359
+ this.cookie = setCookie.split(";")[0] ?? null;
360
+ this.log("dsh-auth: cookie minted");
361
+ return this.cookie;
362
+ }
363
+ this.log(`dsh-auth: exchange failed (HTTP ${String(response.status)}) — stale token?`);
364
+ return null;
365
+ } catch (err) {
366
+ this.log("dsh-auth: exchange transport failure", err);
367
+ return null;
368
+ }
369
+ }
370
+ };
371
+ //#endregion
372
+ //#region src/lib/server/dsh-connection.ts
373
+ /**
374
+ * dsh-connection — the transport singleton (BC-5) owning every wire byte to
375
+ * the DSH Host (BC-6: it speaks only through dsh-rpc helpers and the two
376
+ * downlink WebSocket paths).
377
+ *
378
+ * State model (PRD §3.5):
379
+ * mux frames (session/subscribed, session/event) → per-session ring buffer
380
+ * host frames (host/session-status) → per-session running map
381
+ * running map re-baselines from session.list at every generation open
382
+ * (2026-08-26): the host pushes status only on TRANSITIONS and sends no
383
+ * initial snapshot, so a downlink rebuilt mid-turn would otherwise never
384
+ * learn the session was already running.
385
+ * ring buffer = freshness only; the ledger (session.history) is the truth —
386
+ * cold loads and every resync re-read the ledger (BC-4).
387
+ *
388
+ * Generation semantics (guide Part 1): the two downlinks form one generation;
389
+ * if either dies, both are rebuilt after backoff.
390
+ */
391
+ /** Live capacity from ~/.dsi/settings.yaml (no cache — runtime edits
392
+ * apply, same contract as every other config section). */
393
+ function ringCapacity() {
394
+ return readServerConfig().ringCapacity;
395
+ }
396
+ var singleton;
397
+ /** The process-wide transport (BC-5). Creates lazily on first use. */
398
+ function getDshConnection(opts = {}) {
399
+ singleton ??= new DshConnection(opts);
400
+ return singleton;
401
+ }
402
+ var DshConnection = class {
403
+ baseUrl;
404
+ fetchFn;
405
+ wsFactory;
406
+ auth;
407
+ rpcIdFactory;
408
+ backoff;
409
+ log;
410
+ buffers = /* @__PURE__ */ new Map();
411
+ running = /* @__PURE__ */ new Map();
412
+ /**
413
+ * Live assistant-stream tails (0.1.3-alpha.1): the one in-flight model
414
+ * attempt per session, folded from the follow's seq-less assistant-stream
415
+ * frames. Presentation state only — never part of the durable cursor
416
+ * algebra (lastSeq/gap/coverage), because the frames carry no seq.
417
+ */
418
+ liveTails = /* @__PURE__ */ new Map();
419
+ /**
420
+ * Session-ids that received a host/session-status push since the running
421
+ * re-baseline RPC was ISSUED (2026-08-26). A push is a strictly newer
422
+ * transition than an in-flight snapshot — the seed must never overwrite
423
+ * one. Swapped for a fresh set every time a seed is issued; read at
424
+ * apply time so pushes landing mid-flight are counted.
425
+ */
426
+ runningSeedRaces = /* @__PURE__ */ new Set();
427
+ wsMux;
428
+ generation = 0;
429
+ /** Logical streams by streamId → owning socket (0.1.2 remote.mux). */
430
+ streamSockets = /* @__PURE__ */ new Map();
431
+ /** Generation-scoped counter keeping stream ids unique across rebuilds. */
432
+ streamSeq = 0;
433
+ /** $events generation clientId (RemoteEventReadyFrame; binds $events/result). */
434
+ eventClientId = null;
435
+ /** True once the carrier's OPEN event fired (sends are legal). */
436
+ opened = false;
437
+ /** Per-session projection baselines (follow snapshot + control frames; W2). */
438
+ projections = /* @__PURE__ */ new Map();
439
+ /** Workspace registry cache from workspace/follow (W2; authority = host). */
440
+ workspaces = [];
441
+ /** Waiters resolved by the first workspace/follow baseline (one-shot). */
442
+ workspaceBaselineWaiters = /* @__PURE__ */ new Set();
443
+ /** Subscribes parked while the carrier is CONNECTING (flushed on open). */
444
+ pendingSubscribes = /* @__PURE__ */ new Set();
445
+ /**
446
+ * Parent-address hints for subagent-origin sessions (SubagentAddressHint),
447
+ * folded from session.list rows by rawListSessions.
448
+ */
449
+ subagentHints = /* @__PURE__ */ new Map();
450
+ /** Session-ids whose hint absence was already probed with a list pass
451
+ * (one probe per session lifetime — ordinary sessions never re-probe). */
452
+ hintProbed = /* @__PURE__ */ new Set();
453
+ reconnectTimer;
454
+ disposed = false;
455
+ /** Wave 4.2 — consecutive failed generations; drives the backoff ladder. */
456
+ reconnectAttempt = 0;
457
+ /** Reap grace (see DshConnectionOptions.answerReapGraceMs). */
458
+ answerReapGraceMs;
459
+ answerReapTimer;
460
+ /** Stale-generation retry deadline (see DshConnectionOptions.staleGenerationRetryMs). */
461
+ staleGenerationRetryMs;
462
+ constructor(opts = {}) {
463
+ this.baseUrl = opts.baseUrl ?? dshBaseUrl();
464
+ this.fetchFn = opts.fetchFn ?? fetch;
465
+ this.wsFactory = opts.wsFactory ?? ((url, headers) => new WebSocket(url, headers === void 0 ? void 0 : { headers }));
466
+ this.auth = opts.auth ?? new DshAuth({
467
+ baseUrl: this.baseUrl,
468
+ log: opts.log
469
+ });
470
+ this.rpcIdFactory = opts.rpcIdFactory ?? (() => crypto.randomUUID());
471
+ this.backoff = opts.backoffScheduleMs ?? [
472
+ 1e3,
473
+ 2e3,
474
+ 5e3,
475
+ 1e4,
476
+ 3e4
477
+ ];
478
+ this.answerReapGraceMs = opts.answerReapGraceMs ?? 3e3;
479
+ this.staleGenerationRetryMs = opts.staleGenerationRetryMs ?? 5e3;
480
+ this.log = opts.log ?? (() => {});
481
+ }
482
+ /**
483
+ * Open the stream generation (idempotent): ONE cookie-authenticated
484
+ * remote.mux socket (0.1.2 — events.mux/events.host are gone), with every
485
+ * subscribed session's `session/follow` reopened as a logical stream plus
486
+ * the $events forwarded-event stream (approvals/questions).
487
+ */
488
+ ensureDownlinks() {
489
+ if (this.disposed || this.wsMux) return;
490
+ ++this.generation;
491
+ this.log(`dsh-connection: opening remote.mux generation ${this.generation}`);
492
+ this.wsMux = this.openDownlink((socket) => this.onMuxOpen(socket));
493
+ this.seedRunning();
494
+ }
495
+ openDownlink(attach) {
496
+ const url = this.baseUrl.replace(/^http/, "ws") + DSH_REMOTE_MUX_WS_PATH;
497
+ const headers = this.auth.cookieHeader();
498
+ const socket = this.wsFactory(url, headers === void 0 ? void 0 : { cookie: headers });
499
+ this.auth.ensureCookie().catch(() => null);
500
+ socket.addEventListener("message", (e) => {
501
+ try {
502
+ this.onStreamFrame(JSON.parse(String(e.data)));
503
+ } catch (err) {
504
+ this.log("dsh-connection: dropping malformed frame", err);
505
+ }
506
+ });
507
+ socket.addEventListener("open", () => {
508
+ this.opened = true;
509
+ attach(socket);
510
+ });
511
+ socket.addEventListener("close", () => this.onDownlinkClosed());
512
+ socket.addEventListener("error", () => this.onDownlinkClosed());
513
+ return socket;
514
+ }
515
+ /** mux open: (re)open every subscribed session's follow stream + $events. */
516
+ onMuxOpen(socket) {
517
+ this.streamSeq += 1;
518
+ this.pendingSubscribes.clear();
519
+ for (const sessionId of this.buffers.keys()) this.openFollowWithProbe(socket, sessionId);
520
+ this.openEventStream(socket);
521
+ this.openStateStream(socket, `workspaces-${String(this.streamSeq)}`, "workspace/follow");
522
+ this.openStateStream(socket, `control-${String(this.streamSeq)}`, "session/control");
523
+ }
524
+ /** Open one state stream (workspace/follow | session/control) with empty args. */
525
+ openStateStream(socket, streamId, endpoint) {
526
+ this.sendStreamOpen(socket, streamId, endpoint, { args: {} });
527
+ }
528
+ openFollowStream(socket, sessionId) {
529
+ this.sendStreamOpen(socket, followStreamId(sessionId, this.streamSeq), "session/follow", { args: { request: {
530
+ address: this.addressFor(sessionId),
531
+ maxMessages: 200,
532
+ assistantStream: true
533
+ } } });
534
+ }
535
+ openEventStream(socket) {
536
+ this.sendStreamOpen(socket, `events-${String(this.streamSeq)}`, REMOTE_EVENT_STREAM_ENDPOINT, REMOTE_EVENT_STREAM_PAYLOAD);
537
+ }
538
+ sendStreamOpen(socket, streamId, endpoint, payload) {
539
+ const send = (msg) => socket.send?.(msg);
540
+ this.streamSockets.set(streamId, socket);
541
+ send(JSON.stringify({
542
+ type: "open",
543
+ streamId,
544
+ endpoint,
545
+ payload
546
+ }));
547
+ }
548
+ /** Either socket dying rebuilds the whole generation (guide Part 1). */
549
+ onDownlinkClosed() {
550
+ if (this.disposed) return;
551
+ if (!this.wsMux) return;
552
+ this.log("dsh-connection: remote.mux died — rebuilding generation");
553
+ this.fireCatalogInvalidated("all");
554
+ this.teardownSockets();
555
+ this.scheduleReconnect();
556
+ }
557
+ scheduleReconnect() {
558
+ if (this.disposed) return;
559
+ const rung = Math.min(this.reconnectAttempt, this.backoff.length - 1);
560
+ const delay = this.backoff[rung];
561
+ if (this.reconnectTimer !== void 0) clearTimeout(this.reconnectTimer);
562
+ this.reconnectTimer = setTimeout(() => {
563
+ if (this.disposed) return;
564
+ this.reconnectAttempt += 1;
565
+ this.ensureDownlinks();
566
+ }, delay);
567
+ }
568
+ teardownSockets() {
569
+ const sockets = [this.wsMux];
570
+ this.wsMux = void 0;
571
+ this.opened = false;
572
+ this.streamSockets.clear();
573
+ this.projections.clear();
574
+ this.workspaces = [];
575
+ for (const waiter of this.workspaceBaselineWaiters) waiter.resolve();
576
+ this.workspaceBaselineWaiters.clear();
577
+ if (this.reconnectTimer !== void 0) {
578
+ clearTimeout(this.reconnectTimer);
579
+ this.reconnectTimer = void 0;
580
+ }
581
+ for (const socket of sockets) socket?.close();
582
+ }
583
+ /**
584
+ * 0.1.2 remote.mux demux: `{item,end,error,streamId}` frames. Follow
585
+ * streams deliver a `snapshot` opening (seeds the baseline) then event
586
+ * entries (ring buffer appends); the $events stream delivers forwarded
587
+ * Cordis events (approval/question waterfalls → answerer registry).
588
+ */
589
+ onStreamFrame(payload) {
590
+ const frame = payload;
591
+ if (typeof frame?.streamId !== "string") return;
592
+ if (frame.type === "item" && frame.value !== void 0) {
593
+ this.reconnectAttempt = 0;
594
+ const value = frame.value;
595
+ if (frame.streamId.startsWith("follow-")) this.onFollowItem(frame.streamId, value);
596
+ else if (frame.streamId.startsWith("events-")) this.onEventItem(value);
597
+ else if (frame.streamId.startsWith("workspaces-")) this.onWorkspaceItem(value);
598
+ else if (frame.streamId.startsWith("control-")) this.onControlItem(value);
599
+ return;
600
+ }
601
+ if (frame.type === "error") this.log(`dsh-connection: stream ${frame.streamId} failed`, frame.error?.code, frame.error?.message);
602
+ this.streamSockets.delete(frame.streamId);
603
+ }
604
+ /** session/follow item: snapshot seeds the baseline; events append; the
605
+ * opted-in assistant-stream frames update the live tail (seq-less —
606
+ * they never touch the durable cursor bookkeeping below). */
607
+ onFollowItem(streamId, value) {
608
+ if (value.type === "snapshot") {
609
+ const sessionId = this.sessionIdOfStream(streamId);
610
+ if (sessionId === null) return;
611
+ const snapshot = value;
612
+ const events = expandRecords(snapshot.records ?? []);
613
+ for (const event of events) this.appendEvent(sessionId, event);
614
+ this.seedSnapshotCursor(sessionId, snapshot.cursor);
615
+ if (snapshot.projections !== null && typeof snapshot.projections === "object") this.projections.set(sessionId, snapshot.projections);
616
+ this.seedLiveTailFromBaseline(sessionId, snapshot.assistantStream);
617
+ return;
618
+ }
619
+ if (value.type === "assistant-stream") {
620
+ const sessionId = this.sessionIdOfStream(streamId);
621
+ const frame = value.frame;
622
+ if (sessionId !== null && frame !== void 0) this.onAssistantStreamFrame(sessionId, frame);
623
+ return;
624
+ }
625
+ if (value.type === "event") {
626
+ const sessionId = this.sessionIdOfStream(streamId);
627
+ const entry = value;
628
+ if (sessionId !== null && entry.event !== void 0) for (const event of expandEntry(entry.event)) this.appendEvent(sessionId, event);
629
+ }
630
+ }
631
+ /** $events item: forwarded Cordis event frames (waterfall = answerable). */
632
+ onEventItem(value) {
633
+ if (value.type === "ready") {
634
+ if (typeof value.clientId === "string") this.eventClientId = value.clientId;
635
+ this.log("dsh-connection: $events stream ready");
636
+ this.armAnswerReap();
637
+ return;
638
+ }
639
+ if (value.type === "waterfall") {
640
+ const event = typeof value.event === "string" ? value.event : "";
641
+ const eventId = typeof value.eventId === "string" ? value.eventId : void 0;
642
+ const sessionId = typeof value.agentId === "string" ? value.agentId : void 0;
643
+ const request = value.request !== null && typeof value.request === "object" ? value.request : {};
644
+ if (eventId === void 0) return;
645
+ if (event === "approval/request") {
646
+ this.registerPending({
647
+ type: "approval/requested",
648
+ sessionId: sessionId ?? "",
649
+ ...request
650
+ }, eventId);
651
+ return;
652
+ }
653
+ if (event === "user-questions/request") {
654
+ this.registerPending({
655
+ type: "question/requested",
656
+ sessionId: sessionId ?? "",
657
+ ...request
658
+ }, eventId);
659
+ return;
660
+ }
661
+ this.log("dsh-connection: forwarded waterfall ignored", event);
662
+ return;
663
+ }
664
+ if (value.type === "cancel") {
665
+ const eventId = typeof value.eventId === "string" ? value.eventId : void 0;
666
+ if (eventId !== void 0) this.withdrawPending(eventId);
667
+ this.log("dsh-connection: forwarded waterfall cancelled");
668
+ return;
669
+ }
670
+ if (value.type === "settle") {
671
+ const eventId = typeof value.eventId === "string" ? value.eventId : void 0;
672
+ const outcome = typeof value.outcome === "string" ? value.outcome : "unknown";
673
+ if (eventId !== void 0) this.withdrawPending(eventId, outcome);
674
+ return;
675
+ }
676
+ if (value.type === "emit") {
677
+ if (value.event === "api-session/status" && Array.isArray(value.args)) {
678
+ const [sessionId, running] = value.args;
679
+ if (typeof sessionId === "string") this.running.set(sessionId, running === true);
680
+ return;
681
+ }
682
+ if (value.event === "commands/change") {
683
+ this.fireCatalogInvalidated("all");
684
+ return;
685
+ }
686
+ if (value.event === "agent-preset/selected" && typeof value.agentId === "string") {
687
+ this.fireCatalogInvalidated(value.agentId);
688
+ return;
689
+ }
690
+ this.log("dsh-connection: forwarded emit ignored", value.event);
691
+ return;
692
+ }
693
+ }
694
+ /**
695
+ * workspace/follow item (W2): baseline replaces the cache; increments
696
+ * apply in order (upsert/remove/order/archived — archived sessions are
697
+ * hidden from DSI's surfaces the same way the 0.1.1 registry did).
698
+ */
699
+ onWorkspaceItem(value) {
700
+ if (value.type === "baseline") {
701
+ const baseline = value.value ?? {};
702
+ const rows = Array.isArray(baseline.items) ? baseline.items : [];
703
+ this.workspaces = rows.flatMap((row) => {
704
+ const w = row;
705
+ if (typeof w?.workspaceId !== "string" || typeof w?.path !== "string") return [];
706
+ return [{
707
+ workspaceId: w.workspaceId,
708
+ title: typeof w.title === "string" && w.title.length > 0 ? w.title : w.path,
709
+ path: w.path,
710
+ sessionIds: Array.isArray(w.sessionIds) ? w.sessionIds.filter((id) => typeof id === "string") : []
711
+ }];
712
+ });
713
+ for (const waiter of this.workspaceBaselineWaiters) waiter.resolve();
714
+ this.workspaceBaselineWaiters.clear();
715
+ this.log(`dsh-connection: workspace baseline (${this.workspaces.length} rows)`);
716
+ return;
717
+ }
718
+ if (value.type === "upsert") {
719
+ const w = value.workspace;
720
+ if (typeof w?.workspaceId !== "string" || typeof w?.path !== "string") return;
721
+ const row = {
722
+ workspaceId: w.workspaceId,
723
+ title: typeof w.title === "string" && w.title.length > 0 ? w.title : w.path,
724
+ path: w.path,
725
+ sessionIds: Array.isArray(w.sessionIds) ? w.sessionIds.filter((id) => typeof id === "string") : []
726
+ };
727
+ const idx = this.workspaces.findIndex((x) => x.workspaceId === row.workspaceId);
728
+ if (idx >= 0) this.workspaces.splice(idx, 1, row);
729
+ else this.workspaces.push(row);
730
+ return;
731
+ }
732
+ if (value.type === "remove" && typeof value.workspaceId === "string") {
733
+ this.workspaces = this.workspaces.filter((x) => x.workspaceId !== value.workspaceId);
734
+ return;
735
+ }
736
+ }
737
+ /**
738
+ * session/control item (W2): the baseline's per-session projections seed
739
+ * the store (permissions for the access chip); later projection frames
740
+ * update single keys. queue/job frames are not DSI state (ignored).
741
+ */
742
+ onControlItem(value) {
743
+ if (value.type === "baseline") {
744
+ const baseline = value.value ?? {};
745
+ for (const [sessionId, block] of Object.entries(baseline.projections ?? {})) if (block !== null && typeof block === "object") this.projections.set(sessionId, block);
746
+ this.log(`dsh-connection: control baseline (${this.projections.size} sessions)`);
747
+ return;
748
+ }
749
+ if (value.type === "projection" && typeof value.sessionId === "string" && typeof value.key === "string") {
750
+ const block = this.projections.get(value.sessionId) ?? {
751
+ asOfSeq: 0,
752
+ values: {}
753
+ };
754
+ this.projections.set(value.sessionId, {
755
+ asOfSeq: typeof value.seq === "number" ? value.seq : block.asOfSeq,
756
+ values: {
757
+ ...block.values,
758
+ [value.key]: value.value
759
+ }
760
+ });
761
+ }
762
+ }
763
+ /** Extract the sessionId from a follow-<sessionId>-<seq> stream id. */
764
+ sessionIdOfStream(streamId) {
765
+ const rest = streamId.slice(7);
766
+ const cut = rest.lastIndexOf("-");
767
+ return cut > 0 ? rest.slice(0, cut) : null;
768
+ }
769
+ pendingAnswers = /* @__PURE__ */ new Map();
770
+ /** Settlement ring: last outcome per rpcId, delivered-then-pruned. */
771
+ settledAnswers = /* @__PURE__ */ new Map();
772
+ /** Catalog-invalidation registry (Slash Menu W1) — listeners see every arm. */
773
+ catalogInvalidated = /* @__PURE__ */ new Set();
774
+ /** Subscribe to catalog invalidations ($events arms + socket reset). */
775
+ onCatalogInvalidated(listener) {
776
+ this.catalogInvalidated.add(listener);
777
+ return () => this.catalogInvalidated.delete(listener);
778
+ }
779
+ fireCatalogInvalidated(scope) {
780
+ for (const listener of this.catalogInvalidated) listener(scope);
781
+ }
782
+ onMuxFrame(payload, rpcId) {
783
+ this.reconnectAttempt = 0;
784
+ const frame = payload;
785
+ switch (frame?.type) {
786
+ case "session/subscribed":
787
+ if (typeof frame.sessionId === "string") this.seedBaseline(frame.sessionId, frame.lastSeq ?? -1);
788
+ return;
789
+ case "session/event":
790
+ if (frame.sessionId && frame.event) this.appendEvent(frame.sessionId, frame.event);
791
+ return;
792
+ case "approval/requested":
793
+ case "question/requested":
794
+ this.registerPending(frame, rpcId);
795
+ return;
796
+ case "approval/resolved":
797
+ case "question/resolved":
798
+ this.registerSettlement(frame);
799
+ return;
800
+ default: this.log("dsh-connection: mux frame ignored", frame?.type);
801
+ }
802
+ }
803
+ onHostFrame(payload) {
804
+ const frame = payload;
805
+ if (frame?.type === "host/session-status" && typeof frame.sessionId === "string") {
806
+ this.runningSeedRaces.add(frame.sessionId);
807
+ this.running.set(frame.sessionId, Boolean(frame.running));
808
+ }
809
+ }
810
+ /**
811
+ * 2026-08-26 running re-baseline: DSH pushes host/session-status only on
812
+ * transitions and the host stream sends no initial snapshot on (re)open —
813
+ * a downlink rebuilt mid-turn never sees the running:true flip that
814
+ * happened before it existed (the live bug: events kept flowing on the
815
+ * mux while the streaming indicator said idle). session.list is the host's
816
+ * documented reconnect baseline, so every generation opens with ONE list
817
+ * RPC seeding the whole map — which also prunes entries for sessions that
818
+ * died while disconnected. Pushes that land while the RPC is in flight win
819
+ * over the snapshot (they are strictly newer transitions); a seed from a
820
+ * superseded generation is dropped entirely.
821
+ */
822
+ async seedRunning() {
823
+ const generation = this.generation;
824
+ this.runningSeedRaces = /* @__PURE__ */ new Set();
825
+ try {
826
+ const { items } = await this.listSessions();
827
+ if (this.disposed || generation !== this.generation) return;
828
+ const races = this.runningSeedRaces;
829
+ const next = /* @__PURE__ */ new Map();
830
+ for (const [id, running] of this.running) if (races.has(id)) next.set(id, running);
831
+ for (const row of items) if (!races.has(row.sessionId)) next.set(row.sessionId, row.running);
832
+ this.running = next;
833
+ this.log(`dsh-connection: running map re-baselined (${items.length} sessions)`);
834
+ } catch (err) {
835
+ this.log("dsh-connection: running re-baseline failed — pushes stay authoritative", err);
836
+ }
837
+ }
838
+ bufferFor(sessionId) {
839
+ let buf = this.buffers.get(sessionId);
840
+ if (!buf) {
841
+ buf = {
842
+ entries: [],
843
+ lastSeq: -1,
844
+ coverageFloor: -1
845
+ };
846
+ this.buffers.set(sessionId, buf);
847
+ }
848
+ return buf;
849
+ }
850
+ seedBaseline(sessionId, lastSeq) {
851
+ const buf = this.bufferFor(sessionId);
852
+ if (lastSeq > buf.lastSeq && buf.entries.length === 0) {
853
+ buf.lastSeq = lastSeq;
854
+ buf.coverageFloor = lastSeq;
855
+ return;
856
+ }
857
+ if (buf.lastSeq >= 0 && lastSeq < buf.lastSeq) {
858
+ buf.entries.length = 0;
859
+ buf.lastSeq = lastSeq;
860
+ buf.coverageFloor = lastSeq;
861
+ }
862
+ }
863
+ /**
864
+ * Snapshot cursor seed (0.1.2). A snapshot BELOW the buffered high-water
865
+ * mark means the upstream was rebuilt (stream restart): stale entries
866
+ * belong to the dead instance — reset the buffer to the snapshot truth
867
+ * (Wave 2 parity with the 0.1.1 rewound-baseline semantics).
868
+ */
869
+ seedSnapshotCursor(sessionId, cursor) {
870
+ const buf = this.bufferFor(sessionId);
871
+ if (cursor > buf.lastSeq) {
872
+ buf.lastSeq = cursor;
873
+ buf.coverageFloor = cursor;
874
+ return;
875
+ }
876
+ if (buf.lastSeq >= 0 && cursor < buf.lastSeq) {
877
+ buf.entries.length = 0;
878
+ buf.lastSeq = cursor;
879
+ buf.coverageFloor = cursor;
880
+ }
881
+ }
882
+ appendEvent(sessionId, event) {
883
+ const buf = this.bufferFor(sessionId);
884
+ if (event.seq > buf.lastSeq) {
885
+ if (buf.lastSeq >= 0 && event.seq > buf.lastSeq + 1) {
886
+ buf.coverageFloor = Math.max(buf.coverageFloor, buf.lastSeq);
887
+ this.log(`dsh-connection: seq gap for ${sessionId} (${buf.lastSeq} → ${event.seq}) — resync required below ${event.seq}`);
888
+ }
889
+ buf.lastSeq = event.seq;
890
+ buf.entries.push(event);
891
+ const cap = ringCapacity();
892
+ if (buf.entries.length > cap) buf.entries.splice(0, buf.entries.length - cap);
893
+ }
894
+ }
895
+ /**
896
+ * Fold one assistant-stream frame into the session's live tail. Seq-less
897
+ * presentation state: never touches lastSeq/coverage — the durable
898
+ * cursor algebra stays event-entry-only. The host keeps ONE active
899
+ * attempt per session and drops its own accumulator on any continuity
900
+ * break (foreign attemptId, dense-index gap), so mirrors do the same.
901
+ */
902
+ onAssistantStreamFrame(sessionId, frame) {
903
+ if (frame.type === "start") {
904
+ this.liveTails.set(sessionId, {
905
+ attemptId: frame.attemptId,
906
+ turn: frame.turn,
907
+ step: frame.step,
908
+ startedAfterSeq: frame.startedAfterSeq,
909
+ startedAt: Date.now(),
910
+ lastTime: Date.now(),
911
+ text: "",
912
+ reasoning: "",
913
+ nextIndex: 0
914
+ });
915
+ return;
916
+ }
917
+ const tail = this.liveTails.get(sessionId);
918
+ if (tail === void 0) return;
919
+ if (frame.type === "chunk") {
920
+ if (frame.attemptId !== tail.attemptId || frame.index !== tail.nextIndex) {
921
+ this.liveTails.delete(sessionId);
922
+ return;
923
+ }
924
+ const kind = frame.chunk?.type;
925
+ if (kind === "text-delta" && typeof frame.chunk?.text === "string") tail.text += frame.chunk.text;
926
+ else if (kind === "reasoning-delta" && typeof frame.chunk?.text === "string") tail.reasoning += frame.chunk.text;
927
+ tail.nextIndex += 1;
928
+ if (typeof frame.time === "number") tail.lastTime = frame.time;
929
+ return;
930
+ }
931
+ this.liveTails.delete(sessionId);
932
+ }
933
+ /**
934
+ * Seed the live tail from a follow-opening snapshot's assistantStream
935
+ * baseline (mux-drop reopen mid-attempt): expand the compact stream
936
+ * records' text/reasoning runs into the tail. No activeAttempt → clear
937
+ * any remembered tail — the fresh opening is authoritative for what is
938
+ * still in flight.
939
+ */
940
+ seedLiveTailFromBaseline(sessionId, baseline) {
941
+ const active = baseline?.activeAttempt;
942
+ if (active === void 0) {
943
+ this.liveTails.delete(sessionId);
944
+ return;
945
+ }
946
+ const tail = {
947
+ attemptId: active.attemptId,
948
+ turn: active.turn,
949
+ step: active.step,
950
+ startedAfterSeq: active.startedAfterSeq,
951
+ startedAt: Date.now(),
952
+ lastTime: Date.now(),
953
+ text: "",
954
+ reasoning: "",
955
+ nextIndex: active.nextIndex
956
+ };
957
+ for (const record of active.stream ?? []) {
958
+ if (record.type === "text-chunks" || record.type === "reasoning-chunks") {
959
+ const texts = record.texts;
960
+ const joined = Array.isArray(texts) ? texts.filter((t) => typeof t === "string").join("") : "";
961
+ if (record.type === "text-chunks") tail.text += joined;
962
+ else tail.reasoning += joined;
963
+ }
964
+ const times = compactRecordTailTime(record);
965
+ if (times !== void 0) tail.lastTime = times;
966
+ }
967
+ this.liveTails.set(sessionId, tail);
968
+ }
969
+ /**
970
+ * The session's live tail for the events poll, or null when no attempt
971
+ * is in flight. Served with the FULL accumulated text each poll — the
972
+ * client replaces its streaming bubble wholesale (no cross-poll fragment
973
+ * bookkeeping, so reconnects/retries converge from the served truth).
974
+ */
975
+ liveAssistantStream(sessionId) {
976
+ const tail = this.liveTails.get(sessionId);
977
+ if (tail === void 0) return null;
978
+ return {
979
+ id: `a:${String(tail.turn)}:${String(tail.step)}`,
980
+ turn: tail.turn,
981
+ step: tail.step,
982
+ seq: tail.startedAfterSeq,
983
+ time: tail.lastTime,
984
+ text: tail.text,
985
+ reasoning: tail.reasoning
986
+ };
987
+ }
988
+ /**
989
+ * Delta slice: events with seq > n, plus current lastSeq/running.
990
+ * Wave 4.2: `gap` is true when the caller's `since` mark fell into a hole
991
+ * the buffer no longer covers (mux drop). The response then carries the
992
+ * resync flag so the client re-reads from the ledger instead of trusting
993
+ * a partial delta (BC-4).
994
+ */
995
+ eventsSince(sessionId, n) {
996
+ const buf = this.bufferFor(sessionId);
997
+ const events = buf.entries.filter((e) => e.seq > n);
998
+ const firstDelta = events[0]?.seq;
999
+ const gap = firstDelta !== void 0 && firstDelta > n + 1;
1000
+ return {
1001
+ events,
1002
+ lastSeq: buf.lastSeq,
1003
+ running: this.running.get(sessionId) ?? false,
1004
+ gap
1005
+ };
1006
+ }
1007
+ /**
1008
+ * Subscribe one session (0.1.2): create its buffer and open a
1009
+ * session/follow logical stream on the live carrier (or mark it for the
1010
+ * next generation). Idempotent; the snapshot frame seeds the baseline.
1011
+ * A repeat call while THIS generation already holds the session's follow
1012
+ * stream is a no-op — the api-dsh events route calls subscribe on every
1013
+ * poll, and reopening would replace the stream (same streamId → fresh
1014
+ * snapshot) on every tick. A stream 'end'/'error' frame drops the
1015
+ * tracking entry so the next poll reopens and self-heals.
1016
+ */
1017
+ subscribe(sessionId) {
1018
+ this.bufferFor(sessionId);
1019
+ const socket = this.wsMux;
1020
+ if (socket === void 0) return;
1021
+ if (this.opened === false) {
1022
+ this.pendingSubscribes.add(sessionId);
1023
+ return;
1024
+ }
1025
+ if (this.streamSockets.has(followStreamId(sessionId, this.streamSeq))) return;
1026
+ this.openFollowWithProbe(socket, sessionId);
1027
+ }
1028
+ /**
1029
+ * Open the follow stream with the best-known address NOW (sync, the
1030
+ * historical contract), then — when this turns out to be a subagent child
1031
+ * nobody has folded yet — probe and REOPEN with the parent address (the
1032
+ * mux replaces a stream id; the fresh snapshot reseeds the baseline).
1033
+ */
1034
+ openFollowWithProbe(socket, sessionId) {
1035
+ this.openFollowStream(socket, sessionId);
1036
+ if (!this.subagentHints.has(sessionId) && !this.hintProbed.has(sessionId)) {
1037
+ this.hintProbed.add(sessionId);
1038
+ this.probeHintThenSubscribe(sessionId, socket);
1039
+ }
1040
+ }
1041
+ /** Resolve one session's address hint after a plain-form follow went out;
1042
+ * on a folded hint REOPEN the follow with the parent address (a plain
1043
+ * form on a subagent child is rejected by the host and the stream would
1044
+ * stay dead for the whole generation). Skipped when the carrier moved on
1045
+ * (generation rebuilt / disposed) during the list RPC — onMuxOpen
1046
+ * reopens every buffer with the then-current addresses. */
1047
+ async probeHintThenSubscribe(sessionId, socket) {
1048
+ try {
1049
+ if (!this.subagentHints.has(sessionId)) await this.rawListSessions();
1050
+ } catch {}
1051
+ if (this.disposed || this.wsMux !== socket || this.opened === false) return;
1052
+ if (this.subagentHints.has(sessionId)) this.openFollowStream(socket, sessionId);
1053
+ }
1054
+ /**
1055
+ * Per-session projection value (W2): follow-snapshot + control-baseline
1056
+ * + control projection frames, newest wins. permissions (access chip),
1057
+ * modelSelection, todos, plan … — null when never seen.
1058
+ */
1059
+ projectionValue(sessionId, key) {
1060
+ return this.projections.get(sessionId)?.values?.[key];
1061
+ }
1062
+ /** The whole stored block for one session (null when absent). */
1063
+ projectionBlock(sessionId) {
1064
+ return this.projections.get(sessionId) ?? null;
1065
+ }
1066
+ /**
1067
+ * Bounded wait for the session's FIRST projection baseline (0.1.2 cold
1068
+ * loads seed the access mode + admission numbers from the follow/control
1069
+ * store — session/page carries no projections block). Resolves
1070
+ * immediately when a block exists; null after timeoutMs (an unfenced or
1071
+ * slow host degrades to the documented defaults, never a hang).
1072
+ */
1073
+ async waitForProjections(sessionId, timeoutMs = 2500) {
1074
+ this.ensureDownlinks();
1075
+ this.subscribe(sessionId);
1076
+ const deadline = Date.now() + timeoutMs;
1077
+ for (;;) {
1078
+ const block = this.projectionBlock(sessionId);
1079
+ if (block !== null) return block;
1080
+ if (Date.now() >= deadline) return null;
1081
+ await new Promise((resolve) => setTimeout(resolve, 50));
1082
+ }
1083
+ }
1084
+ /**
1085
+ * True once the workspace/follow baseline landed. Registers the waiter
1086
+ * even while the carrier is CONNECTING (the first RPC often races the
1087
+ * handshake); bounded by 3s — an honest empty after that, never a hang.
1088
+ */
1089
+ async awaitWorkspaceBaseline() {
1090
+ if (this.workspaces.length > 0) return true;
1091
+ this.ensureDownlinks();
1092
+ if (this.wsMux === void 0) return false;
1093
+ const waiter = Promise.withResolvers();
1094
+ this.workspaceBaselineWaiters.add(waiter);
1095
+ await Promise.race([waiter.promise, new Promise((r) => setTimeout(r, 3e3))]);
1096
+ this.workspaceBaselineWaiters.delete(waiter);
1097
+ return this.workspaces.length > 0;
1098
+ }
1099
+ /** True when the session has a live baseline (subscribed at least once). */
1100
+ hasBaseline(sessionId) {
1101
+ return this.buffers.has(sessionId) && this.bufferFor(sessionId).lastSeq >= 0;
1102
+ }
1103
+ isRunning(sessionId) {
1104
+ return this.running.get(sessionId) ?? false;
1105
+ }
1106
+ /** approval/requested | question/requested → registry (idempotent by rpcId). */
1107
+ registerPending(frame, rpcId) {
1108
+ const sessionId = typeof frame.sessionId === "string" ? frame.sessionId : "";
1109
+ if (!sessionId) {
1110
+ this.log("dsh-connection: answerable frame without sessionId — ignored", frame?.type);
1111
+ return;
1112
+ }
1113
+ if (typeof rpcId !== "string" || rpcId.length === 0) {
1114
+ this.log("dsh-connection: answerable frame without rpcId — ignored", frame?.type);
1115
+ return;
1116
+ }
1117
+ const kind = frame.type === "approval/requested" ? "approval" : "question";
1118
+ this.pendingAnswers.set(rpcId, {
1119
+ rpcId,
1120
+ sessionId,
1121
+ kind,
1122
+ body: frame,
1123
+ receivedAt: Date.now(),
1124
+ deliveredGen: this.generation
1125
+ });
1126
+ this.log(`dsh-connection: pending ${kind} registered (${rpcId}) for ${sessionId}`);
1127
+ }
1128
+ /**
1129
+ * Withdraw one pending answer (0.1.2 $events settle/cancel): remove the
1130
+ * registry entry and record the outcome in the settlement ring. Unknown
1131
+ * ids settle nothing (answered before we subscribed — honest no-op).
1132
+ */
1133
+ withdrawPending(eventId, outcome) {
1134
+ const pending = this.pendingAnswers.get(eventId);
1135
+ if (pending === void 0) {
1136
+ this.log(`dsh-connection: settlement with no matching pending (${eventId}) — ignored`);
1137
+ return;
1138
+ }
1139
+ this.pendingAnswers.delete(eventId);
1140
+ this.settledAnswers.set(eventId, {
1141
+ rpcId: eventId,
1142
+ sessionId: pending.sessionId,
1143
+ kind: pending.kind,
1144
+ outcome: outcome ?? "cancelled",
1145
+ settledAt: Date.now()
1146
+ });
1147
+ this.log(`dsh-connection: ${pending.kind} settled (${eventId}) outcome=${outcome ?? "cancelled"}`);
1148
+ }
1149
+ /**
1150
+ * Schedule the still-pending replay reap for this generation (ready
1151
+ * frame just arrived). The host replays every still-pending waterfall
1152
+ * into the new generation, but the ready frame can hit the wire before
1153
+ * the replayed frames drain, so the check runs after a short grace —
1154
+ * entries an older generation registered and the replay did NOT
1155
+ * re-confirm finished while we were disconnected (answered, cancelled,
1156
+ * or aborted); keeping them pending resurrects zombie answer cards the
1157
+ * user can answer forever with no effect.
1158
+ */
1159
+ armAnswerReap() {
1160
+ clearTimeout(this.answerReapTimer);
1161
+ this.answerReapTimer = setTimeout(() => this.reapUnconfirmedPending(), this.answerReapGraceMs);
1162
+ }
1163
+ /** Withdraw every pending answer not re-delivered by the current generation. */
1164
+ reapUnconfirmedPending() {
1165
+ for (const [eventId, pending] of [...this.pendingAnswers]) {
1166
+ if (pending.deliveredGen >= this.generation) continue;
1167
+ this.log(`dsh-connection: reap unconfirmed pending (${eventId}) from generation ${pending.deliveredGen} — not in generation ${this.generation} replay`);
1168
+ this.withdrawPending(eventId);
1169
+ }
1170
+ }
1171
+ /**
1172
+ * After OUR answer was accepted the question is done — withdraw the
1173
+ * registry entry with the outcome we sent instead of waiting for the
1174
+ * host's settle broadcast, which races generation rebuilds and can be
1175
+ * lost entirely (the answered card would resurrect on the next page
1176
+ * load from a stale pendingAnswers).
1177
+ */
1178
+ forgetAnswered(rpcId, value) {
1179
+ this.withdrawPending(rpcId, typeof value === "string" ? value : "answered");
1180
+ }
1181
+ /** approval/resolved | question/resolved → pending removed, settlement recorded. */
1182
+ registerSettlement(frame) {
1183
+ const sessionId = typeof frame.sessionId === "string" ? frame.sessionId : "";
1184
+ if (!sessionId) {
1185
+ this.log("dsh-connection: settlement frame without sessionId — ignored", frame?.type);
1186
+ return;
1187
+ }
1188
+ const kind = frame.type === "approval/resolved" ? "approval" : "question";
1189
+ const outcome = typeof frame.outcome === "string" ? frame.outcome : "unknown";
1190
+ let rpcId;
1191
+ if (kind === "approval" && typeof frame.approvalId === "string") rpcId = [...this.pendingAnswers.values()].find((p) => p.sessionId === sessionId && p.body.approvalId === frame.approvalId)?.rpcId;
1192
+ else if (kind === "question" && typeof frame.questionRpcId === "string") rpcId = frame.questionRpcId;
1193
+ if (rpcId) {
1194
+ this.pendingAnswers.delete(rpcId);
1195
+ this.settledAnswers.set(rpcId, {
1196
+ rpcId,
1197
+ sessionId,
1198
+ kind,
1199
+ outcome,
1200
+ settledAt: Date.now()
1201
+ });
1202
+ this.log(`dsh-connection: ${kind} settled (${rpcId}) outcome=${outcome}`);
1203
+ } else this.log(`dsh-connection: settlement with no matching pending (${frame?.type}) — ignored`);
1204
+ }
1205
+ /** Pending answers for a session (for the PollResponse extension). */
1206
+ pendingFor(sessionId) {
1207
+ return [...this.pendingAnswers.values()].filter((p) => p.sessionId === sessionId);
1208
+ }
1209
+ /** Recent settlements for a session (ring: delivered-then-pruned). */
1210
+ settlementsFor(sessionId) {
1211
+ return [...this.settledAnswers.values()].filter((s) => s.sessionId === sessionId);
1212
+ }
1213
+ /** Delivery hook: drop settlements the client has already seen (ring prune). */
1214
+ pruneSettlements(sessionId, seenRpcIds) {
1215
+ for (const id of seenRpcIds) {
1216
+ const s = this.settledAnswers.get(id);
1217
+ if (s && s.sessionId === sessionId) this.settledAnswers.delete(id);
1218
+ }
1219
+ }
1220
+ /** session.list → normalized rows (DsiSessionSummary + raw projections). */
1221
+ async listSessions() {
1222
+ return { items: (await this.rawListSessions()).map(normalizeSessionRow) };
1223
+ }
1224
+ /**
1225
+ * The workspace-RPC OWNER for a session (2026-09-10): the host keys
1226
+ * workspaceFiles by the OWNING agent session and refuses sub-agent ids
1227
+ * (session/not-found / agent-busy — a sub-agent session is not an RPC
1228
+ * address). A sub-agent's workspace IS its root ancestor's, so walk the
1229
+ * parentSessionId chain (session.list) to the root; a root session
1230
+ * resolves to itself. Cycles guard themselves.
1231
+ */
1232
+ async workspaceOwnerSessionId(sessionId) {
1233
+ const { items } = await this.listSessions();
1234
+ const parentOf = new Map(items.map((s) => [s.sessionId, s.parentSessionId ?? null]));
1235
+ let cursor = sessionId;
1236
+ const seen = /* @__PURE__ */ new Set();
1237
+ while (!seen.has(cursor)) {
1238
+ seen.add(cursor);
1239
+ const parent = parentOf.get(cursor);
1240
+ if (!parent || parent === cursor) break;
1241
+ cursor = parent;
1242
+ }
1243
+ return cursor;
1244
+ }
1245
+ /** Raw session/list rows (0.1.2) — projections.asOfSeq drives page calls. */
1246
+ async rawListSessions() {
1247
+ const raw = await this.rpc(DSH_METHODS.list, DSH_ARGS.list());
1248
+ const rows = Array.isArray(raw?.items) ? raw.items : [];
1249
+ for (const row of rows) this.foldSubagentHint(row);
1250
+ return rows;
1251
+ }
1252
+ /**
1253
+ * Fold one list row's subagent identity into the hint cache. Only rows
1254
+ * the host stamps `origin:'subagent'` with a readable parent and a
1255
+ * descriptor lifecycle mode land here; a half-readable row keeps no hint
1256
+ * so the host's own rejection (surfaced honestly downstream) names the
1257
+ * truth instead of DSI guessing a mode.
1258
+ */
1259
+ foldSubagentHint(row) {
1260
+ if (row.origin !== "subagent") return;
1261
+ if (typeof row.parentSessionId !== "string" || row.parentSessionId === "") return;
1262
+ const identity = row.projections?.values?.["subagent"];
1263
+ const mode = identity !== null && typeof identity === "object" ? identity.mode : void 0;
1264
+ if (mode !== "one-shot" && mode !== "continuable") return;
1265
+ this.subagentHints.set(row.sessionId, {
1266
+ parentSessionId: row.parentSessionId,
1267
+ mode
1268
+ });
1269
+ }
1270
+ /**
1271
+ * The session/page + session/follow address for one session id: the
1272
+ * parent-addressed subagent form when a hint is folded, else the plain
1273
+ * session form. Subagent child ids are bare UUIDs (the spawner mints
1274
+ * `SessionId(randomUUID())`) — the id's SHAPE is never a discriminator;
1275
+ * only the host row's origin is.
1276
+ */
1277
+ addressFor(sessionId) {
1278
+ const hint = this.subagentHints.get(sessionId);
1279
+ if (hint === void 0) return {
1280
+ kind: "session",
1281
+ sessionId
1282
+ };
1283
+ return {
1284
+ kind: "subagent",
1285
+ parentSessionId: hint.parentSessionId,
1286
+ childSessionId: sessionId,
1287
+ mode: hint.mode
1288
+ };
1289
+ }
1290
+ /**
1291
+ * The tail-page through-cursor (0.1.2 session/page `throughSeq`). The
1292
+ * list projection's watermark (asOfSeq) is the LOWEST served-projection
1293
+ * row seq (session-projection-cache cachedSnapshot) and FREEZES while no
1294
+ * projection updates — mid-turn it sat at the last projection-relevant
1295
+ * event while the follow stream ran dozens of seqs ahead, so a page cut
1296
+ * there ended below the live truth, rewound the client's mark, and
1297
+ * pinned every later poll into a gap→resync loop. The follow buffer's
1298
+ * cursor IS the host's stream truth (seeded by the host's own snapshot
1299
+ * cursor, advanced only by host events), so a subscribed session pages
1300
+ * through it; the watermark stays the fallback for sessions without a
1301
+ * buffer (never subscribed — the pre-2026-09 behavior).
1302
+ *
1303
+ * A session the list does not name at all, and a listed row whose served
1304
+ * projection carries no cursor, both page with `throughSeq: -1` — the
1305
+ * 0.1.3 wire's unconstrained tail-page form (validated ≥ −1; the page is
1306
+ * bounded by its own cursor, never by ours). The HOST still owns every
1307
+ * refusal: a missing session rejects session/not-found before the cursor
1308
+ * is read (the cold load's 404 branch), never a client-invented error.
1309
+ *
1310
+ * 0.1.3-alpha.1: cold fork children serve `asOfSeq: -1` (the host's cold
1311
+ * blank-probe left with the v2 list projection) — a negative watermark is
1312
+ * "no served cursor", not "below the ledger head".
1313
+ */
1314
+ async pageCursorFor(sessionId) {
1315
+ const live = this.bufferFor(sessionId).lastSeq;
1316
+ if (live >= 0) return live;
1317
+ const throughSeq = (await this.rawListSessions()).find((r) => r.sessionId === sessionId)?.projections?.asOfSeq;
1318
+ if (typeof throughSeq === "number" && throughSeq >= 0) return throughSeq;
1319
+ return -1;
1320
+ }
1321
+ /**
1322
+ * session.history successor (0.1.2): the tail page via `session/page`.
1323
+ * throughSeq comes from pageCursorFor: the follow buffer's cursor when
1324
+ * the session has one, else the session/list projections (asOfSeq) of
1325
+ * the same session, else −1 — the 0.1.3 unconstrained tail-page form.
1326
+ */
1327
+ async history(sessionId) {
1328
+ const throughSeq = await this.pageCursorFor(sessionId);
1329
+ return this.pageAsHistory(await this.rpc(DSH_METHODS.page, DSH_ARGS.page({
1330
+ address: this.addressFor(sessionId),
1331
+ throughSeq
1332
+ })));
1333
+ }
1334
+ /** Workspace registry adoption (2026-08-23, DSH parity): the Add
1335
+ * workspace button's host call. Raw view passthrough — DSI renders
1336
+ * only title/path; ordering and session membership stay host-owned. */
1337
+ async createWorkspace(path) {
1338
+ return await this.rpc(DSH_METHODS.workspaceCreate, DSH_ARGS.workspaceCreate({ path }));
1339
+ }
1340
+ /** Workspace rename (Chip Menu ADR D3): {request:{workspaceId,title}}
1341
+ * → the updated Workspace view. Refusals throw DshRpcError
1342
+ * (workspace/not-found, workspace/name-conflict). No cache mutation —
1343
+ * the workspace/follow upsert frame owns the registry (ADR D6). */
1344
+ async renameWorkspace(workspaceId, title) {
1345
+ return await this.rpc(DSH_METHODS.workspaceRename, DSH_ARGS.workspaceRename(workspaceId, title));
1346
+ }
1347
+ /** Workspace delete (Chip Menu ADR D3): {request:{workspaceId}} → the
1348
+ * removed id. The host removes ONLY the registry row (sessions survive,
1349
+ * ungrouped); the workspace/follow remove frame owns the registry
1350
+ * (ADR D6) — no local mutation here. */
1351
+ async deleteWorkspace(workspaceId) {
1352
+ return await this.rpc(DSH_METHODS.workspaceDelete, DSH_ARGS.workspaceDelete(workspaceId));
1353
+ }
1354
+ /** Goal Bar (2026-09-08, ADR amendment): one goal mutation through the
1355
+ * host's Goal Remotes — goals/pause | goals/resume | goals/clear |
1356
+ * goals/edit with the CAS ref {id, revision} (a stale revision loses
1357
+ * host-side). goals/edit additionally carries the changed-fields
1358
+ * request {objective?, maxGoalRounds?} (Goal Editor, 2026-09-09). The
1359
+ * raw RemoteResult passes through; the route maps refusals. */
1360
+ async goalVerb(verb, sessionId, ref, request) {
1361
+ return this.rpc(`goals/${verb}`, request === void 0 ? {
1362
+ agentId: sessionId,
1363
+ ref
1364
+ } : {
1365
+ agentId: sessionId,
1366
+ ref,
1367
+ request
1368
+ });
1369
+ }
1370
+ /**
1371
+ * workspaceFiles/read — one page of a UTF-8 text file inside the
1372
+ * session's workspace (Workspace Explorer W2 task 2.1). Flat args
1373
+ * {workspaceFileScopeId, path, range} per the descriptor — NO request
1374
+ * wrapper (scope wire renamed from agentId, host 2026-09-09).
1375
+ * Host refusals (not-found, outside-workspace, too-large, not-text,
1376
+ * not-regular-file) reject as DshRpcError; the route maps the codes.
1377
+ *
1378
+ * @param sessionId - owning session; the host confines the read to its
1379
+ * workspace root by containment, never prefix match.
1380
+ * @param path - workspace path, absolute or root-relative.
1381
+ * @param range - line window; omitted fields take the host's page defaults.
1382
+ */
1383
+ async readWorkspaceFile(sessionId, path, range) {
1384
+ return await this.rpc(DSH_METHODS.workspaceFileRead, DSH_ARGS.workspaceFileRead(sessionId, path, range));
1385
+ }
1386
+ /**
1387
+ * workspaceFiles/readBytes — one byte window of a workspace file, raw
1388
+ * bytes (base64 receipt), no text decoding and no binary refusal
1389
+ * (2026-09-10: binary previews such as images read through this; `read`
1390
+ * refuses them workspace-file/not-text).
1391
+ *
1392
+ * @param sessionId - owning session; the host confines the read to its
1393
+ workspace root by containment.
1394
+ * @param path - workspace path, absolute or root-relative.
1395
+ * @param range - byte window {offset, length}; omitted fields take the
1396
+ host's window defaults.
1397
+ */
1398
+ async readWorkspaceFileBytes(sessionId, path, range) {
1399
+ return await this.rpc(DSH_METHODS.workspaceFileReadBytes, DSH_ARGS.workspaceFileReadBytes(sessionId, path, range));
1400
+ }
1401
+ /**
1402
+ * workspaceFiles/list — one directory level inside the session's
1403
+ * workspace (Workspace Explorer bugfix 2026-09-09: the picker's
1404
+ * directoryPicker/list is native-only on real hosts — the LIVE tree
1405
+ * lists through this session-authorized namespace instead). Flat args
1406
+ * {workspaceFileScopeId, path}; path is workspace-relative, '' = the root.
1407
+ */
1408
+ async listWorkspaceDirectory(sessionId, path) {
1409
+ return await this.rpc(DSH_METHODS.workspaceFileList, DSH_ARGS.workspaceFileList(sessionId, path));
1410
+ }
1411
+ /** The workspace registry (2026-08-23 registry parity) — the AUTHORITY
1412
+ * for DSI's workspace pills/chips: a deleted workspace disappears, and
1413
+ * sessions outside every registry entry render cue-less (DSH's
1414
+ * Ungrouped). Membership is sessionIds, not cwd matching. */
1415
+ async listWorkspaces() {
1416
+ if (!await this.awaitWorkspaceBaseline()) this.log("dsh-connection: workspace baseline did not land in time — honest empty");
1417
+ return { items: [...this.workspaces] };
1418
+ }
1419
+ /**
1420
+ * host.listDirectory — one browsed directory level for the Add-workspace
1421
+ * folder picker (DSH parity: the directory-picker browse capability).
1422
+ * Absent path lists the host home directory. Conservative normalize:
1423
+ * garbage rows drop out, never throw, never invent data.
1424
+ */
1425
+ async listDirectory(path) {
1426
+ const raw = await this.rpc(DSH_METHODS.listDirectory, DSH_ARGS.listDirectory(path));
1427
+ const entry = (v) => {
1428
+ if (v === null || typeof v !== "object") return null;
1429
+ const e = v;
1430
+ if (typeof e.name !== "string" || typeof e.path !== "string") return null;
1431
+ return {
1432
+ name: e.name,
1433
+ path: e.path,
1434
+ hidden: e.hidden === true
1435
+ };
1436
+ };
1437
+ const rows = (v) => Array.isArray(v) ? v.flatMap((x) => {
1438
+ const e = entry(x);
1439
+ return e ? [e] : [];
1440
+ }) : [];
1441
+ return {
1442
+ path: typeof raw?.path === "string" ? raw.path : "",
1443
+ home: typeof raw?.home === "string" ? raw.home : "",
1444
+ crumbs: rows(raw?.crumbs),
1445
+ entries: rows(raw?.entries),
1446
+ truncated: raw?.truncated === true
1447
+ };
1448
+ }
1449
+ /**
1450
+ * host.pickDirectory — the NATIVE interaction: the host opens one OS
1451
+ * chooser on its own display and resolves the picked absolute path
1452
+ * (null = operator cancelled). The signal follows the caller — a
1453
+ * disconnected browser closes the host's dialog mid-choice
1454
+ * (api-proxy pickDirectory's carrier contract).
1455
+ */
1456
+ /**
1457
+ * Wave 3 (task 3.2) — session.attachment: the session-authorized durable
1458
+ * image read. Returns the ref plus canonical base64 data; host refusals
1459
+ * (unknown id, foreign session) reject as DshRpcError → 502 upstream.
1460
+ *
1461
+ * @param sessionId - owning session (authorization scope).
1462
+ * @param attachmentId - durable content-addressed id from an ImageBlock.
1463
+ */
1464
+ async readAttachment(sessionId, attachmentId) {
1465
+ return await this.rpc(DSH_METHODS.attachment, DSH_ARGS.attachment({
1466
+ sessionId,
1467
+ attachmentId
1468
+ }));
1469
+ }
1470
+ async pickDirectory(signal) {
1471
+ const raw = await this.rpc(DSH_METHODS.pickDirectory, DSH_ARGS.pickDirectory(), signal);
1472
+ return { path: typeof raw?.path === "string" ? raw.path : null };
1473
+ }
1474
+ /**
1475
+ * POC-2 W3 — session.history PAGE ending at beforeSeq−1 (ledger truth, BC-4).
1476
+ * Live-probed semantics (PRD §1.2): the page covers events with seq <
1477
+ * beforeSeq; hasMore=false at the ledger head. Drives load-older paging —
1478
+ * the ring buffer is freshness-only and never consulted here.
1479
+ */
1480
+ async historyPage(sessionId, beforeSeq) {
1481
+ const throughSeq = await this.pageCursorFor(sessionId);
1482
+ return this.pageAsHistory(await this.rpc(DSH_METHODS.page, DSH_ARGS.page({
1483
+ address: this.addressFor(sessionId),
1484
+ throughSeq,
1485
+ beforeSeq
1486
+ })));
1487
+ }
1488
+ /** Adapt a 0.1.2 SessionPage {records,hasMore} to the DshHistoryPage shape. */
1489
+ pageAsHistory(raw) {
1490
+ const page = raw;
1491
+ return {
1492
+ events: expandRecords(Array.isArray(page?.records) ? page.records : []).map((event) => ({ event })),
1493
+ hasMore: page?.hasMore === true
1494
+ };
1495
+ }
1496
+ /**
1497
+ * POC-2 W3 — session.create {cwd?, agentPreset?} → {sessionId, …}.
1498
+ * Live-probed round-trip 2026-08-21 (probe7 → real session id).
1499
+ */
1500
+ async createSession(cwd, agentPreset) {
1501
+ return await this.rpc(DSH_METHODS.create, DSH_ARGS.create({
1502
+ ...cwd === void 0 ? {} : { cwd },
1503
+ ...agentPreset === void 0 ? {} : { agentPreset }
1504
+ }));
1505
+ }
1506
+ /**
1507
+ * session/fork {request:{sessionId,atSeq?}} → {sessionId} — the fork child
1508
+ * (The Fork Button ADR, 2026-09-01). An undefined atSeq is OMITTED from the
1509
+ * args, never sent as null: the host then cuts at the source's last
1510
+ * completed turn (host-enforced; see DSH_METHODS.fork for the pinned
1511
+ * contract and refusals).
1512
+ */
1513
+ async forkSession(sessionId, atSeq) {
1514
+ return await this.rpc(DSH_METHODS.fork, DSH_ARGS.fork({
1515
+ sessionId,
1516
+ ...atSeq === void 0 ? {} : { atSeq }
1517
+ }));
1518
+ }
1519
+ /**
1520
+ * POC-2 W3 — agentPreset.list → {presets:[…]} (live-probed 2026-08-21,
1521
+ * rpc-map.ts line 54). Presets are the picker's only source of truth.
1522
+ * The shipped-preset English overlay honors ~/.dsi/settings.yaml
1523
+ * `dsh.presetEnglish` (default true; read live per call — no cache, so
1524
+ * an operator edit applies on the next fetch without a restart).
1525
+ */
1526
+ async listPresets() {
1527
+ const overlayEnglish = readDshPresetEnglishConfig().presetEnglish;
1528
+ const raw = await this.rpc(DSH_METHODS.presets, DSH_ARGS.presets());
1529
+ return { presets: (Array.isArray(raw?.presets) ? raw.presets : []).map((row) => normalizePreset(row, overlayEnglish)) };
1530
+ }
1531
+ /**
1532
+ * session.prompt — returns after the receipt only (BC-3 boundary lives
1533
+ * upstream). Wave 2: a string payload still maps to exactly one text
1534
+ * part (byte-identical for every pre-existing caller); a content array
1535
+ * passes through verbatim — the route owns validation and ordering
1536
+ * (buildPromptContent, images first, text last).
1537
+ */
1538
+ async prompt(sessionId, payload, mode = "queue") {
1539
+ const content = typeof payload === "string" ? [{
1540
+ type: "text",
1541
+ text: payload
1542
+ }] : payload;
1543
+ return await this.rpc(DSH_METHODS.prompt, DSH_ARGS.prompt({
1544
+ requestId: this.rpcIdFactory(),
1545
+ sessionId,
1546
+ mode,
1547
+ content,
1548
+ clientTimeZone: Intl.DateTimeFormat().resolvedOptions().timeZone
1549
+ }));
1550
+ }
1551
+ /**
1552
+ * commands/execute (2026-08-25 fix) — the NATIVE slash-command surface the
1553
+ * host's own web GUI uses (Typert Gateway remote; session.prompt has no
1554
+ * slash interception — the 2026-08-25 RCA). Executes one line on the
1555
+ * session's agent; the host durably logs command/run + command/done (and
1556
+ * the permission command appends its knob events — the poll fold's
1557
+ * confirmation). Returns null on an admission miss (the line named no
1558
+ * registered command): ok:true, the command never ran — the honest
1559
+ * receipt callers must distinguish from a success.
1560
+ */
1561
+ async executeCommand(sessionId, line) {
1562
+ return await this.rpc(DSH_METHODS.commandExecute, DSH_ARGS.commandExecute(sessionId, line, [])) ?? null;
1563
+ }
1564
+ /**
1565
+ * commands/list (Slash Menu W1, task 1.2) — the host's command catalog,
1566
+ * name-sorted rows {name, description, input?{hint, images?}}. Live-pinned
1567
+ * 2026-08-30 (see tests/unit/dsh-rpc.test.ts): agentId IS the sessionId.
1568
+ * May resume a cold session on the host — callers fetch on first menu
1569
+ * open, never on panel mount (ADR §3.3).
1570
+ */
1571
+ async listCommands(sessionId) {
1572
+ return await this.rpc(DSH_METHODS.commandsList, DSH_ARGS.commandsList(sessionId));
1573
+ }
1574
+ /**
1575
+ * skills/list (Slash Menu W1, task 1.2) — the session's user-invocable
1576
+ * skill catalog {skills:[{name, description, whenToUse?, modelInvocable}]}.
1577
+ * Live-pinned 2026-08-30. Never activates a cold session — the menu may
1578
+ * read it freely on first open.
1579
+ */
1580
+ async listSkills(sessionId) {
1581
+ return await this.rpc(DSH_METHODS.skillsList, DSH_ARGS.skillsList(sessionId));
1582
+ }
1583
+ /** session.cancel */
1584
+ async cancel(sessionId) {
1585
+ return await this.rpc(DSH_METHODS.cancel, DSH_ARGS.cancel({ sessionId }));
1586
+ }
1587
+ /**
1588
+ * POC-3 W3 (3.1) — session.rename {sessionId, title}.
1589
+ *
1590
+ * Appends a session/title event with the `user` source — pins the title
1591
+ * against automatic regeneration (sessions.ts rename doc). The host
1592
+ * normalizes; a title that normalizes to empty fails `title-invalid` (an
1593
+ * RpcError → 502 upstream). Session-backed subagents reject `agent-busy`.
1594
+ */
1595
+ async renameSession(sessionId, title) {
1596
+ return await this.rpc(DSH_METHODS.rename, DSH_ARGS.rename({
1597
+ sessionId,
1598
+ title
1599
+ }));
1600
+ }
1601
+ /**
1602
+ * POC-3 W3 (3.2) — session.models {sessionId} → the model directory.
1603
+ *
1604
+ * Provider lookups run independently on the host (sessions.ts): `groups`
1605
+ * are successfully-loaded catalogs, `failures` are provider-local
1606
+ * diagnostics — successful groups remain usable. Garbage-safe normalize
1607
+ * below never throws (task 3.2-T: garbage catalog → empty, not an error).
1608
+ */
1609
+ async listModels(_sessionId) {
1610
+ return normalizeModelDirectory(await this.rpc(DSH_METHODS.modelCatalog, DSH_ARGS.modelCatalog()));
1611
+ }
1612
+ /**
1613
+ * POC-3 W3 (3.2) — session.selectModel {sessionId, provider, model,
1614
+ * reasoningEffort?} → {selected: ModelSelection}. Catalog membership is
1615
+ * advisory on the host — an exact-route selection still validates the
1616
+ * effort. Subagents reject `session/agent-busy` (RpcError → 502 upstream).
1617
+ */
1618
+ async selectModel(sessionId, provider, model, reasoningEffort) {
1619
+ return normalizeModelSelection((await this.rpc(DSH_METHODS.selectModel, DSH_ARGS.selectModel({
1620
+ sessionId,
1621
+ provider,
1622
+ model,
1623
+ ...reasoningEffort !== void 0 ? { reasoningEffort } : {}
1624
+ })))?.selected);
1625
+ }
1626
+ /**
1627
+ * POC-3 W1 — answer a pending approval/question via the dedicated
1628
+ * client-response carrier (POST /api/respond). NOT a unary method: no
1629
+ * DSH_METHODS entry, no rpcId minted here — the rpcId is the REQUEST's
1630
+ * stable id (registry key), echoed in the envelope. Receipt is a union,
1631
+ * not ok/error: {accepted:false, reason} is a SUCCESSFUL exchange (BC-B
1632
+ * upstream maps it to 200 + settle-elsewhere). Transport failure still
1633
+ * throws (route maps to 503 — card stays answerable, lock released).
1634
+ */
1635
+ /**
1636
+ * POC-3 W1 → 0.1.2: an answer is a `$events/result` unary RPC
1637
+ * {clientId, eventId, outcome} returning to the SAME generation whose
1638
+ * $events stream delivered the waterfall (rpcId = the eventId DSI
1639
+ * registered the card under). The outcome's `value` is the waterfall
1640
+ * listener's RETURN VALUE, and the host consumes it verbatim — a
1641
+ * question's value must BE the AskUserQuestionAnswer `{answers:[…]}`
1642
+ * (the live host crashes the tool on any other shape, losing the
1643
+ * answer); an approval's value is the outcome string. The panel's
1644
+ * legacy `{sessionId, answer:{answers}}` wrapper is unwrapped here so
1645
+ * the wire carries the host's contract, not the panel's convenience.
1646
+ */
1647
+ async respond(rpcId, payload) {
1648
+ if (this.eventClientId === null) throw new Error("dsh-connection: no $events generation to answer through yet");
1649
+ const { sessionId, ...rest } = payload;
1650
+ const value = rest.outcome !== void 0 ? rest.outcome : rest.answer !== void 0 ? rest.answer : rest.answers !== void 0 ? { answers: rest.answers } : rest;
1651
+ try {
1652
+ await this.sendEventResult(rpcId, value);
1653
+ this.forgetAnswered(rpcId, value);
1654
+ } catch (err) {
1655
+ if (err instanceof DshRpcError && err.code === "not-pending") return {
1656
+ accepted: false,
1657
+ reason: "not-pending"
1658
+ };
1659
+ if (err instanceof DshRpcError && err.code === "gateway/internal" && err.message.includes("no active event stream")) {
1660
+ const previous = this.eventClientId;
1661
+ if (previous !== null && await this.refreshEventGeneration(previous)) {
1662
+ await this.sendEventResult(rpcId, value);
1663
+ this.forgetAnswered(rpcId, value);
1664
+ return { accepted: true };
1665
+ }
1666
+ }
1667
+ throw err;
1668
+ }
1669
+ return { accepted: true };
1670
+ }
1671
+ /** One $events/result answer under the CURRENT generation's clientId. */
1672
+ sendEventResult(rpcId, value) {
1673
+ return this.rpc(REMOTE_EVENT_RESULT_ENDPOINT, {
1674
+ clientId: this.eventClientId,
1675
+ eventId: rpcId,
1676
+ outcome: {
1677
+ kind: "result",
1678
+ value
1679
+ }
1680
+ }).then(() => void 0);
1681
+ }
1682
+ /**
1683
+ * Rebuild the mux generation immediately (bypassing the reconnect
1684
+ * ladder) and wait until a NEW $events ready frame lands or the deadline
1685
+ * expires. Returns true only when a fresh clientId was observed — the
1686
+ * caller may retry an answer against it.
1687
+ */
1688
+ async refreshEventGeneration(previousClientId) {
1689
+ this.log("dsh-connection: $events generation stale — rebuilding for the answer retry");
1690
+ if (this.reconnectTimer !== void 0) {
1691
+ clearTimeout(this.reconnectTimer);
1692
+ this.reconnectTimer = void 0;
1693
+ }
1694
+ this.teardownSockets();
1695
+ this.ensureDownlinks();
1696
+ const deadline = Date.now() + this.staleGenerationRetryMs;
1697
+ while (!this.disposed && this.eventClientId === previousClientId && Date.now() < deadline) await new Promise((resolve) => setTimeout(resolve, 25));
1698
+ return this.eventClientId !== previousClientId && this.eventClientId !== null;
1699
+ }
1700
+ /**
1701
+ * Resync: replace the ring buffer with the ledger's tail page (BC-4).
1702
+ * Returns the fresh entries so callers can hand them to the store.
1703
+ */
1704
+ async resyncFromLedger(sessionId) {
1705
+ const events = ((await this.history(sessionId)).events ?? []).map((h) => h.event);
1706
+ const buf = this.bufferFor(sessionId);
1707
+ buf.entries = [...events].slice(-ringCapacity());
1708
+ buf.lastSeq = events.length > 0 ? events[events.length - 1].seq : -1;
1709
+ buf.coverageFloor = buf.entries[0]?.seq ?? buf.lastSeq;
1710
+ return buf.entries;
1711
+ }
1712
+ async rpc(method, payload, signal) {
1713
+ const body = JSON.stringify({
1714
+ type: "client-request",
1715
+ rpcId: this.rpcIdFactory(),
1716
+ method,
1717
+ payload: { args: payload }
1718
+ });
1719
+ const attempt = async () => {
1720
+ const cookie = await this.auth.ensureCookie();
1721
+ return this.fetchFn(rpcUrl(this.baseUrl, method), {
1722
+ method: "POST",
1723
+ headers: {
1724
+ "content-type": "application/json",
1725
+ ...cookie === null ? {} : { cookie }
1726
+ },
1727
+ body,
1728
+ ...signal !== void 0 ? { signal } : {}
1729
+ });
1730
+ };
1731
+ let response = await attempt();
1732
+ if (response.status === 401) {
1733
+ this.auth.invalidate();
1734
+ const { authToken } = readDshAuthConfig();
1735
+ response = await attempt();
1736
+ }
1737
+ if (!response.ok) throw new Error(`dsh-connection: transport failure for ${method}: HTTP ${response.status}`);
1738
+ const rpcId = JSON.parse(body).rpcId;
1739
+ return parseResponse(await response.text(), rpcId);
1740
+ }
1741
+ dispose() {
1742
+ this.disposed = true;
1743
+ clearTimeout(this.answerReapTimer);
1744
+ this.teardownSockets();
1745
+ this.pendingAnswers.clear();
1746
+ this.settledAnswers.clear();
1747
+ this.buffers.clear();
1748
+ this.liveTails.clear();
1749
+ this.running.clear();
1750
+ }
1751
+ };
1752
+ /** The follow-<sessionId>-<streamSeq> logical stream id (one per generation). */
1753
+ function followStreamId(sessionId, streamSeq) {
1754
+ return `follow-${sessionId}-${String(streamSeq)}`;
1755
+ }
1756
+ /**
1757
+ * Expand ONE follow/page event payload into its ring-buffer form
1758
+ * (single-element list — ledger v2 serves every record as a plain event).
1759
+ */
1760
+ function expandEntry(e) {
1761
+ return [{
1762
+ type: e.type,
1763
+ seq: typeof e.seq === "number" ? e.seq : -1,
1764
+ time: typeof e.time === "number" ? e.time : 0,
1765
+ data: e.data
1766
+ }];
1767
+ }
1768
+ /** Expand one page/snapshot record ({type:'event', event}) into its member
1769
+ * events; a record without an event payload contributes nothing. */
1770
+ function expandRecord(record) {
1771
+ if (record === void 0 || record.event === void 0) return [];
1772
+ return expandEntry(record.event);
1773
+ }
1774
+ /** Expand a records array into a flat event list. */
1775
+ function expandRecords(records) {
1776
+ return records.flatMap((record) => expandRecord(record));
1777
+ }
1778
+ /** The last member time of one compact stream record, or undefined when
1779
+ * the record carries no readable time (malformed members stay silent —
1780
+ * the baseline is presentation seed data, never re-validated truth). */
1781
+ function compactRecordTailTime(record) {
1782
+ if (record.type === "chunk") return typeof record.time === "number" ? record.time : void 0;
1783
+ if (record.type === "text-chunks" || record.type === "reasoning-chunks" || record.type === "tool-call-chunks") {
1784
+ const time0 = typeof record.time0 === "number" ? record.time0 : void 0;
1785
+ const dt = Array.isArray(record.dt) ? record.dt : [];
1786
+ if (time0 === void 0 || dt.some((g) => typeof g !== "number")) return void 0;
1787
+ return dt.reduce((t, g) => t + g, time0);
1788
+ }
1789
+ }
1790
+ function normalizeModelSelection(v) {
1791
+ if (v === null || typeof v !== "object") return null;
1792
+ const sel = v;
1793
+ if (typeof sel.provider !== "string" || typeof sel.model !== "string") return null;
1794
+ return {
1795
+ provider: sel.provider,
1796
+ model: sel.model,
1797
+ ...typeof sel.reasoningEffort === "string" ? { reasoningEffort: sel.reasoningEffort } : {}
1798
+ };
1799
+ }
1800
+ /**
1801
+ * Conservative normalize of a SessionModels value: structural reads only,
1802
+ * never throws, never invents data (task 3.2-T: a garbage catalog → empty
1803
+ * groups + empty failures, honest `routable`/`current` — the selector can
1804
+ * still show the current selection and say nothing is listed). Reasoning
1805
+ * efforts are flattened to opaque ids (the adapter owns their meaning).
1806
+ */
1807
+ function normalizeModelDirectory(raw) {
1808
+ const empty = {
1809
+ current: null,
1810
+ routable: false,
1811
+ groups: [],
1812
+ failures: []
1813
+ };
1814
+ if (raw === null || typeof raw !== "object") return empty;
1815
+ const dir = raw;
1816
+ const groups = Array.isArray(dir.groups) ? modelGroupsOf(dir.groups) : [];
1817
+ const failures = Array.isArray(dir.failures) ? modelFailuresOf(dir.failures) : [];
1818
+ if (dir.current === void 0 && dir.default !== void 0) return {
1819
+ current: normalizeModelSelection(dir.default),
1820
+ routable: Array.isArray(dir.routableProviders) ? dir.routableProviders.length > 0 || groups.length > 0 : true,
1821
+ groups,
1822
+ failures
1823
+ };
1824
+ return {
1825
+ current: normalizeModelSelection(dir.current),
1826
+ routable: dir.routable === true,
1827
+ groups,
1828
+ failures
1829
+ };
1830
+ }
1831
+ /** ModelCatalog provider groups → DsiModelGroup rows (conservative). */
1832
+ function modelGroupsOf(groups) {
1833
+ return groups.flatMap((g) => {
1834
+ if (g === null || typeof g !== "object") return [];
1835
+ const grp = g;
1836
+ if (typeof grp.id !== "string" || grp.id.length === 0) return [];
1837
+ const models = Array.isArray(grp.models) ? grp.models.flatMap((m) => {
1838
+ if (m === null || typeof m !== "object") return [];
1839
+ const mod = m;
1840
+ if (typeof mod.id !== "string" || mod.id.length === 0) return [];
1841
+ return [{
1842
+ id: mod.id,
1843
+ name: typeof mod.name === "string" ? mod.name : null,
1844
+ ...Array.isArray(mod.reasoning?.efforts) ? { reasoningEfforts: mod.reasoning.efforts.flatMap((e) => typeof e?.id === "string" ? [e.id] : []) } : {}
1845
+ }];
1846
+ }) : [];
1847
+ return [{
1848
+ id: grp.id,
1849
+ name: typeof grp.name === "string" ? grp.name : null,
1850
+ models
1851
+ }];
1852
+ });
1853
+ }
1854
+ /** ModelCatalog provider failures → diagnostic rows (conservative). */
1855
+ function modelFailuresOf(failures) {
1856
+ return failures.flatMap((f) => {
1857
+ if (f === null || typeof f !== "object") return [];
1858
+ const fail = f;
1859
+ if (typeof fail.id !== "string" || fail.id.length === 0) return [];
1860
+ return [{
1861
+ id: fail.id,
1862
+ name: typeof fail.name === "string" ? fail.name : null,
1863
+ message: typeof fail.message === "string" ? fail.message : null
1864
+ }];
1865
+ });
1866
+ }
1867
+ function normalizeSessionRow(row) {
1868
+ return {
1869
+ sessionId: row.sessionId,
1870
+ title: row.origin === "subagent" ? readSubagentLabel(row) ?? readTitle(row) : readTitle(row),
1871
+ agentPreset: readAgentPreset(row) ?? row.agentPreset ?? null,
1872
+ running: Boolean(row.running),
1873
+ blank: Boolean(row.blank),
1874
+ updatedAt: row.updatedAt ?? 0,
1875
+ workspace: typeof row.cwd === "string" && row.cwd.length > 0 ? row.cwd : null,
1876
+ turns: readTurns(row),
1877
+ parentSessionId: row.parentSessionId ?? null,
1878
+ origin: row.origin ?? null
1879
+ };
1880
+ }
1881
+ function readTitle(row) {
1882
+ const title = row.projections?.values?.["title"];
1883
+ return typeof title === "string" && title.length > 0 ? title : null;
1884
+ }
1885
+ /** projections.values.subagent.label (the child descriptor's creation label;
1886
+ * one-shot children may omit it — structural read, null when unreadable). */
1887
+ function readSubagentLabel(row) {
1888
+ const identity = row.projections?.values?.["subagent"];
1889
+ const label = identity !== null && typeof identity === "object" ? identity.label : void 0;
1890
+ return typeof label === "string" && label.length > 0 ? label : null;
1891
+ }
1892
+ /** projections.values.agentPreset (0.1.2 home of the preset id on list rows). */
1893
+ function readAgentPreset(row) {
1894
+ const preset = row.projections?.values?.["agentPreset"];
1895
+ return typeof preset === "string" && preset.length > 0 ? preset : null;
1896
+ }
1897
+ /** `sessionStats.turns` (0.1.2: projections.values.sessionStats) — integer-strict, null on absent/junk. */
1898
+ function readTurns(row) {
1899
+ const stats = row.projections?.values?.["sessionStats"];
1900
+ const turns = stats !== null && typeof stats === "object" ? stats.turns : row.sessionStats?.turns;
1901
+ if (typeof turns !== "number" || !Number.isInteger(turns) || turns < 0) return null;
1902
+ return turns;
1903
+ }
1904
+ /**
1905
+ * English display copy for the host's SHIPPED presets, keyed by the stable
1906
+ * preset id. The wire's `name`/`description` are the UNLOCALIZED metadata a
1907
+ * preset publishes (DSH `PresetDisplaySource`), and the four built-ins ship
1908
+ * Simplified Chinese there — DSH localizes them only inside its own web UI
1909
+ * (`packages/preset/agent-presets/src/display.ts` folds trust:'system' + id →
1910
+ * locale dictionary keys). This app has no host locale seat, so the overlay
1911
+ * mirrors that fold in English: trust:'system' + a known id → the English
1912
+ * copy; every other row (user presets, unknown future ids) keeps the host's
1913
+ * name verbatim — user metadata is never translated, and an unknown id falls
1914
+ * back honestly. Operator-tunable off via ~/.dsi/settings.yaml
1915
+ * `dsh.presetEnglish: false` (readDshPresetEnglishConfig — no cache, so the
1916
+ * next listPresets fetch picks an edit up). Keep the strings in step with DSH
1917
+ * `packages/client/ui-agent-preset/src/client/locales.ts` (the `en` bundle).
1918
+ */
1919
+ var SHIPPED_PRESET_ENGLISH = {
1920
+ standard: {
1921
+ name: "Standard mode",
1922
+ description: "Full coding agent with file editing, shell, file and web search, skills, planning, goals, subagents, and workflows."
1923
+ },
1924
+ ptc: {
1925
+ name: "PTC mode",
1926
+ description: "Full coding agent without the workflow tool; other tools are exposed through the PTC mode SDK so the model can combine multi-step operations in one TypeScript program."
1927
+ },
1928
+ minimal: {
1929
+ name: "Minimal mode",
1930
+ description: "Two-tool coding agent with persistent bash and str_replace_editor."
1931
+ },
1932
+ cordis: {
1933
+ name: "Creator mode",
1934
+ description: "Built for creating custom agent presets, with all Standard mode capabilities plus runtime inspection, plugin experiments, and preset-authoring guidance."
1935
+ }
1936
+ };
1937
+ /**
1938
+ * Conservative normalize: unknown shapes keep their id but never invent data.
1939
+ * The one English overlay seat (SHIPPED_PRESET_ENGLISH) applies before the
1940
+ * host fallbacks — a shipped preset's label is locale-resolved here, never
1941
+ * the wire's unlocalized text. Operator-tunable via
1942
+ * ~/.dsi/settings.yaml `dsh.presetEnglish` (read live per listPresets
1943
+ * call, passed through here): false hands the wire name through untouched.
1944
+ */
1945
+ function normalizePreset(row, overlayEnglish = true) {
1946
+ const id = typeof row.id === "string" ? row.id : String(row.id ?? "");
1947
+ const shipped = overlayEnglish && row.trust === "system" ? SHIPPED_PRESET_ENGLISH[id] : void 0;
1948
+ return {
1949
+ id,
1950
+ name: shipped?.name ?? (typeof row.name === "string" ? row.name : null),
1951
+ description: shipped?.description ?? (typeof row.description === "string" ? row.description : null),
1952
+ isDefault: row.isDefault === true
1953
+ };
1954
+ }
1955
+
1956
+ export { DshRpcError as D, isPromptImageMediaType as a, buildPromptContent as b, getDshConnection as g, isSubagentRejection as i, mapRpcFailure as m, statusFor as s };
1957
+ //# sourceMappingURL=dsh-connection.js-DwihVtk_.js.map