@prismshadow/penguin-server 0.2.13 → 0.2.16

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 (423) hide show
  1. package/dist/api/types.d.ts +2762 -153
  2. package/dist/api/types.js +3 -1
  3. package/dist/auth-token.d.ts +22 -1
  4. package/dist/auth-token.js +3 -3
  5. package/dist/auth-token.js.map +1 -1
  6. package/dist/{chunk-A3QANMU5.js → chunk-3SFJKLQU.js} +315 -10
  7. package/dist/chunk-3SFJKLQU.js.map +1 -0
  8. package/dist/{chunk-3I2XOPJB.js → chunk-D3JNA6ZI.js} +126 -46
  9. package/dist/chunk-D3JNA6ZI.js.map +1 -0
  10. package/dist/{chunk-TOOTEEM4.js → chunk-G7GY7PJU.js} +1 -1
  11. package/dist/chunk-G7GY7PJU.js.map +1 -0
  12. package/dist/chunk-HX3WBUYX.js +18 -0
  13. package/dist/chunk-HX3WBUYX.js.map +1 -0
  14. package/dist/chunk-QFV3ERQO.js +20 -0
  15. package/dist/chunk-QFV3ERQO.js.map +1 -0
  16. package/dist/chunk-W7JNIJ25.js +19 -0
  17. package/dist/chunk-W7JNIJ25.js.map +1 -0
  18. package/dist/index.js +73676 -39990
  19. package/dist/index.js.map +1 -1
  20. package/dist/install.ps1 +16 -6
  21. package/dist/install.sh +24 -10
  22. package/dist/machine-status.js +1 -1
  23. package/dist/plugin/index.d.ts +1073 -276
  24. package/dist/reset-admin-password.js +3 -3
  25. package/dist/secret-file.js +3 -14
  26. package/dist/secret-file.js.map +1 -1
  27. package/dist/version-report.d.ts +3 -0
  28. package/dist/version-report.js +1 -1
  29. package/package.json +8 -6
  30. package/web-dist/assets/KaTeX_Size3-Regular-gV2CO0n9.woff2 +0 -0
  31. package/web-dist/assets/abnfDiagram-VCTEODGH-0AKFiyig.js +1 -0
  32. package/web-dist/assets/{addon-clipboard-DrZY90Ks.js → addon-clipboard-BovFnNVg.js} +1 -1
  33. package/web-dist/assets/{addon-fit-CtEAdAax.js → addon-fit-B1npgxQk.js} +1 -1
  34. package/web-dist/assets/{addon-web-links-DJL13cYY.js → addon-web-links-D88QHj7A.js} +1 -1
  35. package/web-dist/assets/arc-Beox1Cgf.js +1 -0
  36. package/web-dist/assets/architectureDiagram-5GKGNRK7-CC1sLMY1.js +36 -0
  37. package/web-dist/assets/blockDiagram-I7D4REHJ-B8BY1PkZ.js +129 -0
  38. package/web-dist/assets/c4Diagram-7LVT6UL2-yjY0BkxQ.js +38 -0
  39. package/web-dist/assets/channel-mfsAVJLE.js +1 -0
  40. package/web-dist/assets/chunk-2Q5K7J3B-DdgIvETg.js +1 -0
  41. package/web-dist/assets/chunk-5VM5RSS4-BE38k1Rs.js +15 -0
  42. package/web-dist/assets/chunk-F27PBJKO-D4YXmaqQ.js +1 -0
  43. package/web-dist/assets/chunk-IMKFNOWR-CyJFdThc.js +231 -0
  44. package/web-dist/assets/chunk-JWPE2WC7-BAM_oEUU.js +1 -0
  45. package/web-dist/assets/chunk-POPQ4Y6H-BP3D0FVi.js +1 -0
  46. package/web-dist/assets/chunk-SVP7TREG-Clk8PdKZ.js +88 -0
  47. package/web-dist/assets/chunk-TICWLB2K-EvWPnEGg.js +206 -0
  48. package/web-dist/assets/chunk-XXDRQBXY-6p5D0UB1.js +1 -0
  49. package/web-dist/assets/classDiagram-ZZMXUADV-BPVO1m8i.js +1 -0
  50. package/web-dist/assets/classDiagram-v2-VYDZK3BY-BPVO1m8i.js +1 -0
  51. package/web-dist/assets/cose-bilkent-JH36ORCC-bpkI-tLJ.js +1 -0
  52. package/web-dist/assets/cynefin-OW5HDTMX-srLaqJ4e.js +166 -0
  53. package/web-dist/assets/cynefinDiagram-5FMLGOSQ-BPfpHSPc.js +62 -0
  54. package/web-dist/assets/cytoscape.esm-BB4DxJjf.js +321 -0
  55. package/web-dist/assets/dagre-GXQ25YYZ-C4fF5-j7.js +4 -0
  56. package/web-dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  57. package/web-dist/assets/diagram-S7CK7UJ4-BzBLb_9-.js +30 -0
  58. package/web-dist/assets/diagram-UQ7AKVKN-BJuJ1o_T.js +41 -0
  59. package/web-dist/assets/diagram-VSXAHHWV-DFelN9i9.js +3 -0
  60. package/web-dist/assets/diagram-VX7I27RA-BgrEoPyp.js +24 -0
  61. package/web-dist/assets/diagram-Z3DM3KII-C71KiMiX.js +24 -0
  62. package/web-dist/assets/ebnfDiagram-PWID7BFC-EYteyp1b.js +1 -0
  63. package/web-dist/assets/erDiagram-RLTQ6QDP-B6n6-sz6.js +99 -0
  64. package/web-dist/assets/flowDiagram-HODETNUW-BtoMTdyT.js +168 -0
  65. package/web-dist/assets/ganttDiagram-EL5Y4UJY-CbOAa3vS.js +292 -0
  66. package/web-dist/assets/gitGraphDiagram-WWUBYQGX-B7-8sszH.js +106 -0
  67. package/web-dist/assets/highlighter-Bj5l2XH_.js +2 -0
  68. package/web-dist/assets/{highlighter-core-CrBdruEx.js → highlighter-core-CMXoD8ze.js} +7 -7
  69. package/web-dist/assets/{highlighter.worker-CL6f9Fzw.js → highlighter.worker-D6D9fPma.js} +9 -9
  70. package/web-dist/assets/ibm-plex-sans-condensed-latin-600-normal-CRd5VyFf.woff2 +0 -0
  71. package/web-dist/assets/ibm-plex-sans-condensed-latin-ext-600-normal-CBdNZ9Ci.woff2 +0 -0
  72. package/web-dist/assets/ibm-plex-sans-latin-ext-wght-normal-CIII54If.woff2 +0 -0
  73. package/web-dist/assets/ibm-plex-sans-latin-wght-normal-IvpUvPa2.woff2 +0 -0
  74. package/web-dist/assets/index-BOCqRT4e.css +1 -0
  75. package/web-dist/assets/index-BrMpjp2s.js +1286 -0
  76. package/web-dist/assets/infoDiagram-27XIBGKW-BBjf6pkP.js +2 -0
  77. package/web-dist/assets/init-Gi6I4Gst.js +1 -0
  78. package/web-dist/assets/ishikawaDiagram-5VMMS53U-BBSvCv45.js +70 -0
  79. package/web-dist/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  80. package/web-dist/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  81. package/web-dist/assets/journeyDiagram-3NMN7TZE-CuHHl5zk.js +139 -0
  82. package/web-dist/assets/kanban-definition-UXKFOSKX-DJyP4Myw.js +89 -0
  83. package/web-dist/assets/layout-Bqqhdwtt.js +1 -0
  84. package/web-dist/assets/linear-BZfUSIf8.js +1 -0
  85. package/web-dist/assets/mermaid.core-BlUspEsI.js +310 -0
  86. package/web-dist/assets/mindmap-definition-YA3MSWOX-BtyMD4wz.js +96 -0
  87. package/web-dist/assets/misans-400-100-DHkMCix2.woff2 +0 -0
  88. package/web-dist/assets/misans-400-101-CwgSLsmc.woff2 +0 -0
  89. package/web-dist/assets/misans-400-102-BQgefHPQ.woff2 +0 -0
  90. package/web-dist/assets/misans-400-103-CP58Mc_T.woff2 +0 -0
  91. package/web-dist/assets/misans-400-104-Ta_Xu1gd.woff2 +0 -0
  92. package/web-dist/assets/misans-400-105-CDop11AR.woff2 +0 -0
  93. package/web-dist/assets/misans-400-106-Dv8y0ubk.woff2 +0 -0
  94. package/web-dist/assets/misans-400-107-CjnHtp9E.woff2 +0 -0
  95. package/web-dist/assets/misans-400-108-BA8nQUBw.woff2 +0 -0
  96. package/web-dist/assets/misans-400-109-B6uEN0pG.woff2 +0 -0
  97. package/web-dist/assets/misans-400-110-BQqnShrY.woff2 +0 -0
  98. package/web-dist/assets/misans-400-111-CHB-Jj5X.woff2 +0 -0
  99. package/web-dist/assets/misans-400-112-D1dh4Cjx.woff2 +0 -0
  100. package/web-dist/assets/misans-400-113-Bw_wNCFp.woff2 +0 -0
  101. package/web-dist/assets/misans-400-114-CUb-5IzE.woff2 +0 -0
  102. package/web-dist/assets/misans-400-115-BICOatsO.woff2 +0 -0
  103. package/web-dist/assets/misans-400-116-B3_gmYW6.woff2 +0 -0
  104. package/web-dist/assets/misans-400-117-B8k_y3cv.woff2 +0 -0
  105. package/web-dist/assets/misans-400-118-C-2Cf5C6.woff2 +0 -0
  106. package/web-dist/assets/misans-400-119-CmLP7xnX.woff2 +0 -0
  107. package/web-dist/assets/misans-400-21-DsSSD_Zj.woff2 +0 -0
  108. package/web-dist/assets/misans-400-22-rtkxWOv4.woff2 +0 -0
  109. package/web-dist/assets/misans-400-23-CDDB4M1H.woff2 +0 -0
  110. package/web-dist/assets/misans-400-24-Dqf_XeOL.woff2 +0 -0
  111. package/web-dist/assets/misans-400-25-C4U1tJ5J.woff2 +0 -0
  112. package/web-dist/assets/misans-400-26-CEXOK-nT.woff2 +0 -0
  113. package/web-dist/assets/misans-400-27-BrNOAScF.woff2 +0 -0
  114. package/web-dist/assets/misans-400-28-CPpEnCkG.woff2 +0 -0
  115. package/web-dist/assets/misans-400-29-C9JMVw8g.woff2 +0 -0
  116. package/web-dist/assets/misans-400-30-CD-QimXa.woff2 +0 -0
  117. package/web-dist/assets/misans-400-31-S4k06NbS.woff2 +0 -0
  118. package/web-dist/assets/misans-400-32-zRMBDkz6.woff2 +0 -0
  119. package/web-dist/assets/misans-400-33-sQ_tNZSM.woff2 +0 -0
  120. package/web-dist/assets/misans-400-34-8LEGljja.woff2 +0 -0
  121. package/web-dist/assets/misans-400-35-CxSPyuiU.woff2 +0 -0
  122. package/web-dist/assets/misans-400-36-BY1i4WtO.woff2 +0 -0
  123. package/web-dist/assets/misans-400-37-C0oF42pu.woff2 +0 -0
  124. package/web-dist/assets/misans-400-38-y7xcB90w.woff2 +0 -0
  125. package/web-dist/assets/misans-400-39-C307YgZx.woff2 +0 -0
  126. package/web-dist/assets/misans-400-40-ByZr736f.woff2 +0 -0
  127. package/web-dist/assets/misans-400-41-ChKEw5zV.woff2 +0 -0
  128. package/web-dist/assets/misans-400-42-CD__zJu5.woff2 +0 -0
  129. package/web-dist/assets/misans-400-43-CqNV6_TG.woff2 +0 -0
  130. package/web-dist/assets/misans-400-44-zE-akmW8.woff2 +0 -0
  131. package/web-dist/assets/misans-400-45-Du98otwc.woff2 +0 -0
  132. package/web-dist/assets/misans-400-46-B6JXbEy2.woff2 +0 -0
  133. package/web-dist/assets/misans-400-47-DpqW0kSq.woff2 +0 -0
  134. package/web-dist/assets/misans-400-48-BprON5Q_.woff2 +0 -0
  135. package/web-dist/assets/misans-400-49-N-ct3sN6.woff2 +0 -0
  136. package/web-dist/assets/misans-400-5-ton_4jCN.woff2 +0 -0
  137. package/web-dist/assets/misans-400-50-JgU49P0U.woff2 +0 -0
  138. package/web-dist/assets/misans-400-51-DSS3Moge.woff2 +0 -0
  139. package/web-dist/assets/misans-400-52-BMoXFmwE.woff2 +0 -0
  140. package/web-dist/assets/misans-400-53-BaLMGl4z.woff2 +0 -0
  141. package/web-dist/assets/misans-400-54-CaeSn3OR.woff2 +0 -0
  142. package/web-dist/assets/misans-400-55-BBqrtLxN.woff2 +0 -0
  143. package/web-dist/assets/misans-400-56-DAZ5BnbO.woff2 +0 -0
  144. package/web-dist/assets/misans-400-57-F-n3b0gI.woff2 +0 -0
  145. package/web-dist/assets/misans-400-58-DEUuPQAq.woff2 +0 -0
  146. package/web-dist/assets/misans-400-59-DXP5rhCL.woff2 +0 -0
  147. package/web-dist/assets/misans-400-6-DWCCUO_c.woff2 +0 -0
  148. package/web-dist/assets/misans-400-60-BE6IO6zJ.woff2 +0 -0
  149. package/web-dist/assets/misans-400-61-5Qb1gaxK.woff2 +0 -0
  150. package/web-dist/assets/misans-400-62-D_tJKNAj.woff2 +0 -0
  151. package/web-dist/assets/misans-400-63-hwmne8yB.woff2 +0 -0
  152. package/web-dist/assets/misans-400-64-BGedxReM.woff2 +0 -0
  153. package/web-dist/assets/misans-400-65-BaKPsugb.woff2 +0 -0
  154. package/web-dist/assets/misans-400-66-IdaOMzCz.woff2 +0 -0
  155. package/web-dist/assets/misans-400-67-BdWyS83H.woff2 +0 -0
  156. package/web-dist/assets/misans-400-68-DrZ_1swq.woff2 +0 -0
  157. package/web-dist/assets/misans-400-69-B3jsSD92.woff2 +0 -0
  158. package/web-dist/assets/misans-400-70-D6UZI2Ua.woff2 +0 -0
  159. package/web-dist/assets/misans-400-71-HFxyMCYg.woff2 +0 -0
  160. package/web-dist/assets/misans-400-72-CP5A-yQP.woff2 +0 -0
  161. package/web-dist/assets/misans-400-73-BoD_bu6s.woff2 +0 -0
  162. package/web-dist/assets/misans-400-74-CbJlhZlZ.woff2 +0 -0
  163. package/web-dist/assets/misans-400-75-BsbWuELX.woff2 +0 -0
  164. package/web-dist/assets/misans-400-76-DkwiQhof.woff2 +0 -0
  165. package/web-dist/assets/misans-400-77-6pVHXHnF.woff2 +0 -0
  166. package/web-dist/assets/misans-400-78-CfUF6pih.woff2 +0 -0
  167. package/web-dist/assets/misans-400-79-BCS3Dx4e.woff2 +0 -0
  168. package/web-dist/assets/misans-400-80-BEYSdQ_N.woff2 +0 -0
  169. package/web-dist/assets/misans-400-81-BzL-Gncx.woff2 +0 -0
  170. package/web-dist/assets/misans-400-82-CaENmdUU.woff2 +0 -0
  171. package/web-dist/assets/misans-400-83-Bvq1eBcA.woff2 +0 -0
  172. package/web-dist/assets/misans-400-84-Ctd90hiP.woff2 +0 -0
  173. package/web-dist/assets/misans-400-85-DWuOxfL1.woff2 +0 -0
  174. package/web-dist/assets/misans-400-86-D5niZS5c.woff2 +0 -0
  175. package/web-dist/assets/misans-400-87-DkOIADQJ.woff2 +0 -0
  176. package/web-dist/assets/misans-400-88-CGY4WTcC.woff2 +0 -0
  177. package/web-dist/assets/misans-400-89-CA3eoMTt.woff2 +0 -0
  178. package/web-dist/assets/misans-400-90-CzxR9MYb.woff2 +0 -0
  179. package/web-dist/assets/misans-400-91-Dyr85txc.woff2 +0 -0
  180. package/web-dist/assets/misans-400-97-qEZrCUNe.woff2 +0 -0
  181. package/web-dist/assets/misans-400-cyrillic--hFcG0Xp.woff2 +0 -0
  182. package/web-dist/assets/misans-400-extra-ZRI9n2Hu.woff2 +0 -0
  183. package/web-dist/assets/misans-400-latin-BRDR1WaL.woff2 +0 -0
  184. package/web-dist/assets/misans-400-latin-ext-6xS-MvHW.woff2 +0 -0
  185. package/web-dist/assets/misans-400-vietnamese-D7xD7kBB.woff2 +0 -0
  186. package/web-dist/assets/misans-500-100-BHuGA96E.woff2 +0 -0
  187. package/web-dist/assets/misans-500-101-CDMYUz0c.woff2 +0 -0
  188. package/web-dist/assets/misans-500-102-CWcqYYCV.woff2 +0 -0
  189. package/web-dist/assets/misans-500-103-BE1QSEzY.woff2 +0 -0
  190. package/web-dist/assets/misans-500-104-CDp743BV.woff2 +0 -0
  191. package/web-dist/assets/misans-500-105-y7dY6Foi.woff2 +0 -0
  192. package/web-dist/assets/misans-500-106-CP-kGZ6j.woff2 +0 -0
  193. package/web-dist/assets/misans-500-107-CUc8Hvle.woff2 +0 -0
  194. package/web-dist/assets/misans-500-108-ItN_x8jX.woff2 +0 -0
  195. package/web-dist/assets/misans-500-109-CEtf3R4D.woff2 +0 -0
  196. package/web-dist/assets/misans-500-110-BSZSHYRL.woff2 +0 -0
  197. package/web-dist/assets/misans-500-111-DfdArrjn.woff2 +0 -0
  198. package/web-dist/assets/misans-500-112-DRtNyrQw.woff2 +0 -0
  199. package/web-dist/assets/misans-500-113-CDT7tKuB.woff2 +0 -0
  200. package/web-dist/assets/misans-500-114-CCh4jwL8.woff2 +0 -0
  201. package/web-dist/assets/misans-500-115-DplUgPp0.woff2 +0 -0
  202. package/web-dist/assets/misans-500-116-C7eCklyK.woff2 +0 -0
  203. package/web-dist/assets/misans-500-117-BA0KabyT.woff2 +0 -0
  204. package/web-dist/assets/misans-500-118-V_M0eu2q.woff2 +0 -0
  205. package/web-dist/assets/misans-500-119-Cgf3Hszt.woff2 +0 -0
  206. package/web-dist/assets/misans-500-21-Di6DyR6O.woff2 +0 -0
  207. package/web-dist/assets/misans-500-22-CSnXvtL-.woff2 +0 -0
  208. package/web-dist/assets/misans-500-23-A06Hm9a6.woff2 +0 -0
  209. package/web-dist/assets/misans-500-24-Df97RCik.woff2 +0 -0
  210. package/web-dist/assets/misans-500-25-bOSJ6YXH.woff2 +0 -0
  211. package/web-dist/assets/misans-500-26-C65icgBb.woff2 +0 -0
  212. package/web-dist/assets/misans-500-27-C_GRm0va.woff2 +0 -0
  213. package/web-dist/assets/misans-500-28-CzZkqO0l.woff2 +0 -0
  214. package/web-dist/assets/misans-500-29-BReyeRyX.woff2 +0 -0
  215. package/web-dist/assets/misans-500-30-DzfjlROz.woff2 +0 -0
  216. package/web-dist/assets/misans-500-31-B2nLpgdU.woff2 +0 -0
  217. package/web-dist/assets/misans-500-32-Bz3rHm8X.woff2 +0 -0
  218. package/web-dist/assets/misans-500-33-AU3lZ1vY.woff2 +0 -0
  219. package/web-dist/assets/misans-500-34-CBAWkH-I.woff2 +0 -0
  220. package/web-dist/assets/misans-500-35-VgnGd14P.woff2 +0 -0
  221. package/web-dist/assets/misans-500-36-B2kfXv6A.woff2 +0 -0
  222. package/web-dist/assets/misans-500-37-DhKo8ykb.woff2 +0 -0
  223. package/web-dist/assets/misans-500-38-D8tTxFuc.woff2 +0 -0
  224. package/web-dist/assets/misans-500-39-DTm3aqJQ.woff2 +0 -0
  225. package/web-dist/assets/misans-500-40-C_akwj8_.woff2 +0 -0
  226. package/web-dist/assets/misans-500-41-RVM1JHQW.woff2 +0 -0
  227. package/web-dist/assets/misans-500-42-MVWIN_il.woff2 +0 -0
  228. package/web-dist/assets/misans-500-43-BtLZYNyD.woff2 +0 -0
  229. package/web-dist/assets/misans-500-44-D0QHMl6G.woff2 +0 -0
  230. package/web-dist/assets/misans-500-45-DRlclX7-.woff2 +0 -0
  231. package/web-dist/assets/misans-500-46-BEWswL4N.woff2 +0 -0
  232. package/web-dist/assets/misans-500-47-COSoxWaW.woff2 +0 -0
  233. package/web-dist/assets/misans-500-48-DZAISq3n.woff2 +0 -0
  234. package/web-dist/assets/misans-500-49-KSfi7MW0.woff2 +0 -0
  235. package/web-dist/assets/misans-500-5-h1SBcwuW.woff2 +0 -0
  236. package/web-dist/assets/misans-500-50-CIVMfG11.woff2 +0 -0
  237. package/web-dist/assets/misans-500-51-BdeNBhBL.woff2 +0 -0
  238. package/web-dist/assets/misans-500-52-CEW4e7ex.woff2 +0 -0
  239. package/web-dist/assets/misans-500-53-lmPCsEnt.woff2 +0 -0
  240. package/web-dist/assets/misans-500-54-B_7AqKQW.woff2 +0 -0
  241. package/web-dist/assets/misans-500-55-B-3RtUnH.woff2 +0 -0
  242. package/web-dist/assets/misans-500-56-g6D0e0cW.woff2 +0 -0
  243. package/web-dist/assets/misans-500-57-D5hp5Yqr.woff2 +0 -0
  244. package/web-dist/assets/misans-500-58-ilnDgEKt.woff2 +0 -0
  245. package/web-dist/assets/misans-500-59-DJYXz8tr.woff2 +0 -0
  246. package/web-dist/assets/misans-500-6-uCdpdDQy.woff2 +0 -0
  247. package/web-dist/assets/misans-500-60-B3Iaqx0p.woff2 +0 -0
  248. package/web-dist/assets/misans-500-61-CMxqg5hc.woff2 +0 -0
  249. package/web-dist/assets/misans-500-62-7FY8O2NQ.woff2 +0 -0
  250. package/web-dist/assets/misans-500-63-JP6vEuJf.woff2 +0 -0
  251. package/web-dist/assets/misans-500-64-Cdub62jh.woff2 +0 -0
  252. package/web-dist/assets/misans-500-65-DVHk_mlC.woff2 +0 -0
  253. package/web-dist/assets/misans-500-66-BX39wRtF.woff2 +0 -0
  254. package/web-dist/assets/misans-500-67-BcsepI3X.woff2 +0 -0
  255. package/web-dist/assets/misans-500-68-etUIUjrh.woff2 +0 -0
  256. package/web-dist/assets/misans-500-69-DfhVmnuA.woff2 +0 -0
  257. package/web-dist/assets/misans-500-70-D6dgy6-B.woff2 +0 -0
  258. package/web-dist/assets/misans-500-71-PkeKjcVn.woff2 +0 -0
  259. package/web-dist/assets/misans-500-72-D66Fnb30.woff2 +0 -0
  260. package/web-dist/assets/misans-500-73-jr9z9870.woff2 +0 -0
  261. package/web-dist/assets/misans-500-74-DsCkPKwe.woff2 +0 -0
  262. package/web-dist/assets/misans-500-75-Dkooq6KM.woff2 +0 -0
  263. package/web-dist/assets/misans-500-76-B0sfieHl.woff2 +0 -0
  264. package/web-dist/assets/misans-500-77-8tflq1ji.woff2 +0 -0
  265. package/web-dist/assets/misans-500-78-COKdMbdp.woff2 +0 -0
  266. package/web-dist/assets/misans-500-79-Bm0rBjSF.woff2 +0 -0
  267. package/web-dist/assets/misans-500-80-B9OoAnyy.woff2 +0 -0
  268. package/web-dist/assets/misans-500-81-DsQ9tpfX.woff2 +0 -0
  269. package/web-dist/assets/misans-500-82-VKsL2Tk2.woff2 +0 -0
  270. package/web-dist/assets/misans-500-83-Ce6mfHYD.woff2 +0 -0
  271. package/web-dist/assets/misans-500-84-D7kPSZLm.woff2 +0 -0
  272. package/web-dist/assets/misans-500-85-W5L0cEfd.woff2 +0 -0
  273. package/web-dist/assets/misans-500-86-DukGOEET.woff2 +0 -0
  274. package/web-dist/assets/misans-500-87-BJ_Lok6j.woff2 +0 -0
  275. package/web-dist/assets/misans-500-88-DbJ11zcA.woff2 +0 -0
  276. package/web-dist/assets/misans-500-89-LNKcEq6M.woff2 +0 -0
  277. package/web-dist/assets/misans-500-90-DNDDgSCd.woff2 +0 -0
  278. package/web-dist/assets/misans-500-91-rT8Q0evu.woff2 +0 -0
  279. package/web-dist/assets/misans-500-97-c8zW_WRq.woff2 +0 -0
  280. package/web-dist/assets/misans-500-cyrillic-CLHOGwgx.woff2 +0 -0
  281. package/web-dist/assets/misans-500-extra-CTAMEQ59.woff2 +0 -0
  282. package/web-dist/assets/misans-500-latin-Be606ott.woff2 +0 -0
  283. package/web-dist/assets/misans-500-latin-ext-BzDyL4sU.woff2 +0 -0
  284. package/web-dist/assets/misans-500-vietnamese-DBOLOqZH.woff2 +0 -0
  285. package/web-dist/assets/mona-sans-latin-ext-wght-normal-BqSk0Z3x.woff2 +0 -0
  286. package/web-dist/assets/mona-sans-latin-wght-normal-Pz49MTQZ.woff2 +0 -0
  287. package/web-dist/assets/noto-sans-sc-100-wght-normal-DrqXJETY.woff2 +0 -0
  288. package/web-dist/assets/noto-sans-sc-101-wght-normal-c94LAl5p.woff2 +0 -0
  289. package/web-dist/assets/noto-sans-sc-102-wght-normal-C1RtbCZr.woff2 +0 -0
  290. package/web-dist/assets/noto-sans-sc-103-wght-normal-DGHo20nu.woff2 +0 -0
  291. package/web-dist/assets/noto-sans-sc-104-wght-normal-CJT2ioDJ.woff2 +0 -0
  292. package/web-dist/assets/noto-sans-sc-105-wght-normal-BFoiJwz2.woff2 +0 -0
  293. package/web-dist/assets/noto-sans-sc-106-wght-normal-D6uUHw3w.woff2 +0 -0
  294. package/web-dist/assets/noto-sans-sc-107-wght-normal-kFQzJDLH.woff2 +0 -0
  295. package/web-dist/assets/noto-sans-sc-108-wght-normal-7aXvqIa2.woff2 +0 -0
  296. package/web-dist/assets/noto-sans-sc-109-wght-normal-CnjNbNmw.woff2 +0 -0
  297. package/web-dist/assets/noto-sans-sc-110-wght-normal-D2JBr045.woff2 +0 -0
  298. package/web-dist/assets/noto-sans-sc-111-wght-normal-ClFr5QXM.woff2 +0 -0
  299. package/web-dist/assets/noto-sans-sc-112-wght-normal-CiZxJMyY.woff2 +0 -0
  300. package/web-dist/assets/noto-sans-sc-113-wght-normal-OgQXOX6x.woff2 +0 -0
  301. package/web-dist/assets/noto-sans-sc-114-wght-normal-CRH9SDZu.woff2 +0 -0
  302. package/web-dist/assets/noto-sans-sc-115-wght-normal-BN__2iBG.woff2 +0 -0
  303. package/web-dist/assets/noto-sans-sc-116-wght-normal-vdFVzbO2.woff2 +0 -0
  304. package/web-dist/assets/noto-sans-sc-117-wght-normal-Dsz3vkzU.woff2 +0 -0
  305. package/web-dist/assets/noto-sans-sc-118-wght-normal-BPEb0gM9.woff2 +0 -0
  306. package/web-dist/assets/noto-sans-sc-119-wght-normal-BfzSEbFz.woff2 +0 -0
  307. package/web-dist/assets/noto-sans-sc-21-wght-normal-DIu_1ZBI.woff2 +0 -0
  308. package/web-dist/assets/noto-sans-sc-22-wght-normal-VXjdYcT-.woff2 +0 -0
  309. package/web-dist/assets/noto-sans-sc-23-wght-normal-4HrHKhpL.woff2 +0 -0
  310. package/web-dist/assets/noto-sans-sc-24-wght-normal-B58DWgHS.woff2 +0 -0
  311. package/web-dist/assets/noto-sans-sc-25-wght-normal-C3Jm6l7O.woff2 +0 -0
  312. package/web-dist/assets/noto-sans-sc-26-wght-normal-YNOygvr_.woff2 +0 -0
  313. package/web-dist/assets/noto-sans-sc-27-wght-normal-BP2pEGQH.woff2 +0 -0
  314. package/web-dist/assets/noto-sans-sc-28-wght-normal-BWPKDbtH.woff2 +0 -0
  315. package/web-dist/assets/noto-sans-sc-29-wght-normal-45aVmHn_.woff2 +0 -0
  316. package/web-dist/assets/noto-sans-sc-30-wght-normal-B8_s30jZ.woff2 +0 -0
  317. package/web-dist/assets/noto-sans-sc-31-wght-normal-DW0t445S.woff2 +0 -0
  318. package/web-dist/assets/noto-sans-sc-32-wght-normal-ShbWPcei.woff2 +0 -0
  319. package/web-dist/assets/noto-sans-sc-33-wght-normal-BOTZYTjM.woff2 +0 -0
  320. package/web-dist/assets/noto-sans-sc-34-wght-normal-BF1rqDSt.woff2 +0 -0
  321. package/web-dist/assets/noto-sans-sc-35-wght-normal-Bosspiyr.woff2 +0 -0
  322. package/web-dist/assets/noto-sans-sc-36-wght-normal-CtU9CyKw.woff2 +0 -0
  323. package/web-dist/assets/noto-sans-sc-37-wght-normal-ghDiyiuv.woff2 +0 -0
  324. package/web-dist/assets/noto-sans-sc-38-wght-normal-4Nz8k_dt.woff2 +0 -0
  325. package/web-dist/assets/noto-sans-sc-39-wght-normal-Ddtm1XVC.woff2 +0 -0
  326. package/web-dist/assets/noto-sans-sc-4-wght-normal-BRbPMj-C.woff2 +0 -0
  327. package/web-dist/assets/noto-sans-sc-40-wght-normal-BVn4-kgX.woff2 +0 -0
  328. package/web-dist/assets/noto-sans-sc-41-wght-normal-CZqajZnl.woff2 +0 -0
  329. package/web-dist/assets/noto-sans-sc-42-wght-normal-CQ4lY28u.woff2 +0 -0
  330. package/web-dist/assets/noto-sans-sc-43-wght-normal-psppu0d8.woff2 +0 -0
  331. package/web-dist/assets/noto-sans-sc-44-wght-normal-cbKJlTUe.woff2 +0 -0
  332. package/web-dist/assets/noto-sans-sc-45-wght-normal-MKIEVRIC.woff2 +0 -0
  333. package/web-dist/assets/noto-sans-sc-46-wght-normal-CErGc6Mt.woff2 +0 -0
  334. package/web-dist/assets/noto-sans-sc-47-wght-normal-C5Kgg1_m.woff2 +0 -0
  335. package/web-dist/assets/noto-sans-sc-48-wght-normal-DFMjDDY6.woff2 +0 -0
  336. package/web-dist/assets/noto-sans-sc-49-wght-normal-B7l_3BZr.woff2 +0 -0
  337. package/web-dist/assets/noto-sans-sc-5-wght-normal-DI846rD9.woff2 +0 -0
  338. package/web-dist/assets/noto-sans-sc-50-wght-normal-C5KfAmFp.woff2 +0 -0
  339. package/web-dist/assets/noto-sans-sc-51-wght-normal-C2bq6hC8.woff2 +0 -0
  340. package/web-dist/assets/noto-sans-sc-52-wght-normal-P18e_Y0j.woff2 +0 -0
  341. package/web-dist/assets/noto-sans-sc-53-wght-normal-Bgkuo2tF.woff2 +0 -0
  342. package/web-dist/assets/noto-sans-sc-54-wght-normal-DL5lmlUd.woff2 +0 -0
  343. package/web-dist/assets/noto-sans-sc-55-wght-normal-BV_Oy6IF.woff2 +0 -0
  344. package/web-dist/assets/noto-sans-sc-56-wght-normal-B4L7Hj8f.woff2 +0 -0
  345. package/web-dist/assets/noto-sans-sc-57-wght-normal-DU8YBsDj.woff2 +0 -0
  346. package/web-dist/assets/noto-sans-sc-58-wght-normal-CE0BKS9u.woff2 +0 -0
  347. package/web-dist/assets/noto-sans-sc-59-wght-normal-Azv2Ku9_.woff2 +0 -0
  348. package/web-dist/assets/noto-sans-sc-6-wght-normal-ozUCEY9O.woff2 +0 -0
  349. package/web-dist/assets/noto-sans-sc-60-wght-normal-C1lngC61.woff2 +0 -0
  350. package/web-dist/assets/noto-sans-sc-61-wght-normal-BxWkGF5M.woff2 +0 -0
  351. package/web-dist/assets/noto-sans-sc-62-wght-normal-CXXMAq-5.woff2 +0 -0
  352. package/web-dist/assets/noto-sans-sc-63-wght-normal-4La5uKvS.woff2 +0 -0
  353. package/web-dist/assets/noto-sans-sc-64-wght-normal-CbIuD2Eg.woff2 +0 -0
  354. package/web-dist/assets/noto-sans-sc-65-wght-normal-BGqvKda2.woff2 +0 -0
  355. package/web-dist/assets/noto-sans-sc-66-wght-normal-BvNozWOY.woff2 +0 -0
  356. package/web-dist/assets/noto-sans-sc-67-wght-normal-CC-WRBV-.woff2 +0 -0
  357. package/web-dist/assets/noto-sans-sc-68-wght-normal-c0FjfUaj.woff2 +0 -0
  358. package/web-dist/assets/noto-sans-sc-69-wght-normal-D596Jjl2.woff2 +0 -0
  359. package/web-dist/assets/noto-sans-sc-70-wght-normal-ColEcmtJ.woff2 +0 -0
  360. package/web-dist/assets/noto-sans-sc-71-wght-normal-ChZfXpdg.woff2 +0 -0
  361. package/web-dist/assets/noto-sans-sc-72-wght-normal-D1VyfEG3.woff2 +0 -0
  362. package/web-dist/assets/noto-sans-sc-73-wght-normal-DwSJRYPJ.woff2 +0 -0
  363. package/web-dist/assets/noto-sans-sc-74-wght-normal-Cte7o1QG.woff2 +0 -0
  364. package/web-dist/assets/noto-sans-sc-75-wght-normal-B0qL41rF.woff2 +0 -0
  365. package/web-dist/assets/noto-sans-sc-76-wght-normal-BPLMO_2x.woff2 +0 -0
  366. package/web-dist/assets/noto-sans-sc-77-wght-normal-Dkbi3pye.woff2 +0 -0
  367. package/web-dist/assets/noto-sans-sc-78-wght-normal-BFUXeSw6.woff2 +0 -0
  368. package/web-dist/assets/noto-sans-sc-79-wght-normal-DoJG4UTB.woff2 +0 -0
  369. package/web-dist/assets/noto-sans-sc-80-wght-normal-BEFypWOO.woff2 +0 -0
  370. package/web-dist/assets/noto-sans-sc-81-wght-normal-ed_TZlfo.woff2 +0 -0
  371. package/web-dist/assets/noto-sans-sc-82-wght-normal-DcxfR5OJ.woff2 +0 -0
  372. package/web-dist/assets/noto-sans-sc-83-wght-normal-C9UjAcMM.woff2 +0 -0
  373. package/web-dist/assets/noto-sans-sc-84-wght-normal-C7NxT478.woff2 +0 -0
  374. package/web-dist/assets/noto-sans-sc-85-wght-normal-DBcDBu1n.woff2 +0 -0
  375. package/web-dist/assets/noto-sans-sc-86-wght-normal-CLJIoPpl.woff2 +0 -0
  376. package/web-dist/assets/noto-sans-sc-87-wght-normal-DYfJ-vGE.woff2 +0 -0
  377. package/web-dist/assets/noto-sans-sc-88-wght-normal-ClNDOCGW.woff2 +0 -0
  378. package/web-dist/assets/noto-sans-sc-89-wght-normal-IQU_KQA8.woff2 +0 -0
  379. package/web-dist/assets/noto-sans-sc-90-wght-normal-BWcrRNoG.woff2 +0 -0
  380. package/web-dist/assets/noto-sans-sc-91-wght-normal-CGekvDdH.woff2 +0 -0
  381. package/web-dist/assets/noto-sans-sc-97-wght-normal-BsBHkXi3.woff2 +0 -0
  382. package/web-dist/assets/noto-sans-sc-98-wght-normal-BZqSA1r_.woff2 +0 -0
  383. package/web-dist/assets/noto-sans-sc-99-wght-normal-Q_IptJM5.woff2 +0 -0
  384. package/web-dist/assets/noto-sans-sc-cyrillic-wght-normal-CRA5_Pf1.woff2 +0 -0
  385. package/web-dist/assets/noto-sans-sc-latin-ext-wght-normal-CqPqLCfe.woff2 +0 -0
  386. package/web-dist/assets/noto-sans-sc-latin-wght-normal-DYAKOyvl.woff2 +0 -0
  387. package/web-dist/assets/noto-sans-sc-vietnamese-wght-normal-y5SU35Rl.woff2 +0 -0
  388. package/web-dist/assets/ordinal-Cboi1Yqb.js +1 -0
  389. package/web-dist/assets/pegDiagram-XKGWAZYB-BUF_GJ0B.js +1 -0
  390. package/web-dist/assets/pieDiagram-E7YTZNPT-nweubbsU.js +39 -0
  391. package/web-dist/assets/quadrantDiagram-AXDQQJYC-B_r4Wkxy.js +7 -0
  392. package/web-dist/assets/railroadDiagram-O6MQD6OU-Dn7nm0MX.js +1 -0
  393. package/web-dist/assets/requirementDiagram-BXWQKSXE-DaagYJ49.js +84 -0
  394. package/web-dist/assets/sankeyDiagram-P5KCCOFB-CqEX6H-B.js +40 -0
  395. package/web-dist/assets/sequenceDiagram-WJ2MYXX4-BzLfQO55.js +162 -0
  396. package/web-dist/assets/sizeCapture-INFHLROL-BcUuVH2L.js +1 -0
  397. package/web-dist/assets/stateDiagram-D77RDMKH-BO3LWLvT.js +1 -0
  398. package/web-dist/assets/stateDiagram-v2-MP3YSRHH-B-EuucjM.js +1 -0
  399. package/web-dist/assets/swimlanes-42K2YHIH-etP_hkIB.js +1 -0
  400. package/web-dist/assets/swimlanesDiagram-VR7AAH4N-0JbFdDbQ.js +8 -0
  401. package/web-dist/assets/timeline-definition-24CTP7MA-CTLt2XCT.js +120 -0
  402. package/web-dist/assets/vennDiagram-4TSXK5OY-DPO6ZSvH.js +34 -0
  403. package/web-dist/assets/wardleyDiagram-VM6X3IG4-DFofGg8b.js +78 -0
  404. package/web-dist/assets/{xterm-B2oS8Mg8.js → xterm-RdyTuaAH.js} +1 -1
  405. package/web-dist/assets/xychartDiagram-S5SC5T6Z-Bgtb34Gu.js +7 -0
  406. package/web-dist/fonts-licenses/ibm-plex-sans-condensed.txt +93 -0
  407. package/web-dist/fonts-licenses/ibm-plex-sans.txt +93 -0
  408. package/web-dist/fonts-licenses/jetbrains-mono.txt +93 -0
  409. package/web-dist/fonts-licenses/misans.txt +119 -0
  410. package/web-dist/fonts-licenses/mona-sans.txt +93 -0
  411. package/web-dist/fonts-licenses/noto-sans-sc.txt +93 -0
  412. package/web-dist/index.html +24 -5
  413. package/web-dist/workbench-ui.css +266 -0
  414. package/dist/chunk-3I2XOPJB.js.map +0 -1
  415. package/dist/chunk-A3QANMU5.js.map +0 -1
  416. package/dist/chunk-L2H3K4DT.js +0 -7
  417. package/dist/chunk-L2H3K4DT.js.map +0 -1
  418. package/dist/chunk-S3BI6R7I.js +0 -14
  419. package/dist/chunk-S3BI6R7I.js.map +0 -1
  420. package/dist/chunk-TOOTEEM4.js.map +0 -1
  421. package/web-dist/assets/highlighter-CUb4XKtQ.js +0 -2
  422. package/web-dist/assets/index-CmfumtXy.js +0 -570
  423. package/web-dist/assets/index-Ywc2uM5u.css +0 -1
@@ -1,29 +1,50 @@
1
- import { SpawnConfiner, ThinkingLevelName, OmniMessage, ToolCallPayload, ProjectConfig, ModelRef, ListEndpointModelsOptions, ProxyEnvPolicy, ControlEnvContext, ApproveFn, CompactAvailability, SessionTitleResult, BackgroundCommandInfo, ToolDetachResult, BackgroundSubagentInfo, SubagentMessageOptions, SubagentMessageOutcome } from '@prismshadow/penguin-core';
2
- import { SandboxProviderSource, SandboxSettings, SandboxDimension } from '@prismshadow/penguin-core/plugin';
1
+ import { SpawnConfiner, OmniMessage, ThinkingLevelName, ToolCallPayload, ApprovalDecision, ProjectConfig, ModelRef, PluginTables, ListEndpointModelsOptions, ModelRequestContext, ProxyEnvPolicy, ControlEnvContext, AgentAssembly, ApproveFn, StopReason, CompactAvailability, SessionTitleResult, BackgroundCommandInfo, ToolDetachResult, BackgroundSubagentInfo, SubagentMessageOptions, SubagentMessageOutcome } from '@prismshadow/penguin-core';
2
+ import { SandboxProviderSource, SandboxSettings, SandboxDimension, SandboxLimit } from '@prismshadow/penguin-core/plugin';
3
3
  import { Slot, Opaque } from '@prismshadow/penguin-core/kernel';
4
- import { ApprovalMode, SessionSource, ModelRefDto, ChatDefaultsDto, CommandPolicyDto, ModelVisionDetectRequest, ModelVisionDetectResponse, ModelTestRequest, ModelTestResponse, ModelProtocolDetectRequest, ModelProtocolDetectResponse, EndpointModelListRequest, EndpointModelListResponse, ModelsResponse, ModelsUpdateRequest, MessagingChannel as MessagingChannel$1, SessionInfo, SessionCategory, SessionCategoryCounts, ServerEvent, SessionStatus, SessionBackgroundTasks, PendingSteeringInfo, PendingFollowUpInfo, AgentConfigDto, AgentConfigUpdateRequest, AgentKernelUpdateResponse, VaultResponse, VaultUpdateRequest, PluginUpdateRef, MessagingRuntimeStatus, ContributionsResponse, RendererRef } from '../api/types.js';
4
+ import { ApprovalMode, ModelRefDto, ChatDefaultsDto, CommandPolicyDto, ModelVisionDetectRequest, ModelVisionDetectResponse, ModelTestRequest, ModelTestResponse, ModelProtocolDetectRequest, ModelProtocolDetectResponse, EndpointModelListRequest, EndpointModelListResponse, ModelsResponse, ModelsUpdateRequest, ProviderConnectionUpdate, PresetSyncMode, PresetSyncResponse, ServerEvent, MessagingChannel, UnavailableSandboxBackend, SessionSandboxPreset, SessionSandbox, SessionInfo, SessionCategory, SessionCategoryCounts, ModelScopeAuthFlowStatusResponse, SessionStatus, PendingSteeringInfo, SessionBackgroundTasks, PendingFollowUpInfo, AgentConfigDto, AgentConfigUpdateRequest, AgentKernelUpdateResponse, VaultResponse, VaultUpdateRequest, PluginUpdateRef, MessagingRuntimeStatus, PluginConfiguration, ContributionsResponse, RendererRef } from '../api/types.js';
5
+ export { PluginConfigField } from '../api/types.js';
5
6
  import { ThinkingLevelName as ThinkingLevelName$1 } from '@prismshadow/penguin-core/interfaces';
7
+ import { SessionSource } from '@prismshadow/penguin-core/omnimessage';
6
8
  import { Hono } from 'hono';
7
- import '@prismshadow/penguin-core/omnimessage';
9
+ import '@prismshadow/penguin-hmr';
10
+
11
+ interface UserRow {
12
+ userId: string;
13
+ passwordHash: string;
14
+ isAdmin: boolean;
15
+ /** Still using the initial password (seeded / set by an admin); cleared to 0 once the user changes it. */
16
+ passwordIsInitial: boolean;
17
+ /** Nickname shown in place of the id; null = never set. */
18
+ displayName: string | null;
19
+ /** Avatar as a data URL; null = never set. */
20
+ avatar: string | null;
21
+ createdAt: string;
22
+ }
8
23
 
9
24
  /**
10
- * The sandbox service: owns the registered backends, routes each spawn's policy to a
11
- * backend that implements the dimensions it requires, and produces the SpawnConfiner
12
- * closure the runtime transports into core's command-session seam.
13
- *
14
- * Routing is by CAPABILITY, not by registration order alone: a policy requiring only
15
- * `fs-write` goes to the first backend that covers it (the DSH adaptor, which works on
16
- * Linux/macOS/Windows alike), while a policy also requiring `network` or `mask-paths`
17
- * goes to the first backend implementing those (penguin-bwrap). Registration order
18
- * only breaks ties between backends that both cover the request. A request nothing
19
- * covers fails closed — never a silent unconfined run, and never a silently dropped
20
- * dimension.
25
+ * "desktop" and "setup" may set a password without the old one (theirs is random and was never
26
+ * shown); desktop-only routes open to "desktop" alone. "token" is the local API token's
27
+ * authority, whether presented as a Bearer header or exchanged for a Remote cookie.
28
+ * Exchanging the credential must not grant the person's privileges. Anything else reads as
29
+ * "password".
21
30
  */
31
+ type SessionVia = "password" | "desktop" | "setup" | "token";
22
32
 
23
33
  declare class SandboxService {
24
- private readonly mounted;
25
- /** name → why it failed to load; surfaced in the fail-closed message. */
26
- private readonly loadErrors;
34
+ private mounted;
35
+ /**
36
+ * name → why it FAILED: it could not load, or failed its check on a host it is meant for —
37
+ * never silently absent. Surfaced in the fail-closed message and on the settings card.
38
+ */
39
+ private loadErrors;
40
+ /**
41
+ * The backends that declined: this host is not theirs (a Linux backend on Windows). Nothing
42
+ * is wrong with a deployment that installs one backend per platform, so a decline is not a
43
+ * failure — it is listed only when NO backend serves, where it is the explanation.
44
+ */
45
+ private declinedNames;
46
+ /** Every registration in routing order, with how its latest load settled. */
47
+ private readonly registrations;
27
48
  /**
28
49
  * Ships with confinement OFF (`danger-full-access`): the default flips to
29
50
  * workspace-write together with the deployment-facing config surface, so a
@@ -40,6 +61,14 @@ declare class SandboxService {
40
61
  * exists for deployments flipping the mode in the first milliseconds after boot.
41
62
  */
42
63
  constructor(registrations?: Iterable<[string, SandboxProviderSource]>);
64
+ /** Rebuilds the mounted / failed / declined views from the registrations, in routing order. */
65
+ private record;
66
+ /**
67
+ * Loads again every backend whose load failed and that registered a loader (a bare promise
68
+ * cannot be re-run). Called after the sandbox's settings are saved: a backend whose check
69
+ * failed on its own setting mounts once that setting is fixed. Resolves when those settle.
70
+ */
71
+ retryFailed(): Promise<void>;
43
72
  /** Resolves when every registered backend has loaded (or failed to). */
44
73
  whenReady(): Promise<void>;
45
74
  /** Replaces the active settings (the config surface's write path). */
@@ -55,10 +84,22 @@ declare class SandboxService {
55
84
  * refusing the swap beats a swap that silently un-confines the deployment.
56
85
  */
57
86
  parkedSettings(): SandboxSettings | undefined;
58
- /** The mounted backends and what each implements (diagnostics / the config surface). */
87
+ /** The backends that failed to load, each with why (diagnostics / the config surface). */
88
+ failures(): Array<{
89
+ name: string;
90
+ reason: string;
91
+ }>;
92
+ /** The backends that declined because this host is not theirs (the config surface). */
93
+ declined(): string[];
94
+ /**
95
+ * The mounted backends and what each implements, in routing preference: the one a policy
96
+ * goes to first (diagnostics / the config surface).
97
+ */
59
98
  backends(): Array<{
60
99
  name: string;
61
100
  dimensions: readonly SandboxDimension[];
101
+ mechanism?: string;
102
+ limits?: readonly SandboxLimit[];
62
103
  }>;
63
104
  /**
64
105
  * The closure handed to the runtime: rewrites each spawn's argv under the active
@@ -66,11 +107,19 @@ declare class SandboxService {
66
107
  * serving during a hot-swap freeze window.
67
108
  */
68
109
  confiner(): SpawnConfiner;
69
- /** The first mounted backend implementing every required dimension, or a fail-closed throw. */
110
+ /**
111
+ * The same closure under a policy the caller keeps — a Session's own snapshot. The
112
+ * backends are still this service's, so a backend change reaches every Session.
113
+ */
114
+ confinerFor(policyOf: () => SandboxSettings): SpawnConfiner;
115
+ /**
116
+ * The preferred mounted backend implementing every required dimension (byPreference), or a
117
+ * fail-closed throw.
118
+ */
70
119
  private pick;
71
120
  private failedSuffix;
72
121
  }
73
- declare const Sandbox_base: (abstract new () => Pick<SandboxService, "configure" | "currentSettings" | "parkedSettings" | "backends" | "confiner" | "whenReady">) & (<C>(target: C, context: ClassDecoratorContext) => C);
122
+ declare const Sandbox_base: (abstract new () => Pick<SandboxService, "failures" | "configure" | "currentSettings" | "parkedSettings" | "backends" | "declined" | "retryFailed" | "confiner" | "confinerFor" | "whenReady">) & (<C>(target: C, context: ClassDecoratorContext) => C);
74
123
  /** Confinement: settings, the active confiner, and which backends are mounted. */
75
124
  declare abstract class Sandbox extends Sandbox_base {
76
125
  }
@@ -85,6 +134,29 @@ interface SandboxSlots {
85
134
  }, SandboxProviderSource>;
86
135
  }
87
136
 
137
+ /** What a terminal puts first on PATH, where the startup files that do it live, and its first line. */
138
+ interface TerminalPathFirst {
139
+ /** The directories that must lead PATH once the shell is ready, in order: the CLI shim dir. */
140
+ dirs: readonly string[];
141
+ /** The directory holding the startup files, see {@link shellStartupDir}. */
142
+ startupDir: string;
143
+ /** A development instance's first line, for the shell to print at its first prompt. Absent or null: none. */
144
+ notice?: ShellNotice | null;
145
+ }
146
+ /**
147
+ * The first line of a development instance's terminal, printed by the shell itself at its first
148
+ * prompt, after its own startup files: `text` while `penguin` there still targets this instance,
149
+ * else `elsewhere`'s two halves around `VAR=value` — when those files set PENGUIN_API_URL, which
150
+ * names a server outright, or a PENGUIN_HOME that is not `root` (a trailing `/` aside), which the
151
+ * shim then leaves to the CLI.
152
+ */
153
+ interface ShellNotice {
154
+ text: string;
155
+ /** This instance's data root. */
156
+ root: string;
157
+ elsewhere: readonly [before: string, after: string];
158
+ }
159
+
88
160
  /**
89
161
  * Screen snapshot -> ANSI restore stream, plus plain-text capture.
90
162
  *
@@ -141,6 +213,42 @@ interface CreateTerminalSessionOptions {
141
213
  * absent falls back to the packaged require (see pty-module.ts).
142
214
  */
143
215
  assets?: () => string | null;
216
+ /**
217
+ * What leads PATH once the shell's own startup files have run: the server's CLI shim
218
+ * directory, so `penguin` here is the one the Agent's commands get (see shell-startup.ts).
219
+ * Absent or null, the shell starts with the inherited PATH as it is.
220
+ */
221
+ pathFirst?: TerminalPathFirst | null;
222
+ /**
223
+ * One line shown above everything the shell prints — on a development instance, which
224
+ * `penguin` this terminal runs, or why it is not this instance's (cli-notice.ts). Written to
225
+ * the screen, never to the shell: nothing runs, and the user's prompt follows it. A line with
226
+ * a `target` the shell prints itself instead, at its first prompt, where it can
227
+ * (shell-startup.ts ShellNotice): only then is it known what the user's own startup files left
228
+ * `penguin` pointing at.
229
+ */
230
+ notice?: TerminalNotice | null;
231
+ /**
232
+ * Whether the shell keeps the server process's `PENGUIN_HOME` and `PENGUIN_WEB_DB`. Only
233
+ * where no shim names this server's data root for `penguin` — a server with no CLI to offer,
234
+ * or a runtime older than the shim's root default — so that `penguin` there still finds this
235
+ * server the way it did before. Default false: every `PENGUIN_*` stays out.
236
+ */
237
+ inheritDataRoot?: boolean;
238
+ }
239
+ /** A line the server shows at the top of a new terminal; `warning` when something is wrong. */
240
+ interface TerminalNotice {
241
+ text: string;
242
+ tone: "info" | "warning";
243
+ /**
244
+ * What `text` takes for granted: that `penguin` here targets this data root. On a development
245
+ * instance's line, which then says otherwise — `elsewhere`'s two halves around `VAR=value` —
246
+ * when the shell's own startup files set PENGUIN_API_URL, or a PENGUIN_HOME that is not `root`.
247
+ */
248
+ target?: {
249
+ root: string;
250
+ elsewhere: readonly [before: string, after: string];
251
+ };
144
252
  }
145
253
  interface TerminalSessionInfo {
146
254
  id: string;
@@ -223,20 +331,18 @@ interface CreateTerminalRequest {
223
331
  }
224
332
  /** A live pty: a host-like object (it owns a process), compared by name. */
225
333
  type Terminal = Opaque<"TerminalSession", TerminalSession>;
226
- declare const Terminals_base: (abstract new () => {
227
- create(request: CreateTerminalRequest): Promise<Terminal>;
228
- adopt(ids: readonly string[]): void;
229
- quiesce(): void;
230
- handleIds(): string[];
231
- require(id: string, userId: string): Terminal;
232
- get(id: string): Terminal | undefined;
233
- list(userId: string): Terminal[];
234
- listInfo(userId: string): TerminalSessionInfo[];
235
- kill(id: string, userId: string): void;
236
- disposeAll(): void;
237
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
238
334
  /** Live ptys: the spawn primitive behind /api/terminals and the terminal WebSocket. */
239
- declare abstract class Terminals extends Terminals_base {
335
+ declare abstract class Terminals {
336
+ abstract create(request: CreateTerminalRequest): Promise<Terminal>;
337
+ abstract adopt(ids: readonly string[]): void;
338
+ abstract quiesce(): void;
339
+ abstract handleIds(): string[];
340
+ abstract require(id: string, userId: string): Terminal;
341
+ abstract get(id: string): Terminal | undefined;
342
+ abstract list(userId: string): Terminal[];
343
+ abstract listInfo(userId: string): TerminalSessionInfo[];
344
+ abstract kill(id: string, userId: string): void;
345
+ abstract disposeAll(): void;
240
346
  }
241
347
 
242
348
  /**
@@ -264,37 +370,67 @@ interface RecallableFile {
264
370
  mime: string;
265
371
  }
266
372
 
267
- declare const SessionIndex_base: (abstract new () => {
268
- insert(row: SessionRow): void;
269
- insertOrIgnore(row: SessionRow): void;
270
- insertFork(sourceSessionId: string, row: SessionRow): SessionRow;
271
- markOrgClient(sessionIds: readonly string[]): void;
272
- markHasTrace(sessionId: string): void;
273
- markDriven(sessionId: string, at: string): void;
274
- touchLastActive(sessionId: string, at: string): void;
275
- findById(sessionId: string): SessionRow | null;
276
- listByAgent(projectId: string, agentId: string): SessionRow[];
277
- listByProject(projectId: string): SessionRow[];
278
- updateApprovalMode(sessionId: string, mode: ApprovalMode): void;
279
- updateThinkingLevel(sessionId: string, level: ThinkingLevelName): void;
280
- updateTitle(sessionId: string, title: string): void;
281
- updateTitleIfNull(sessionId: string, title: string): void;
282
- setArchived(sessionId: string, archivedAt: string | null): void;
283
- replaceId(oldSessionId: string, newSessionId: string): void;
284
- deleteByAgent(projectId: string, agentId: string): void;
285
- deleteByProject(projectId: string): void;
286
- deleteById(sessionId: string): void;
287
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
373
+ /** The open streaming fragments of running sessions, as their holder uses them — what {@link LiveTailTracker} implements. */
374
+ interface LiveTail {
375
+ observe(sessionId: string, msg: OmniMessage): void;
376
+ fragments(sessionId: string): OmniMessage[];
377
+ clear(sessionId: string): void;
378
+ }
379
+
288
380
  /** SessionIndex: the mechanism SessionsRepo implements. */
289
- declare abstract class SessionIndex extends SessionIndex_base {
381
+ declare abstract class SessionIndex {
382
+ abstract insert(row: SessionRow): void;
383
+ abstract insertOrIgnore(row: SessionRow): void;
384
+ abstract insertFork(sourceSessionId: string, row: SessionRow): SessionRow;
385
+ abstract markOrgClient(sessionIds: readonly string[]): void;
386
+ abstract markHasTrace(sessionId: string): void;
387
+ abstract markDriven(sessionId: string, at: string): void;
388
+ abstract touchLastActive(sessionId: string, at: string): void;
389
+ abstract findById(sessionId: string): SessionRow | null;
390
+ abstract listByAgent(projectId: string, agentId: string): SessionRow[];
391
+ abstract listByProject(projectId: string): SessionRow[];
392
+ abstract updateApprovalMode(sessionId: string, mode: ApprovalMode): void;
393
+ abstract updateSandbox(sessionId: string, sandbox: SandboxSettings): void;
394
+ abstract updateThinkingLevel(sessionId: string, level: ThinkingLevelName): void;
395
+ abstract updateTitle(sessionId: string, title: string): void;
396
+ abstract updateTitleIfNull(sessionId: string, title: string): void;
397
+ abstract updateModel(sessionId: string, provider: string, modelId: string): void;
398
+ abstract setListing(sessionIds: readonly string[], listing: "archived" | "kept", at: string): void;
399
+ abstract replaceId(oldSessionId: string, newSessionId: string): void;
400
+ abstract deleteByAgent(projectId: string, agentId: string): void;
401
+ abstract deleteByProject(projectId: string): void;
402
+ abstract deleteById(sessionId: string): void;
290
403
  }
291
- declare const SessionOrigins_base: (abstract new () => {
292
- set(sessionId: string, source: SessionSource | null): void;
293
- get(sessionId: string): SessionSource | null | undefined;
294
- delete(sessionId: string): void;
404
+ /**
405
+ * What a run that has just ended asks of the CURRENT generation: start the next queued
406
+ * follow-up, or deliver background notices that raced the run's exit. A run outlives the
407
+ * generation that launched it (see AgentState.current), and what it starts next is the
408
+ * current logic's to start.
409
+ */
410
+ interface RunStarter {
411
+ startQueuedFollowUp(sessionId: string): Promise<void>;
412
+ startBackgroundNoticeTask(sessionId: string): Promise<void>;
413
+ }
414
+ declare const AgentState_base: (abstract new () => {
415
+ readonly entries: Map<string, RuntimeEntry>;
416
+ readonly childRoots: Map<string, string>;
417
+ readonly locks: Map<string, Promise<unknown>>;
418
+ readonly deletingAgents: Set<string>;
419
+ readonly deletingSessions: Set<string>;
420
+ readonly agentGenerations: Map<string, number>;
421
+ readonly liveTail: LiveTail;
422
+ /** The generation working on this state right now; every generation points it at itself when it takes the state over. */
423
+ current: RunStarter | null;
295
424
  }) & (<C>(target: C, context: ClassDecoratorContext) => C);
425
+ /** AgentState: the mechanism AgentStateStore implements — the Session runtime's in-memory state, data only. */
426
+ declare abstract class AgentState extends AgentState_base {
427
+ }
296
428
  /** SessionOrigins: the mechanism SessionSources implements. */
297
- declare abstract class SessionOrigins extends SessionOrigins_base {
429
+ declare abstract class SessionOrigins {
430
+ /** `null`: the Session's Trace head records no source (written before it was required). */
431
+ abstract set(sessionId: string, source: SessionSource | null): void;
432
+ abstract get(sessionId: string): SessionSource | null | undefined;
433
+ abstract delete(sessionId: string): void;
298
434
  }
299
435
 
300
436
  /**
@@ -306,9 +442,13 @@ interface SessionRow {
306
442
  sessionId: string;
307
443
  projectId: string;
308
444
  agentId: string;
309
- /** Provider group of the session's model (pairs with `modelId` to form the model reference). */
445
+ /**
446
+ * Provider group of the session's **current** model (pairs with `modelId` to form the model
447
+ * reference). Moves with each in-session switch (`updateModel`); the Trace's latest
448
+ * `session_meta` is the durable truth, and the runtime reconciles a row it disagrees with.
449
+ */
310
450
  provider: string;
311
- /** Upstream model_id of the session's model (sent as-is to AgentHub; never concatenated). */
451
+ /** Upstream model_id of the session's current model (sent as-is to MMSP; never concatenated). */
312
452
  modelId: string;
313
453
  workspace: string;
314
454
  approvalMode: ApprovalMode;
@@ -320,21 +460,33 @@ interface SessionRow {
320
460
  thinkingLevel?: ThinkingLevelName$1 | null;
321
461
  /** Auto-generated session title; NULL = not yet generated (frontend shows "New Conversation"). */
322
462
  title: string | null;
323
- /** Archive timestamp, ISO; NULL = not archived (omitting on insert defaults to NULL). */
463
+ /**
464
+ * When the user archived it, ISO; NULL = not archived by hand (omitting on insert defaults to
465
+ * NULL). Archived or not is the listing rule's answer (runtime/session-sources.ts listCategory):
466
+ * a background Session without `keptAt` is filed away too.
467
+ */
324
468
  archivedAt?: string | null;
469
+ /**
470
+ * The "keep in sidebar" mark, ISO: written when the user unarchives the Session, cleared when
471
+ * they archive it, never written by a run. It is what keeps a background Session (api /
472
+ * schedule / cli) listed among the conversations; NULL = the listing rule decides.
473
+ */
474
+ keptAt?: string | null;
325
475
  /**
326
476
  * Creating client: "web" (created via the Web App), "cli" (created through the API by
327
477
  * the CLI, or adopted from a Trace a legacy CLI-direct run left behind), "org" (opened by
328
- * the organization runtime — a desk or a ticket session); NULL = legacy row from before
329
- * the column existed, treated as web. Free text in SQLite, so a new value needs no
330
- * migration. Provenance that outlives its organization: development mode's list is the
331
- * one reader that filters on it, and it hides "org" rows whether or not the organization
332
- * still exists. The schedule/subagent SOURCE is deliberately NOT a row field — core
478
+ * the organization runtime — a desk or a ticket session — or spawned as a sub-session of
479
+ * one, which inherits the stamp at registration), "api" (created by an Agent API run — the
480
+ * only rows the API may continue), "workbench" (created by the Workbench host); NULL = legacy row from before the column existed,
481
+ * treated as web. Free text in SQLite, so a new value needs no migration.
482
+ * Provenance that outlives its organization: development mode's list is the one reader
483
+ * that filters on it (through the list's `excludeOrg`), and it hides "org" rows whether or
484
+ * not the organization still exists. The schedule/subagent SOURCE is deliberately NOT a row field — core
333
485
  * session_meta in the Trace stays the single source of truth for it
334
486
  * (runtime/session-sources.ts); `client` is a separate, DB-only axis that meta never
335
487
  * records.
336
488
  */
337
- client?: "web" | "cli" | "org" | null;
489
+ client?: "web" | "cli" | "org" | "api" | "workbench" | null;
338
490
  /** Cache: a Trace record exists (set at task start / adoption / subagent registration; backfilled by list hydration). */
339
491
  hasTrace?: boolean;
340
492
  /**
@@ -347,6 +499,11 @@ interface SessionRow {
347
499
  */
348
500
  lastActiveAt: string;
349
501
  createdAt: string;
502
+ /**
503
+ * The Session's own sandbox policy, snapshotted from the server's settings at creation.
504
+ * NULL = a row from before the column: it takes the settings at its next command.
505
+ */
506
+ sandbox?: SandboxSettings | null;
350
507
  }
351
508
 
352
509
  /**
@@ -361,6 +518,9 @@ interface SessionRow {
361
518
  * `POST /approvals/:toolCallId`; no timeout — pending approvals are resolved to deny
362
519
  * when the Task is interrupted (then proceeds through the abort flow).
363
520
  *
521
+ * An unattended Session takes that one route differently: there is no frontend to ask, so
522
+ * the call is denied on the spot instead of suspending forever (see `unattended` below).
523
+ *
364
524
  * Every approval decision re-reads the current approval_mode (`getMode` reads the DB),
365
525
  * so mode changes take effect immediately.
366
526
  * Docs: /docs/tools § "Approval".
@@ -370,6 +530,15 @@ interface PendingApproval {
370
530
  toolCall: OmniMessage<ToolCallPayload>;
371
531
  origin?: string[];
372
532
  }
533
+ /** A Session runtime's pending approvals, as its holder uses them — what {@link ApprovalRegistry} implements. */
534
+ interface Approvals {
535
+ readonly size: number;
536
+ list(): PendingApproval[];
537
+ wait(toolCall: OmniMessage<ToolCallPayload>): Promise<ApprovalDecision>;
538
+ decide(toolCallId: string, decision: ApprovalDecision): boolean;
539
+ denyAll(): void;
540
+ denyMain(): void;
541
+ }
373
542
 
374
543
  /** A numbered channel event; `id` is `<epoch>-<seq>`, `data` is serialized single-line JSON. */
375
544
  interface ChannelEvent {
@@ -518,15 +687,13 @@ interface TieredRates {
518
687
  offPeak: PricingRates;
519
688
  }
520
689
 
521
- declare const Errors_base: (abstract new () => {
522
- record(args: ErrorRecordArgs): void;
523
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
524
690
  /** Errors: the mechanism ErrorRecorder implements. */
525
- declare abstract class Errors extends Errors_base {
691
+ declare abstract class Errors {
692
+ abstract record(args: ErrorRecordArgs): void;
526
693
  }
527
694
 
528
695
  /** Capture-site source (maps one-to-one to error_records.source). */
529
- type ErrorSource = "http" | "organization" | "session" | "llm" | "environment" | "compaction" | "usage" | "title" | "subagent" | "process" | "schedule" | "messaging";
696
+ type ErrorSource = "http" | "organization" | "session" | "llm" | "environment" | "compaction" | "usage" | "title" | "subagent" | "process" | "schedule" | "messaging" | "id_suggest" | "plugin";
530
697
  /** Error classification: see file header — the criterion is "does a human need to step in". */
531
698
  type ErrorKind = "expected" | "unexpected";
532
699
  /** Attribution context (all optional: the login endpoint has no Project, and process-level fallback has no request at all). */
@@ -582,10 +749,25 @@ interface TitleNotifier {
582
749
  }
583
750
 
584
751
  /**
585
- * trace_files / trace_sessions repo: the Trace-tree derived cache (see schema.ts and
586
- * services/trace-index.ts for the cache rules). Pure row access — reconciliation
587
- * policy (mtime gates, head-reads) lives in the service.
752
+ * In-process registry of Session sources, derived from core `session_meta` — the single
753
+ * source of truth for what kind of conversation a Session is (the DB stores no `source`
754
+ * column).
755
+ *
756
+ * Populated wherever the server actually has the meta in hand: Session creation
757
+ * (SessionService reads the just-created core Session's meta), subagent registration
758
+ * (SessionManager reads the forwarded child meta), forks (always `user`), and Trace adoption /
759
+ * lazy list resolution (the trace index's head read). A value read back from a Trace is
760
+ * narrowed through core's `normalizeSessionSource` (the retired `benchmark` reads as `cli`),
761
+ * except that a head recording no source is kept as `null`: what it reads as depends on the
762
+ * Session's index row (`readRecordedSource`), which a head read does not have. An absent entry
763
+ * means "unknown": the list path resolves it from the Trace once per process lifetime.
764
+ */
765
+
766
+ /**
767
+ * What a Session's Trace head records as its source: one of the sources, or `null` for a head
768
+ * written before the source was required, which records none.
588
769
  */
770
+ type RecordedSource = SessionSource | null;
589
771
 
590
772
  /** One indexed Trace shard. */
591
773
  interface TraceFileRow {
@@ -601,7 +783,12 @@ interface TraceSessionRow {
601
783
  sessionId: string;
602
784
  projectId: string;
603
785
  agentId: string;
604
- source: SessionSource | null;
786
+ /**
787
+ * The head's session_meta source, narrowed; `null` when the head records none (written before
788
+ * the source was required; read it with the row's client, `readRecordedSource`). Meaningful
789
+ * only once `metaRead`.
790
+ */
791
+ source: RecordedSource;
605
792
  workspace: string;
606
793
  title: string | null;
607
794
  provider: string | null;
@@ -610,38 +797,37 @@ interface TraceSessionRow {
610
797
  metaRead: boolean;
611
798
  }
612
799
 
613
- declare const TraceIndexStore_base: (abstract new () => {
614
- upsertFile(row: TraceFileRow): void;
615
- updateFileSize(projectId: string, agentId: string, sessionId: string, fileIndex: number, sizeBytes: number): void;
616
- getPageStats(projectId: string, agentId: string, sessionId: string, fileIndex: number): string | null;
617
- setPageStats(projectId: string, agentId: string, sessionId: string, fileIndex: number, sizeBytes: number, pageStats: string): void;
618
- deleteFile(projectId: string, agentId: string, sessionId: string, fileIndex: number): void;
619
- listFilesByAgent(projectId: string, agentId: string): TraceFileRow[];
620
- listFilesBySession(projectId: string, agentId: string, sessionId: string): TraceFileRow[];
621
- findAgentBySession(projectId: string, sessionId: string): string | null;
622
- upsertSession(row: TraceSessionRow): void;
623
- getSession(sessionId: string): TraceSessionRow | null;
624
- listSessionsByAgent(projectId: string, agentId: string): TraceSessionRow[];
625
- deleteBySession(sessionId: string): void;
626
- deleteByAgent(projectId: string, agentId: string): void;
627
- deleteByProject(projectId: string): void;
628
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
629
800
  /** TraceIndexStore: the mechanism TraceIndexRepo implements. */
630
- declare abstract class TraceIndexStore extends TraceIndexStore_base {
801
+ declare abstract class TraceIndexStore {
802
+ abstract upsertFile(row: TraceFileRow): void;
803
+ abstract updateFileSize(projectId: string, agentId: string, sessionId: string, fileIndex: number, sizeBytes: number): void;
804
+ abstract getPageStats(projectId: string, agentId: string, sessionId: string, fileIndex: number): string | null;
805
+ abstract setPageStats(projectId: string, agentId: string, sessionId: string, fileIndex: number, sizeBytes: number, pageStats: string): void;
806
+ abstract deleteFile(projectId: string, agentId: string, sessionId: string, fileIndex: number): void;
807
+ abstract listFilesByAgent(projectId: string, agentId: string): TraceFileRow[];
808
+ abstract listFilesBySession(projectId: string, agentId: string, sessionId: string): TraceFileRow[];
809
+ abstract findAgentBySession(projectId: string, sessionId: string): string | null;
810
+ abstract upsertSession(row: TraceSessionRow): void;
811
+ abstract getSession(sessionId: string): TraceSessionRow | null;
812
+ abstract listSessionsByAgent(projectId: string, agentId: string): TraceSessionRow[];
813
+ abstract deleteBySession(sessionId: string): void;
814
+ abstract deleteByAgent(projectId: string, agentId: string): void;
815
+ abstract deleteByProject(projectId: string): void;
631
816
  }
632
- declare const TraceIndex_base: (abstract new () => {
633
- readonly counters: {
817
+ /** TraceIndex: the mechanism TraceIndexService implements. */
818
+ declare abstract class TraceIndex {
819
+ abstract readonly counters: {
634
820
  gateStats: number;
635
821
  dirScans: number;
636
822
  headReads: number;
637
823
  };
638
- reconcileAgent(projectId: string, agentId: string, opts?: {
824
+ abstract reconcileAgent(projectId: string, agentId: string, opts?: {
639
825
  force?: boolean;
640
826
  }): Promise<void>;
641
- reconcileProject(projectId: string, opts?: {
827
+ abstract reconcileProject(projectId: string, opts?: {
642
828
  force?: boolean;
643
829
  }): Promise<void>;
644
- registerImportedFile(args: {
830
+ abstract registerImportedFile(args: {
645
831
  projectId: string;
646
832
  agentId: string;
647
833
  sessionId: string;
@@ -650,32 +836,44 @@ declare const TraceIndex_base: (abstract new () => {
650
836
  sizeBytes: number;
651
837
  records: OmniMessage[];
652
838
  }): void;
653
- removeSession(projectId: string, agentId: string, sessionId: string): void;
654
- removeAgent(projectId: string, agentId: string): void;
655
- removeProject(projectId: string): void;
656
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
657
- /** TraceIndex: the mechanism TraceIndexService implements. */
658
- declare abstract class TraceIndex extends TraceIndex_base {
839
+ abstract removeSession(projectId: string, agentId: string, sessionId: string): void;
840
+ abstract removeAgent(projectId: string, agentId: string): void;
841
+ abstract removeProject(projectId: string): void;
659
842
  }
660
843
 
844
+ interface PlatformCatalogPricing {
845
+ unit: "usd_per_mtok";
846
+ cacheRead: number;
847
+ cacheWrite: number;
848
+ output: number;
849
+ }
850
+ /** A platform model normalized into the fields Penguin persists for a newly discovered row. */
851
+ interface PlatformCatalogModel {
852
+ modelId: string;
853
+ displayName: string;
854
+ contextWindow: number;
855
+ supportsVision: boolean;
856
+ /**
857
+ * List price, which is what a Project stores: the platform's `listPricing` when it runs a
858
+ * promotion on the model, its billed `pricing` otherwise.
859
+ */
860
+ pricing: PlatformCatalogPricing;
861
+ /** Fraction off `pricing` the platform is running (0.5 = half price); absent when none. */
862
+ discount?: number;
863
+ baseUrl: string;
864
+ clientType: "mmsp";
865
+ }
866
+ /** Validated snapshot returned by Penguin Go's client-model catalog. */
867
+ interface PlatformModelCatalog {
868
+ models: PlatformCatalogModel[];
869
+ }
661
870
  /**
662
- * "desktop" and "setup" may set a password without the old one (theirs is random and was never
663
- * shown); desktop-only routes open to "desktop" alone. "token" is the local API token's Bearer
664
- * header — per request, never a stored row. Anything else reads as "password".
871
+ * Catalog merge result. `updated` counts route migrations; existing metadata is preserved.
665
872
  */
666
- type SessionVia = "password" | "desktop" | "setup" | "token";
667
-
668
- interface UserRow {
669
- userId: string;
670
- passwordHash: string;
671
- isAdmin: boolean;
672
- /** Still using the initial password (seeded / set by an admin); cleared to 0 once the user changes it. */
673
- passwordIsInitial: boolean;
674
- /** Nickname shown in place of the id; null = never set. */
675
- displayName: string | null;
676
- /** Avatar as a data URL; null = never set. */
677
- avatar: string | null;
678
- createdAt: string;
873
+ interface PlatformModelApplyResult {
874
+ added: number;
875
+ updated: number;
876
+ applied: number;
679
877
  }
680
878
 
681
879
  type RawTable = Record<string, unknown>;
@@ -695,20 +893,26 @@ type UtilityCompletion = {
695
893
  error: string;
696
894
  };
697
895
 
698
- declare const ProjectConfigStore_base: (abstract new () => {
699
- readRaw(projectId: string): Promise<RawTable>;
700
- loadConfig(projectId: string): Promise<ProjectConfig>;
701
- writeRaw(projectId: string, data: RawTable): Promise<void>;
702
- writeInitialConfig(projectId: string, name: string): Promise<void>;
703
- ensurePresetModels(projectId: string): Promise<void>;
704
- getName(projectId: string): Promise<string | undefined>;
705
- setName(projectId: string, name: string): Promise<void>;
706
- getDefaultModelRef(projectId: string): Promise<ModelRef | undefined>;
707
- setDefaultModelRef(projectId: string, ref: ModelRefDto): Promise<ModelRefDto>;
708
- getChatDefaults(projectId: string): Promise<ChatDefaultsDto>;
709
- setChatDefaults(projectId: string, req: ChatDefaultsDto): Promise<ChatDefaultsDto>;
710
- getCommandPolicy(projectId: string): Promise<CommandPolicyDto>;
711
- setCommandPolicy(projectId: string, req: {
896
+ /** ProjectConfigStore: the mechanism ProjectConfigService implements. */
897
+ declare abstract class ProjectConfigStore {
898
+ abstract readRaw(projectId: string): Promise<RawTable>;
899
+ abstract loadConfig(projectId: string): Promise<ProjectConfig>;
900
+ abstract writeRaw(projectId: string, data: RawTable): Promise<void>;
901
+ abstract writeInitialConfig(projectId: string, name: string): Promise<void>;
902
+ abstract ensurePresetModels(projectId: string): Promise<boolean>;
903
+ abstract seedPresetPromotions(projectId: string): Promise<void>;
904
+ abstract getName(projectId: string): Promise<string | undefined>;
905
+ abstract setName(projectId: string, name: string): Promise<void>;
906
+ abstract getDefaultModelRef(projectId: string): Promise<ModelRef | undefined>;
907
+ abstract setDefaultModelRef(projectId: string, ref: ModelRefDto): Promise<ModelRefDto>;
908
+ abstract getChatDefaults(projectId: string): Promise<ChatDefaultsDto>;
909
+ abstract setChatDefaults(projectId: string, req: ChatDefaultsDto): Promise<ChatDefaultsDto>;
910
+ /** The `[plugins]` key: the shared table and each machine's own, package name → requirement, in the file's order. */
911
+ abstract getPluginTables(projectId: string): Promise<PluginTables>;
912
+ /** Replaces the tables (a declarative PUT); answers what was written. */
913
+ abstract setPluginTables(projectId: string, tables: PluginTables): Promise<PluginTables>;
914
+ abstract getCommandPolicy(projectId: string): Promise<CommandPolicyDto>;
915
+ abstract setCommandPolicy(projectId: string, req: {
712
916
  enabled?: boolean;
713
917
  rules: {
714
918
  name: string;
@@ -717,20 +921,74 @@ declare const ProjectConfigStore_base: (abstract new () => {
717
921
  enabled?: boolean;
718
922
  }[];
719
923
  }): Promise<CommandPolicyDto>;
720
- getPricing(projectId: string, provider: string, modelId: string): Promise<TieredRates | undefined>;
721
- detectVision(projectId: string, req: ModelVisionDetectRequest): Promise<ModelVisionDetectResponse>;
722
- testModel(projectId: string, req: ModelTestRequest): Promise<ModelTestResponse>;
723
- detectProtocol(projectId: string, req: ModelProtocolDetectRequest): Promise<ModelProtocolDetectResponse>;
724
- listEndpointModels(req: EndpointModelListRequest, listImpl?: (options: ListEndpointModelsOptions) => Promise<string[]>, timeoutMs?: number): Promise<EndpointModelListResponse>;
725
- getModels(projectId: string): Promise<ModelsResponse>;
726
- updateModels(projectId: string, req: ModelsUpdateRequest): Promise<ModelsResponse>;
727
- setGroupApiKey(projectId: string, provider: string, apiKey: string): Promise<number>;
728
- completeOnce(projectId: string, prompt: string): Promise<UtilityCompletion>;
729
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
730
- /** ProjectConfigStore: the mechanism ProjectConfigService implements. */
731
- declare abstract class ProjectConfigStore extends ProjectConfigStore_base {
924
+ abstract getPricing(projectId: string, provider: string, modelId: string): Promise<TieredRates | undefined>;
925
+ abstract detectVision(projectId: string, req: ModelVisionDetectRequest): Promise<ModelVisionDetectResponse>;
926
+ abstract testModel(projectId: string, req: ModelTestRequest): Promise<ModelTestResponse>;
927
+ abstract detectProtocol(projectId: string, req: ModelProtocolDetectRequest): Promise<ModelProtocolDetectResponse>;
928
+ abstract listEndpointModels(req: EndpointModelListRequest, listImpl?: (options: ListEndpointModelsOptions) => Promise<string[]>, timeoutMs?: number): Promise<EndpointModelListResponse>;
929
+ abstract getModels(projectId: string): Promise<ModelsResponse>;
930
+ abstract updateModels(projectId: string, req: ModelsUpdateRequest): Promise<ModelsResponse>;
931
+ /** One group's `[providers.<id>]` connection, merged per field; rows are never touched. */
932
+ abstract setProviderConnection(projectId: string, provider: string, patch: ProviderConnectionUpdate): Promise<ModelsResponse>;
933
+ /** "Add new models" (`add`) or "Restore defaults" (`restore`) against the built-in catalog. */
934
+ abstract syncPresets(projectId: string, mode: PresetSyncMode): Promise<PresetSyncResponse>;
935
+ /** Writes the group key; resolves to how many of the group's rows use it (none of their own). */
936
+ abstract setGroupApiKey(projectId: string, provider: string, apiKey: string): Promise<number>;
937
+ abstract setGroupApiKeyWithProviderAuthToken(projectId: string, provider: string, apiKey: string, token: Omit<ModelProviderAuthToken, "provider" | "updatedAt">, options?: {
938
+ expectedRefreshToken?: string;
939
+ }): Promise<number>;
940
+ /** The group key (`[providers.<id>].api_key`), never a row's own. Server-side only. */
941
+ abstract getGroupApiKey(projectId: string, provider: string): Promise<string | undefined>;
942
+ /** One row's own key (its override of the group's), or undefined. Server-side only. */
943
+ abstract getModelApiKey(projectId: string, provider: string, modelId: string): Promise<string | undefined>;
944
+ /**
945
+ * The key a group's account balance is read with: the group key (never a row's own), or
946
+ * else the environment key a Session on one of the group's rows would use on its effective
947
+ * endpoint, when that variable's official endpoint is the balance endpoint's own host.
948
+ * Server-side only.
949
+ */
950
+ abstract getGroupBalanceKey(projectId: string, provider: string): Promise<string | undefined>;
951
+ abstract setModelApiKeyResolver(resolver: (context: ModelRequestContext) => Promise<string | undefined>): void;
952
+ abstract mergePlatformModels(projectId: string, provider: string, catalog: PlatformModelCatalog, apiKey: string, applyKeyToExisting: boolean): Promise<PlatformModelApplyResult>;
953
+ abstract completeOnce(projectId: string, prompt: string): Promise<UtilityCompletion>;
954
+ }
955
+ interface ModelProviderAuthToken {
956
+ provider: string;
957
+ refreshToken: string;
958
+ accessTokenExpiresAt?: string;
959
+ updatedAt: string;
732
960
  }
733
961
 
962
+ /**
963
+ * The order a Session list is served in: `created` (the default) is newest creation first;
964
+ * `activity` is most recent `lastActiveAt` first — the order the sidebar displays, and the only
965
+ * one an {@link ActivityCursor} pages.
966
+ */
967
+ type SessionListOrder = "created" | "activity";
968
+ /** A row's place in the activity order; as a `before` cursor, the last row the client holds. */
969
+ interface ActivityCursor {
970
+ lastActiveAt: string;
971
+ sessionId: string;
972
+ }
973
+ /** One row the listing rule files as archived (SessionService.archivedRows). */
974
+ interface ArchivedRow {
975
+ row: SessionRow;
976
+ /** The source the row was classified by; absent while its Trace head is unread. */
977
+ source?: SessionSource;
978
+ /** Whether a Trace record exists: what toInfo takes for this row. */
979
+ hasTrace: boolean;
980
+ }
981
+ /**
982
+ * A slice of a Session list: `offset` rows skipped (the `created` contract, also accepted under
983
+ * `activity`), or every row strictly below `before` in the activity order.
984
+ */
985
+ type SessionListPaging = {
986
+ offset: number;
987
+ limit: number;
988
+ } | {
989
+ before: ActivityCursor;
990
+ limit: number;
991
+ };
734
992
  interface SessionServiceDeps {
735
993
  root: string;
736
994
  sessions: SessionIndex;
@@ -759,6 +1017,14 @@ interface SessionServiceDeps {
759
1017
  * this harness's own `penguin`, put in front of every command an Agent runs.
760
1018
  */
761
1019
  pathPrepend?: () => string[];
1020
+ /**
1021
+ * Tells every user of a Project that a Session now exists. The list only ever learns
1022
+ * about rows it did not create itself from this: a Session started by the CLI, by
1023
+ * another tab, by a schedule, or by an agent spawning a child sat invisible until the
1024
+ * next full reload without it. Optional so the service keeps unit-testing without a
1025
+ * channel registry; absent means nobody is told.
1026
+ */
1027
+ notifyProjectUsers?: (projectId: string, event: ServerEvent) => void;
762
1028
  /**
763
1029
  * The channel of the Session's ENABLED messaging binding, or null when none is enabled
764
1030
  * (SessionInfo.messagingChannel, the sidebar row's per-channel indicator — saved-but-
@@ -766,7 +1032,7 @@ interface SessionServiceDeps {
766
1032
  * service stays decoupled from the bindings table; absent (older assemblies/tests)
767
1033
  * means the field is never set.
768
1034
  */
769
- messagingChannel?: (sessionId: string) => MessagingChannel$1 | null;
1035
+ messagingChannel?: (sessionId: string) => MessagingChannel | null;
770
1036
  /**
771
1037
  * Company mode: the organization owning a Session (a desk session, or a session
772
1038
  * contributing to a ticket), for `SessionInfo.orgId` — development mode's list hides
@@ -779,44 +1045,99 @@ interface SessionServiceDeps {
779
1045
  orgIdOfSession?: (sessionId: string) => string | undefined;
780
1046
  orgIdsOfProject?: (projectId: string) => ReadonlyMap<string, string>;
781
1047
  /** Spawn-confinement getter (the sandbox module's), forwarded into core beside proxyEnv. */
782
- confineSpawn?: () => SpawnConfiner | null;
1048
+ confineSpawn?: (ctx: ControlEnvContext) => SpawnConfiner | null;
1049
+ /** The server's Sandbox settings: what a new Session's policy is snapshotted from. */
1050
+ sandboxDefaults?: () => SandboxSettings;
1051
+ /** Host-owned model-request hooks, forwarded to core for every Session LLM. */
1052
+ assembly?: AgentAssembly;
1053
+ /** The dimensions the mounted sandbox backends implement between them (none when absent). */
1054
+ sandboxDimensions?: () => readonly SandboxDimension[];
1055
+ /** The enabled sandbox backends that failed to load or failed their check, with why. */
1056
+ sandboxUnavailable?: () => readonly UnavailableSandboxBackend[];
1057
+ /** The names of the sandbox backends in use, in routing preference (none when absent). */
1058
+ sandboxBackends?: () => readonly string[];
1059
+ /** The Sandbox card's presets table, in table order (absent: the view carries none). */
1060
+ sandboxPresets?: () => readonly SessionSandboxPreset[];
1061
+ /** The Sandbox card's switch: whether new Sessions start confined (absent: not reported). */
1062
+ sandboxSwitchOn?: () => boolean;
1063
+ /** The approval mode a new Session starts with when its request names none (the default preset's). */
1064
+ sandboxDefaultApproval?: () => ApprovalMode | undefined;
783
1065
  }
784
1066
  declare class SessionService {
785
1067
  private readonly deps;
786
1068
  constructor(deps: SessionServiceDeps);
1069
+ /** The policy a new Session starts with: the server's Sandbox settings. */
1070
+ defaultSandbox(): SandboxSettings;
1071
+ /** Whether this server can enforce the `local` network level right now. */
1072
+ localNetworkSupported(): boolean;
1073
+ private sandboxDimensions;
1074
+ /** A policy as the composer sees it, with which of its levels this server can enforce. */
1075
+ sandboxView(policy: SandboxSettings): SessionSandbox;
1076
+ /**
1077
+ * What a new Session starts from, as the composer's draft reads it (the chat defaults): the
1078
+ * settings' policy, plus the approval mode the default preset gives a request naming none.
1079
+ */
1080
+ defaultsView(): SessionSandbox;
1081
+ /**
1082
+ * The approval mode a Session created without one starts with. An organization's Session
1083
+ * keeps `allow-all`: its runtime names the mode it wants, and nobody is there to answer an
1084
+ * ask a preset might bring. The other unattended creators — a scheduled run, a workbench —
1085
+ * pass `allow-all` themselves (scheduler.ts, workbenches/service.ts).
1086
+ */
1087
+ private startApproval;
1088
+ /** A Session's policy: its snapshot, or — for a row from before snapshots — the settings. */
1089
+ sandboxOf(row: SessionRow): SandboxSettings;
1090
+ /**
1091
+ * The policy a pick would give one Session, checked — the non-admin ceiling and what this
1092
+ * server can enforce — and not written: a PATCH checks every field before it writes any.
1093
+ */
1094
+ pickSandbox(row: SessionRow, pick: Partial<SessionSandbox>, isAdmin: boolean): SandboxSettings;
1095
+ /** Stores one Session's policy (`pickSandbox`'s): its next command runs under it. */
1096
+ updateSandbox(sessionId: string, policy: SandboxSettings): void;
787
1097
  /**
788
1098
  * DB row -> SessionInfo (run status and pending approval count come from session-manager).
789
1099
  * Async because `source` is derived from session_meta: a registry miss (Session predating
790
1100
  * this process) falls back to reading the Trace head once (see sourceOf). `traces` is the
791
1101
  * list flow's one-walk discovery result; without it a miss locates the shard itself.
1102
+ * `archived` is the listing rule's answer (listCategory), not the bare mark: a background
1103
+ * Session the user never kept reads archived, and a subagent with a stray mark does not.
792
1104
  *
793
1105
  * `orgIds` is the list flow's one-query organization map (see listSessions); without it the
794
1106
  * organization is a point lookup, which is what the single-Session paths want.
795
1107
  */
796
1108
  toInfo(row: SessionRow, hasTrace: boolean, orgIds?: ReadonlyMap<string, string>): Promise<SessionInfo>;
797
1109
  /**
798
- * A Session's origin, with session_meta as the single source of truth: the in-process
799
- * registry answers first (populated at creation / subagent registration / adoption /
1110
+ * A Session's source, with session_meta as the single source of truth: the in-process
1111
+ * registry answers first (populated at creation / subagent registration / forks / adoption /
800
1112
  * index registration); on a miss (a Session created before this process started) the
801
1113
  * trace index's registration-time facts answer — the reconciler head-read the earliest
802
- * shard once when the file first appeared, so no file is touched here. A Session with
803
- * no Trace yet stays unknown and is NOT cached negatively — its meta may appear with
804
- * the first run.
1114
+ * shard once when the file first appeared, so no file is touched here. A head that records
1115
+ * no source is read with the row's client (readRecordedSource). A Session with no Trace yet
1116
+ * is NOT cached — its meta appears with the first run — and reads as its row says
1117
+ * (unrunSource): the organization runtime's as `company`, the Agent API's as `api`, any other
1118
+ * as unknown.
805
1119
  */
806
1120
  private sourceOf;
807
1121
  /** Whether this Session already has a Trace record (a Task has been run): answered by the index (reconciled first). */
808
1122
  hasTrace(row: SessionRow): Promise<boolean>;
809
1123
  /**
810
- * The list category of a row: archived wins (an explicit user action), then the
811
- * origin's bucket, and no/unknown source is `active` — the same precedence the
812
- * sidebar's partition applies to loaded rows, so server filtering and client
813
- * rendering can never disagree.
1124
+ * The list category of a row (see listCategory): none for a `subagent` or `company` Session,
1125
+ * whatever its marks, which the list never serves; otherwise archived when the user archived
1126
+ * it or when a program opened it and the user has not kept it, else active.
814
1127
  */
815
1128
  private categoryOf;
816
1129
  /**
817
- * List, sorted by createdAt descending. Every row is served **straight from the DB**,
818
- * whichever client created it, with no Trace directory scanning — the answer to
819
- * many-session sidebar reloads re-walking the filesystem on every request (#139).
1130
+ * Where one row is listed, for a caller holding just the row (a PATCH, the archive page's
1131
+ * batches). A row the registry already classified costs nothing; any other is looked up
1132
+ * through the trace index once (hasTrace reconciles the Agent), so a subagent Session is
1133
+ * known for one even before any list has walked it.
1134
+ */
1135
+ listingOf(row: SessionRow): Promise<SessionCategory | null>;
1136
+ /**
1137
+ * List, sorted by createdAt descending — or, with `order: "activity"`, by lastActiveAt
1138
+ * descending (ties by sessionId, see {@link compareActivityDesc}). Every row is served
1139
+ * **straight from the DB**, whichever client created it, with no Trace directory scanning —
1140
+ * the answer to many-session sidebar reloads re-walking the filesystem on every request (#139).
820
1141
  * Sessions living only in the Trace directory were adopted into the index by the
821
1142
  * boot-time sweep (`adoptUnmanagedTraceSessions`), so listing never discovers. One
822
1143
  * lazy discovery walk still runs for a list call that contains rows this process has
@@ -828,36 +1149,78 @@ declare class SessionService {
828
1149
  * "has more"); slicing happens before toInfo, so per-request source derivation (lazy
829
1150
  * Trace-head reads) stays bounded by the page size.
830
1151
  *
831
- * `category` filters to one sidebar bucket **before** paging, so offset/limit page
832
- * within the category. Filtering needs each walked row's category (a possible
833
- * Trace-head read per row, cached in the sources registry); without `withCounts`
834
- * the walk stops as soon as the requested page is complete. `withCounts` classifies
835
- * every row and returns per-category totals over the whole list — plus the same
836
- * totals broken down by Workspace path, and each path's newest Session's `createdAt` —
837
- * so the sidebar can label the collapsed folders, list every Workspace that holds
838
- * Sessions (not only the ones its loaded pages happen to touch) and place the groups
839
- * by recency, all without loading them.
1152
+ * The `before` form pages the activity order by cursor: only rows strictly below it are
1153
+ * walked and served. An offset cannot page that order, because a row below the offset that
1154
+ * becomes active moves above it and the next page would skip a row; a cursor stays put — the
1155
+ * moved row is simply not served again (the client learns of it from `session_state`). The
1156
+ * offset form keeps serving the `created` order exactly as before.
1157
+ *
1158
+ * `category` filters to one list (the sidebar's `active`, the archive page's `archived`)
1159
+ * **before** paging, so offset/limit page within the category. Filtering needs each walked
1160
+ * row's category (a possible Trace-head read per row, cached in the sources registry);
1161
+ * without `withCounts` the walk stops as soon as the requested page is complete.
1162
+ * `withCounts` classifies every row, a cursor or not, and returns per-category totals over
1163
+ * the whole list — plus the same totals broken down by Workspace path, and each path's
1164
+ * newest Session's `createdAt`, once over every category (`workspaceLatest`, the Workspace
1165
+ * picker's "recently used") and once over the active ones (`workspaceLatestActive`) — so the
1166
+ * sidebar can list every Workspace that holds active Sessions (not only the ones its loaded
1167
+ * pages happen to touch) and place the groups by the recency of what they show, all without
1168
+ * loading them.
840
1169
  *
841
1170
  * `workspaceGroup` filters the same way, to one Workspace group (see workspace-group.ts),
842
1171
  * so a sidebar grouped by Workspace pages each group down its OWN stream instead of
843
1172
  * sharing one per-Agent cursor — without it, one group's "load more" consumes the page
844
1173
  * its siblings were about to read, and their rows move on screen untouched. The two
845
1174
  * filters compose; the returned counts stay whole-Agent either way.
1175
+ *
1176
+ * A `subagent` or `company` Session — a child its parent's Agent panel opens, company mode's
1177
+ * desk and ticket Sessions — belongs to no category (listCategory), whatever its marks: every
1178
+ * classified form of the list (`category`, `workspaceGroup` or `withCounts`) leaves it out of
1179
+ * the page, the totals, the Workspace breakdown and its stamps alike. The plain form, which
1180
+ * classifies nothing, still serves every row.
1181
+ *
1182
+ * `excludeOrg` drops the rows an organization owns (its desk and ticket sessions, and the
1183
+ * sub-sessions they spawned) from the stream BEFORE anything else looks at it — the page,
1184
+ * `counts`, `workspaceCounts`, both Workspace stamps and the limit+1 "has more" all describe the
1185
+ * same own-rows stream. It is what development mode's list asks for: that list draws the
1186
+ * user's own conversations, and a total or a stamp that still counted a desk or a ticket
1187
+ * session would make its Workspace appear as a group the list can never fill. The rule above
1188
+ * does not replace it: a person's conversation attached to a ticket is stamped `org` but
1189
+ * stays `user`, and a row the organization caches name before the reconcile pass stamps it
1190
+ * reads as `user`. Without the flag every row is served, whichever client created it, but
1191
+ * for that rule.
846
1192
  */
847
1193
  listSessions(projectId: string, agentId: string, opts?: {
848
- paging?: {
849
- offset: number;
850
- limit: number;
851
- };
1194
+ paging?: SessionListPaging;
1195
+ order?: SessionListOrder;
852
1196
  category?: SessionCategory;
853
1197
  workspaceGroup?: string;
854
1198
  withCounts?: boolean;
1199
+ excludeOrg?: boolean;
855
1200
  }): Promise<{
856
1201
  sessions: SessionInfo[];
857
1202
  counts?: SessionCategoryCounts;
858
1203
  workspaceCounts?: Record<string, SessionCategoryCounts>;
859
1204
  workspaceLatest?: Record<string, string>;
1205
+ workspaceLatestActive?: Record<string, string>;
860
1206
  }>;
1207
+ /**
1208
+ * The archive page's walk (services/archived-sessions-service.ts): every row of the Agent the
1209
+ * listing rule files as archived — the rows `listSessions` serves with `category: "archived"`
1210
+ * and `excludeOrg`, classified the same way — with the source each was classified by and
1211
+ * whether it has a Trace. It builds no SessionInfo: the page makes one (toInfo) only for the
1212
+ * rows a response carries, so a request costs a classification per archived row, not a full
1213
+ * row view per archived row.
1214
+ */
1215
+ archivedRows(projectId: string, agentId: string): Promise<ArchivedRow[]>;
1216
+ /**
1217
+ * The rows a list walk starts from: the Agent's index rows, an organization's left out when
1218
+ * `excludeOrg` asks (see listSessions), with the Project's organization map; and when any row
1219
+ * predates this process, the one reconciled Trace discovery (`traces`) that lets every row be
1220
+ * classified without per-row work and backfills the `has_trace` cache. Steady state —
1221
+ * every row classified — skips the discovery and leaves `traces` absent.
1222
+ */
1223
+ private walkRows;
861
1224
  /**
862
1225
  * Session stats (Agents list card): total count = size of the union of DB index
863
1226
  * ∪ Trace directory discovery; activity = number of active Sessions per day over
@@ -886,21 +1249,30 @@ declare class SessionService {
886
1249
  /** The provider group for `modelId`; always paired with modelId, never inferred. */
887
1250
  provider?: string;
888
1251
  workspace?: string;
1252
+ thinkingLevel?: ThinkingLevelName;
889
1253
  approvalMode?: ApprovalMode;
1254
+ /** The composer's sandbox picks; omitted halves take the server's settings. */
1255
+ sandbox?: Partial<SessionSandbox>;
1256
+ /** Whether the creator is an administrator (may loosen past the settings). Default false. */
1257
+ isAdmin?: boolean;
890
1258
  /**
891
- * Session source marker: `schedule` when triggered by a scheduled task, `benchmark` when
892
- * created by a Benchmark evaluation or optimization (the only value a client may send);
893
- * defaults to user-created.
1259
+ * What kind of conversation this is, recorded in the Session's session_meta: `schedule`
1260
+ * from the scheduler, `company` from the organization runtime, `cli` from `penguin run`
1261
+ * (the only source a request may name), `api` from an Agent API run; absent means `user`,
1262
+ * a person's conversation.
894
1263
  */
895
- source?: "schedule" | "benchmark";
1264
+ source?: SessionSource;
896
1265
  /**
897
1266
  * Creating-client hint stored on the index row (`POST .../sessions` body `client`):
898
- * "cli" from the CLI, defaulting to "web". "org" is not accepted over HTTP — the
899
- * organization runtime calls this method directly and is the only caller that passes
900
- * it, so no request can claim an organization's provenance for itself.
1267
+ * "cli" from the CLI, defaulting to "web". "org" and "api" are not accepted over HTTP —
1268
+ * the organization runtime and the Agent API's run handler call this method directly and
1269
+ * are the only callers that pass them, so no request can claim either provenance for
1270
+ * itself ("api" is what lets the Agent API continue a Session).
901
1271
  */
902
- client?: "web" | "cli" | "org";
1272
+ client?: "web" | "cli" | "org" | "api" | "workbench";
903
1273
  }): Promise<SessionInfo>;
1274
+ /** A new Session's snapshot: the settings, with the creator's picks (checked) on top. */
1275
+ private snapshotSandbox;
904
1276
  /**
905
1277
  * Absolute path of a Session's **latest** Trace file (the current context shard);
906
1278
  * undefined when no Trace exists. Costs a directory walk, so only the single-session
@@ -936,16 +1308,118 @@ declare class SessionService {
936
1308
  private adoptTraceSession;
937
1309
  }
938
1310
 
1311
+ type ModelScopeAuthStatusResult = ModelScopeAuthFlowStatusResponse & {
1312
+ changed?: true;
1313
+ };
1314
+ interface ModelScopeCredential {
1315
+ accessToken: string;
1316
+ refreshToken?: string;
1317
+ expiresAt?: string;
1318
+ }
1319
+ interface ModelScopeRefreshResult {
1320
+ changed: boolean;
1321
+ }
1322
+ interface ModelScopeAuthDeps {
1323
+ /** Base URL of the bridge, without a trailing slash (config.modelscopeBridgeUrl). */
1324
+ bridgeUrl: string;
1325
+ /**
1326
+ * Writes the delivered credential as the group key; resolves to how many of the group's rows
1327
+ * use the access token (0 also when a refresh lost to a newer authorization and wrote nothing).
1328
+ */
1329
+ applyCredential: (projectId: string, credential: ModelScopeCredential, options?: {
1330
+ expectedRefreshToken?: string;
1331
+ }) => Promise<number>;
1332
+ /** Reads the server-side refresh metadata for the group. */
1333
+ readStoredCredential?: (projectId: string) => ModelProviderAuthToken | undefined;
1334
+ fetchImpl?: typeof fetch;
1335
+ now?: () => number;
1336
+ }
1337
+ declare class ModelScopeAuthService {
1338
+ private readonly deps;
1339
+ private readonly flows;
1340
+ private readonly refreshes;
1341
+ private readonly refreshFailures;
1342
+ private readonly fetchImpl;
1343
+ private readonly now;
1344
+ constructor(deps: ModelScopeAuthDeps);
1345
+ private url;
1346
+ private isPending;
1347
+ private request;
1348
+ ensureFresh(input: {
1349
+ projectId: string;
1350
+ provider: string;
1351
+ }): Promise<ModelScopeRefreshResult>;
1352
+ start(input: {
1353
+ projectId: string;
1354
+ userId: string;
1355
+ }): Promise<{
1356
+ flowId: string;
1357
+ authorizeUrl: string;
1358
+ expiresAt: string;
1359
+ }>;
1360
+ status(input: {
1361
+ flowId: string;
1362
+ projectId: string;
1363
+ userId: string;
1364
+ }): Promise<ModelScopeAuthStatusResult>;
1365
+ retryApply(input: {
1366
+ flowId: string;
1367
+ projectId: string;
1368
+ userId: string;
1369
+ }): Promise<ModelScopeAuthStatusResult>;
1370
+ cancel(input: {
1371
+ flowId: string;
1372
+ projectId: string;
1373
+ userId: string;
1374
+ }): void;
1375
+ private refreshState;
1376
+ private refreshNow;
1377
+ private reauthorizationRequired;
1378
+ private pollBridge;
1379
+ private apply;
1380
+ private publicFlow;
1381
+ private requireFlow;
1382
+ private sweep;
1383
+ private evictOldest;
1384
+ }
1385
+ declare const ModelScopeAuth_base: (abstract new () => Pick<ModelScopeAuthService, "status" | "start" | "retryApply" | "cancel" | "ensureFresh">) & (<C>(target: C, context: ClassDecoratorContext) => C);
1386
+ /** The per-App ModelScope key authorization capability consumed by Project model routes. */
1387
+ declare abstract class ModelScopeAuth extends ModelScopeAuth_base {
1388
+ }
1389
+
939
1390
  /** Minimal interface for a runtime Session (satisfied by core Session; tests may inject a fake implementation). */
1391
+ /** What one run is started with: the approval callback and the interrupt signal. */
1392
+ type RunOpts = {
1393
+ approve: ApproveFn;
1394
+ signal: AbortSignal;
1395
+ };
940
1396
  interface RuntimeSession {
941
1397
  readonly sessionId: string;
942
- run(newMessages: OmniMessage[], opts: {
943
- approve: ApproveFn;
944
- signal: AbortSignal;
945
- }): AsyncGenerator<OmniMessage>;
1398
+ run(newMessages: OmniMessage[], opts: RunOpts): AsyncGenerator<OmniMessage>;
946
1399
  compact(opts: {
947
1400
  signal: AbortSignal;
948
1401
  }): AsyncGenerator<OmniMessage>;
1402
+ /**
1403
+ * The running context's model (core `Session.provider` / `Session.modelId`, getters over
1404
+ * the context's session_meta): the row's pair at creation, moved by each in-session switch
1405
+ * — and, after a restart, whatever the Trace resumed on. Optional: test fakes may omit them,
1406
+ * in which case the entry keeps the row's pair.
1407
+ */
1408
+ readonly provider?: string;
1409
+ readonly modelId?: string;
1410
+ /**
1411
+ * Switches the Session's model in place (core `Session.switchModel`): closes the running
1412
+ * context on the model it ran on, then opens the next one on the target. A completed switch
1413
+ * streams the new context's main-session `session_meta` last, and the getters above answer
1414
+ * the target by then; a refusal is thrown before the first event; a Session that never ran
1415
+ * switches without streaming anything. Optional: test fakes may omit it, reading as
1416
+ * "switching is not available".
1417
+ */
1418
+ switchModel?(opts: {
1419
+ provider: string;
1420
+ modelId: string;
1421
+ signal: AbortSignal;
1422
+ }): AsyncGenerator<OmniMessage, StopReason>;
949
1423
  /**
950
1424
  * The Session's thinking level (core `Session.thinkingLevel`, plain state): soft-limited —
951
1425
  * an assignment applies from the Session's very next LLM request. Optional: test fakes may
@@ -965,6 +1439,13 @@ interface RuntimeSession {
965
1439
  unsteer?(input: OmniMessage[]): boolean;
966
1440
  /** Skips the in-progress reconnect backoff, firing the next retry immediately (core `Session.skipReconnectWait`); false when no wait is in progress. */
967
1441
  skipReconnectWait(): boolean;
1442
+ /**
1443
+ * Whether a run with empty input would send anything (core `Session.hasPendingInput`): the
1444
+ * input of a turn that failed or was stopped, held as carry-over — in process, or rebuilt
1445
+ * from the Trace after a restart — or a compaction summary waiting for the next run.
1446
+ * Optional: test fakes may omit it, reading as nothing held.
1447
+ */
1448
+ hasPendingInput?(): boolean;
968
1449
  /**
969
1450
  * Runs the named package's `user_prompt` hook (core `Session.runUserPromptHook` — hooks
970
1451
  * run in core and nowhere else); null = the package is not installed or names no such
@@ -1041,6 +1522,8 @@ interface UsageRecorderLike {
1041
1522
  record(ctx: UsageContext, msg: OmniMessage): Promise<void>;
1042
1523
  }
1043
1524
  interface SessionManagerDeps {
1525
+ /** The in-memory state this manager works on (see agent-state.ts). Optional: a manager built without one — unit tests — gets an empty state of its own. */
1526
+ state?: AgentState;
1044
1527
  sessions: SessionIndex;
1045
1528
  channels: ChannelHub;
1046
1529
  loader: SessionLoader;
@@ -1051,6 +1534,8 @@ interface SessionManagerDeps {
1051
1534
  titles?: TitleNotifier;
1052
1535
  /** Error persistence (optional: without it, only logs — same as before this was wired up). */
1053
1536
  errors?: ErrorSink;
1537
+ /** Provider auth refresh (optional for tests that do not exercise external model credentials). */
1538
+ modelScopeAuth?: ModelScopeAuth;
1054
1539
  log?: (line: string) => void;
1055
1540
  /** Goal run-state persistence (optional like `titles`: without it, goals run but leave no restorable record). */
1056
1541
  /**
@@ -1087,9 +1572,120 @@ interface RecallStore {
1087
1572
  images: string[];
1088
1573
  files: RecallableFile[];
1089
1574
  }
1575
+ /** One queued follow-up task (`queueIfBusy`): the task input, held until the session is idle. */
1576
+ interface QueuedFollowUp {
1577
+ /** Recall handle (PendingFollowUpInfo.id), assigned at queue time. */
1578
+ id: string;
1579
+ input: OmniMessage[];
1580
+ /**
1581
+ * Original content for recall, and the content `task_state` shows on the queued line.
1582
+ * Always present: a caller that knows more than the input carries (the HTTP route, which
1583
+ * also knows where the file attachments landed on disk) supplies it, and every other
1584
+ * queueing path gets it derived from the input — what can be recalled must not depend on
1585
+ * which door the message came through.
1586
+ */
1587
+ recall: RecallStore;
1588
+ }
1589
+ /** One undelivered steering entry: the display info broadcast on task_state, plus what a recall needs — the exact input list core queued (its unsteer handle) and the original content. */
1590
+ interface PendingSteeringEntry {
1591
+ info: PendingSteeringInfo;
1592
+ input: OmniMessage[];
1593
+ recall: RecallStore;
1594
+ }
1595
+ /** Active-table entry: a loaded runtime Session plus its running state. */
1596
+ interface RuntimeEntry {
1597
+ sessionId: string;
1598
+ projectId: string;
1599
+ agentId: string;
1600
+ /**
1601
+ * The Session's current model (provider group paired with modelId): what the next drive
1602
+ * attributes usage to. The row's pair, kept in step with the runtime's own answer (see
1603
+ * `syncEntryModel`) — at load, and whenever a model switch moves it.
1604
+ */
1605
+ provider: string;
1606
+ modelId: string;
1607
+ session: RuntimeSession;
1608
+ status: SessionStatus;
1609
+ approvals: Approvals;
1610
+ abort: AbortController | null;
1611
+ /** The in-flight drive Promise (awaited during graceful shutdown). */
1612
+ running: Promise<void> | null;
1613
+ /**
1614
+ * Agent config generation this runtime was built under (see
1615
+ * invalidateAgentRuntimes): once it falls behind the Agent's current generation,
1616
+ * the entry is discarded on its next idle access and re-resumed via the loader.
1617
+ */
1618
+ generation: number;
1619
+ /**
1620
+ * Queued follow-up tasks (`queueIfBusy`): task inputs accepted while a Task/compaction was
1621
+ * in progress, auto-started one at a time (in order) once the session returns to idle —
1622
+ * ordinary tasks with normal semantics, unlike the mid-run steering queue.
1623
+ * Deliberately NOT discarded on abort: they are future tasks the user explicitly queued.
1624
+ */
1625
+ followUps: QueuedFollowUp[];
1626
+ /**
1627
+ * The running Task's input messages, held from launch until the run ends. The engine
1628
+ * writes these exact envelopes to the Trace only after the session bootstrap (MCP
1629
+ * connect + discovery on the first run), so a history read during that window would
1630
+ * miss the user's own message — and the draft flow subscribes to the stream only after
1631
+ * the input publish, so the live channel cannot backfill it either. GET /messages
1632
+ * appends the ones the Trace has not caught up to yet (exact-envelope dedup).
1633
+ */
1634
+ pendingInputs: OmniMessage[];
1635
+ /**
1636
+ * The running Task's streamed bootstrap records (mcp_connect begin/end,
1637
+ * tool_list_ready), held until the run ends. Their Trace writes are deferred by the
1638
+ * engine until after the input lands (turn attribution), and the draft flow subscribes
1639
+ * only after they were published — so a history rebuild during the MCP connect would
1640
+ * otherwise show nothing at all (a silent blank while a slow server times out).
1641
+ * GET /messages appends whichever of them the Trace has not caught up to.
1642
+ */
1643
+ pendingBootstrap: OmniMessage[];
1644
+ /**
1645
+ * Steering messages queued on core but not yet delivered to the model — a display mirror
1646
+ * of core's steering queue (same FIFO order), so the composer's "steering queued" hint and
1647
+ * its content survive reloads: pushed on `steer`, shifted as each `[user_steering]`
1648
+ * message appears on the drive stream, dropped wholesale when the run exits (core
1649
+ * discards undelivered steering on abort/completion). The `info` halves are broadcast on
1650
+ * every `task_state`; the rest is the recall handle (see recallSteering).
1651
+ */
1652
+ pendingSteering: PendingSteeringEntry[];
1653
+ /**
1654
+ * Steering the run ended without ever delivering — handed back to the user instead of
1655
+ * discarded (#287 follow-up). An interrupt mid-tool-call is the ordinary way to get here:
1656
+ * core drops its queue when the run exits, so the message would otherwise vanish with the
1657
+ * text the user had already typed.
1658
+ *
1659
+ * Entries move here from `pendingSteering` when the run exits and stay recallable by the
1660
+ * same handle, so the composer pulls them back into its draft (auto-recall on observing
1661
+ * them, a reload included). They are deliberately NOT cleared when the next run starts:
1662
+ * the message was never delivered whatever happens later, and dropping it there would
1663
+ * reintroduce exactly the loss this list exists to prevent. Only a recall — or the entry's
1664
+ * idle eviction — removes one.
1665
+ */
1666
+ returnedSteering: PendingSteeringEntry[];
1667
+ /** Timestamp of last activity (refreshed on load / status flip / drive completion / background-task change), used for idle-eviction checks. */
1668
+ lastActivityMs: number;
1669
+ /**
1670
+ * Background-task counts as last read from the runtime — at entry creation, then on every
1671
+ * background-state ping. The baseline `session_background` is published against: a ping
1672
+ * that leaves the counts where they were (a foreground-window child settling, an exited
1673
+ * process being removed from the list) publishes nothing.
1674
+ */
1675
+ backgroundTasks: SessionBackgroundTasks;
1676
+ }
1090
1677
  declare class SessionManager {
1091
1678
  private readonly deps;
1679
+ private readonly state;
1092
1680
  private readonly entries;
1681
+ /**
1682
+ * Each registered subagent Session's ROOT Session: a subagent runs under its root's
1683
+ * sandbox policy, as it runs under its root's approval mode, so a change the person makes
1684
+ * in the composer reaches the children already running.
1685
+ */
1686
+ private readonly childRoots;
1687
+ /** The Session whose policy governs `sessionId`: its root for a subagent, else itself. */
1688
+ rootSessionOf(sessionId: string): string;
1093
1689
  /** Per-Session mutex (serializes get-or-load and status flips); auto-cleaned once the chain drains. */
1094
1690
  private readonly locks;
1095
1691
  private readonly log;
@@ -1099,6 +1695,8 @@ declare class SessionManager {
1099
1695
  private readonly deletingAgents;
1100
1696
  /** Sessions currently being deleted (guards against the entry/Trace file being rebuilt and reviving it inside the deletion race window). */
1101
1697
  private readonly deletingSessions;
1698
+ /** Sessions whose runtime entry idle eviction leaves alone (see keepLoaded). */
1699
+ private readonly keptLoaded;
1102
1700
  /** Per-Agent config generation (key = agentKey), bumped by invalidateAgentRuntimes when a Project's credentials change. */
1103
1701
  private readonly agentGenerations;
1104
1702
  /** Open streaming fragments of running sessions (fed by drive, served to GET /messages; see live-tail.ts). */
@@ -1193,6 +1791,14 @@ declare class SessionManager {
1193
1791
  * The entry-lifetime approval callback: the registry, the per-decision approval-mode
1194
1792
  * re-read, and the SSE escalation are all entry-scoped, so one instance serves every run
1195
1793
  * of the entry and the children's session-lifetime fallback sink alike.
1794
+ *
1795
+ * This is also where the one cross-cutting approval override lives — wrapping the callback
1796
+ * handed to `Session.run` rather than teaching the engine anything: an organization's
1797
+ * sessions run with nobody watching, so a call their approval mode would hand to a person
1798
+ * is denied here instead of waiting for an answer that is never coming (see `unattended`
1799
+ * in approvals.ts). The marker is the row's durable `client = "org"` stamp — desk, ticket
1800
+ * and the sub-sessions that inherit it — read per decision, so a row the reconcile pass
1801
+ * stamps later is covered from its next decision on.
1196
1802
  */
1197
1803
  private entryApprove;
1198
1804
  /** Publishes one live background-subagent message and records its usage (fire-and-forget; the child's own Trace is the durable record). */
@@ -1256,6 +1862,21 @@ declare class SessionManager {
1256
1862
  sessionId: string;
1257
1863
  queued: boolean;
1258
1864
  }>;
1865
+ /**
1866
+ * Retry a failed or stopped run (`POST /retry`): a Task started with EMPTY input, so core
1867
+ * sends the input of the turn that did not finish — held as carry-over since the run ended
1868
+ * `fatal`, gave up reconnecting or was stopped by the user, with whatever the model had
1869
+ * already written — and nothing else. No user message is published or recorded, and with no
1870
+ * Prompt there is no user_prompt consult. Gated like startTask (busy is the same 409) but
1871
+ * never queued, and refused with 409 `nothing_to_retry` when nothing is held — the user's
1872
+ * next message already resent that input, or no run was cut short — since the run would end
1873
+ * at once and the client would watch a Task start and stop with nothing sent. Not a person's
1874
+ * input for company mode's hop accounting: the turn it resends was counted, or not, when
1875
+ * it was first sent.
1876
+ */
1877
+ retryTask(sessionId: string): Promise<{
1878
+ sessionId: string;
1879
+ }>;
1259
1880
  /**
1260
1881
  * Start a goal run: like startTask, but the goal plugin composes the input and its stop
1261
1882
  * hook drives every later round inside this one `session.run` call — so the Session
@@ -1281,7 +1902,9 @@ declare class SessionManager {
1281
1902
  * Taps a goal run's stream for `drive`: round boundaries become goal_round events —
1282
1903
  * round 1 from the seeded input (core never yields a run's initial input, so it is
1283
1904
  * counted here, where startGoal published it), later rounds from the stop-hook continues
1284
- * core yields — and the goal hook's `stop` event becomes the
1905
+ * core yields, each announced by the hook's `continue` event; a harness-stamped input
1906
+ * nothing announced is another package's user_prompt context riding behind the
1907
+ * objective, not a round — and the goal hook's `stop` event becomes the
1285
1908
  * goal_finished server event. `used` is what the hook last recorded in its event's
1286
1909
  * `output` — the same number its budget check used — so the UI never shows a different
1287
1910
  * figure. A stream that ends without the hook's terminal event (a cut-off run, an
@@ -1290,7 +1913,12 @@ declare class SessionManager {
1290
1913
  private goalStream;
1291
1914
  /** Closes a goal as aborted for the UI (stream cut off / infrastructure failure). */
1292
1915
  private finishAborted;
1293
- /** Shared task launch (fresh tasks and auto-started follow-ups): flips to running, publishes the input, and drives the run. Caller holds the session lock and has verified idle. */
1916
+ /**
1917
+ * Shared task launch (fresh tasks, auto-started follow-ups and retries): flips to running,
1918
+ * publishes the input, and drives the run. Caller holds the session lock and has verified
1919
+ * idle. A retry's input is empty: nothing is published or held, and drive gets no title
1920
+ * material — the turn it resends already offered its text when it was first sent.
1921
+ */
1294
1922
  private launchTask;
1295
1923
  /**
1296
1924
  * Auto-start of queued follow-ups: called from drive's finally whenever a Task or
@@ -1305,6 +1933,53 @@ declare class SessionManager {
1305
1933
  startCompact(sessionId: string): Promise<{
1306
1934
  sessionId: string;
1307
1935
  }>;
1936
+ /**
1937
+ * Switches the Session's model in place (`POST /switch-model`) — core `Session.switchModel`,
1938
+ * gated like a compaction (open, not deleting, idle). Core decides everything before its
1939
+ * first event, so the head of its stream is pulled here, under the lock, and tells the three
1940
+ * outcomes apart:
1941
+ *
1942
+ * - it throws — a refusal, answered 409 with the code of its reason (see `switchRefusal`);
1943
+ * - it is already done — a Session that never ran has no context to close: core re-assembled
1944
+ * its first context on the target, nothing streams, and the row carries the new pair by
1945
+ * the time this returns (`switched: true`; the route answers with the fresh DTO);
1946
+ * - it yields — a driven run like a compaction (`switched: false`): status `compacting`,
1947
+ * idle when it ends. The head is a compaction's begin, the first record of a bootstrap (a
1948
+ * Session loaded from its Trace opens its context first), or — for a context with no
1949
+ * completed turn, which streams no pair — the opener's first record.
1950
+ *
1951
+ * The switch is the entry's run from that pull on — it can be stopped, and a deletion or a
1952
+ * shutdown waits for it — while the entry only reads `compacting` once there is something
1953
+ * to stream, so a refusal never flickers a state.
1954
+ *
1955
+ * The entry and the row follow the runtime as the new context's `session_meta` passes
1956
+ * through the drive (see `drive`); a switch that does not complete streams no meta and
1957
+ * leaves them where they were. Usage: a drive bills to the model it started on, so the
1958
+ * switch's compaction request goes to the model that served it and the next run to the new
1959
+ * one.
1960
+ */
1961
+ startSwitch(sessionId: string, ref: ModelRefDto): Promise<{
1962
+ sessionId: string;
1963
+ switched: boolean;
1964
+ }>;
1965
+ /** Who is told when a Session's model moves (see `onModelChanged`). */
1966
+ private readonly modelListeners;
1967
+ /**
1968
+ * Subscribes to a Session's model moving: an in-session switch completing, or a row brought
1969
+ * back in step with its Trace when the Session loads. The listener runs once the entry and
1970
+ * the row carry the new pair. Returns the unsubscribe. Company mode listens: an employee's
1971
+ * model is its desk Session's.
1972
+ */
1973
+ onModelChanged(listener: (sessionId: string, model: ModelRefDto) => void): () => void;
1974
+ /**
1975
+ * Moves the entry — and the row it caches into — to the model the runtime reports itself on,
1976
+ * when that differs: the pair the next drive attributes usage to, and the one
1977
+ * `GET /sessions/:id` describes. A runtime that reports none (test fakes) leaves the entry
1978
+ * alone. The row write is guarded like the other bookkeeping writes (see `touchRow`): a
1979
+ * switch must not be stranded mid-drive by a closed DB handle, and the entry is right either
1980
+ * way. The same holds for whoever listens: a listener that throws is recorded, not rethrown.
1981
+ */
1982
+ private syncEntryModel;
1308
1983
  /** Submit an approval decision; returns false if the pending approval doesn't exist (already decided/unknown). */
1309
1984
  decideApproval(sessionId: string, toolCallId: string, decision: "allow" | "deny"): boolean;
1310
1985
  /**
@@ -1436,6 +2111,15 @@ declare class SessionManager {
1436
2111
  */
1437
2112
  beginSessionDeletion(sessionId: string): Promise<void>[];
1438
2113
  endSessionDeletion(sessionId: string): void;
2114
+ /**
2115
+ * The hand-over's half of a swap: this generation stops working on the state and leaves
2116
+ * everything in it as it is — running Tasks keep running, pending approvals keep waiting —
2117
+ * for the next generation to take over (PRFC-0015). Only what is this instance's own stops:
2118
+ * the idle sweep. `shutdown` is the other ending, for a state nobody takes over.
2119
+ */
2120
+ detach(): void;
2121
+ /** The Sessions with a Task in flight right now — what a swap parks, so that a successor that cannot take the state over knows which ones to start again. */
2122
+ runningSessionIds(): string[];
1439
2123
  /** Graceful shutdown: reject new tasks (503), interrupt all active runs, and wait for them to finish (default ≤5s). */
1440
2124
  shutdown(timeoutMs?: number): Promise<void>;
1441
2125
  /**
@@ -1448,6 +2132,34 @@ declare class SessionManager {
1448
2132
  * tests and timers.
1449
2133
  */
1450
2134
  sweepIdle(now?: number, idleMs?: number): void;
2135
+ /**
2136
+ * Pins (`on`) or releases a Session's runtime entry against idle eviction (see sweepIdle).
2137
+ * The messaging bridge holds the pin for as long as a binding's connection is enabled: that
2138
+ * Session is where the bot lives on this server, and evicting it after half an hour of quiet
2139
+ * makes the next chat message wait for a resume from the Trace before anything else happens.
2140
+ *
2141
+ * It pins only what is loaded and loads nothing itself (preload does). A stale id pins
2142
+ * nothing: an entry a delete removed is gone from the table whatever this set says, and the
2143
+ * bridge releases the id when its connection goes.
2144
+ */
2145
+ keepLoaded(sessionId: string, on: boolean): void;
2146
+ /**
2147
+ * Loads a Session's runtime into the active table ahead of its first Task, through the same
2148
+ * get-or-resume path that Task would take, so the first message after a messaging connection
2149
+ * comes up — an enable, a server restart — does not pay for a resume from the Trace on top of
2150
+ * everything else. Only the load: no run starts, and the core bootstrap (session metadata,
2151
+ * MCP connections) still happens on the first run. Under the Session lock, like startTask, so
2152
+ * a Task arriving mid-load waits for this load instead of starting a second one.
2153
+ *
2154
+ * A Session that is gone or being deleted, alone or with its Agent, resolves quietly: there is
2155
+ * nothing to warm and the caller has nothing to do about it. So does one with no Trace yet,
2156
+ * because loading it is a self-heal REBUILD under a new session id — that would move the
2157
+ * Session out from under the binding that asked, and earlier than any Task would. The row's
2158
+ * `hasTrace` is a cache, so a row that has not caught up only misses the warm-up. Any other
2159
+ * load failure rejects; the first Task meets the same failure and reports it where a person
2160
+ * will see it.
2161
+ */
2162
+ preload(sessionId: string): Promise<void>;
1451
2163
  private assertOpen;
1452
2164
  /** The Agent owning this Session is being deleted → 409 (guards against directory recreation inside the deletion race window). */
1453
2165
  private assertAgentNotDeleting;
@@ -1455,6 +2167,7 @@ declare class SessionManager {
1455
2167
  private assertSessionNotDeleting;
1456
2168
  private assertIdle;
1457
2169
  private generationOf;
2170
+ private refreshProviderCredentialIfNeeded;
1458
2171
  /** get-or-resume-or-heal: use directly on an active-table hit; otherwise load via the loader, updating the index's primary key on self-heal. */
1459
2172
  private ensureEntry;
1460
2173
  /**
@@ -1504,14 +2217,21 @@ declare class SessionManager {
1504
2217
  * row is gone as well.
1505
2218
  */
1506
2219
  private publishBackgroundTasks;
2220
+ /**
2221
+ * Publishes `session_approvals` on the user channel: how many tool calls of this Session wait
2222
+ * for a person now. The Session's own stream carries the calls; this is what lets a list, or
2223
+ * a host watching every Session of a Project, see which one waits without subscribing to each.
2224
+ * Sent for an entry that is being disposed too, so a mark it had is cleared.
2225
+ */
2226
+ private publishApprovals;
1507
2227
  /** Serialize (mutually exclude) execution by sessionId; cleans up the lock-table entry once its chain drains (avoids unbounded growth). */
1508
2228
  private withLock;
1509
2229
  }
1510
- declare const Sessions_base: (abstract new () => Pick<SessionManager, "steer" | "pendingApprovalCount" | "pendingFollowUpCount" | "statusOf" | "pendingApprovals" | "pendingSteeringOf" | "subagentsOf" | "sendToSubagent" | "abortSubagentRun" | "pendingFollowUpsOf" | "liveFragments" | "pendingInputs" | "pendingBootstrap" | "activeCountForAgent" | "invalidateAgentRuntimes" | "invalidateProjectRuntimes" | "assertCanAcceptTask" | "startTask" | "startGoal" | "startCompact" | "decideApproval" | "recallSteering" | "recallFollowUp" | "retryNow" | "abortTask" | "listProcesses" | "probeProcessServices" | "killProcess" | "removeProcess" | "abortProject" | "beginAgentDeletion" | "endAgentDeletion" | "beginSessionDeletion" | "endSessionDeletion" | "atIdleBoundary" | "shutdown" | "sweepIdle">) & (<C>(target: C, context: ClassDecoratorContext) => C);
2230
+ declare const Sessions_base: (abstract new () => Pick<SessionManager, "steer" | "pendingApprovalCount" | "pendingFollowUpCount" | "statusOf" | "pendingApprovals" | "pendingSteeringOf" | "subagentsOf" | "sendToSubagent" | "abortSubagentRun" | "pendingFollowUpsOf" | "liveFragments" | "pendingInputs" | "pendingBootstrap" | "activeCountForAgent" | "invalidateAgentRuntimes" | "invalidateProjectRuntimes" | "assertCanAcceptTask" | "startTask" | "retryTask" | "startGoal" | "startCompact" | "startSwitch" | "onModelChanged" | "decideApproval" | "recallSteering" | "recallFollowUp" | "retryNow" | "abortTask" | "listProcesses" | "probeProcessServices" | "killProcess" | "removeProcess" | "abortProject" | "beginAgentDeletion" | "endAgentDeletion" | "beginSessionDeletion" | "endSessionDeletion" | "atIdleBoundary" | "shutdown" | "detach" | "runningSessionIds" | "sweepIdle" | "keepLoaded" | "preload">) & (<C>(target: C, context: ClassDecoratorContext) => C);
1511
2231
  /** The session runtime: one entry per live Session, task mutex, approvals, streaming. */
1512
2232
  declare abstract class Sessions extends Sessions_base {
1513
2233
  }
1514
- declare const SessionServiceIface_base: (abstract new () => Pick<SessionService, "hasTrace" | "toInfo" | "listSessions" | "sessionStats" | "createSession" | "latestTracePath" | "adoptUnmanagedTraceSessions">) & (<C>(target: C, context: ClassDecoratorContext) => C);
2234
+ declare const SessionServiceIface_base: (abstract new () => Pick<SessionService, "hasTrace" | "toInfo" | "listingOf" | "listSessions" | "archivedRows" | "sessionStats" | "createSession" | "defaultSandbox" | "pickSandbox" | "updateSandbox" | "latestTracePath" | "adoptUnmanagedTraceSessions">) & (<C>(target: C, context: ClassDecoratorContext) => C);
1515
2235
  declare abstract class SessionServiceIface extends SessionServiceIface_base {
1516
2236
  }
1517
2237
 
@@ -1586,42 +2306,40 @@ declare class AgentConfigService implements AgentConfig {
1586
2306
  updateVault(projectId: string, agentId: string, req: VaultUpdateRequest): Promise<VaultResponse>;
1587
2307
  }
1588
2308
 
1589
- declare const AgentConfig_base: (abstract new () => {
1590
- exists(projectId: string, agentId: string): Promise<boolean>;
1591
- requireExists(projectId: string, agentId: string): Promise<void>;
1592
- readCardMeta(projectId: string, agentId: string): Promise<{
2309
+ /** AgentConfig: the mechanism AgentConfigService implements. */
2310
+ declare abstract class AgentConfig {
2311
+ abstract exists(projectId: string, agentId: string): Promise<boolean>;
2312
+ abstract requireExists(projectId: string, agentId: string): Promise<void>;
2313
+ abstract readCardMeta(projectId: string, agentId: string): Promise<{
1593
2314
  name?: string;
1594
2315
  description?: string;
1595
2316
  toolCount: number;
1596
2317
  version: number;
1597
2318
  kernelOutdated: boolean;
1598
2319
  }>;
1599
- getConfig(projectId: string, agentId: string): Promise<AgentConfigView>;
1600
- updateConfig(projectId: string, agentId: string, req: AgentConfigUpdateRequest): Promise<void>;
1601
- resetConfig(projectId: string, agentId: string): Promise<void>;
1602
- kernelUpdate(projectId: string, agentId: string): Promise<AgentKernelUpdateResponse>;
1603
- insertTemplatePlaceholder(projectId: string, agentId: string, feature: "vault" | "skills" | "schedules"): Promise<AgentConfigView>;
1604
- getVault(projectId: string, agentId: string): Promise<VaultResponse>;
1605
- updateVault(projectId: string, agentId: string, req: VaultUpdateRequest): Promise<VaultResponse>;
1606
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
1607
- /** AgentConfig: the mechanism AgentConfigService implements. */
1608
- declare abstract class AgentConfig extends AgentConfig_base {
2320
+ abstract getConfig(projectId: string, agentId: string): Promise<AgentConfigView>;
2321
+ abstract updateConfig(projectId: string, agentId: string, req: AgentConfigUpdateRequest): Promise<void>;
2322
+ abstract resetConfig(projectId: string, agentId: string): Promise<void>;
2323
+ abstract kernelUpdate(projectId: string, agentId: string): Promise<AgentKernelUpdateResponse>;
2324
+ abstract insertTemplatePlaceholder(projectId: string, agentId: string, feature: "vault" | "skills" | "schedules"): Promise<AgentConfigView>;
2325
+ abstract getVault(projectId: string, agentId: string): Promise<VaultResponse>;
2326
+ abstract updateVault(projectId: string, agentId: string, req: VaultUpdateRequest): Promise<VaultResponse>;
1609
2327
  }
1610
- declare const AgentLifecycle_base: (abstract new () => {
1611
- listAgents(projectId: string): Promise<AgentListItem[]>;
1612
- deleteAgent(projectId: string, agentId: string): Promise<void>;
1613
- createAgent(projectId: string, agentId: string, name?: string, description?: string, skillNames?: readonly string[], directory?: {
2328
+ /** AgentLifecycle: the mechanism AgentService implements. */
2329
+ declare abstract class AgentLifecycle {
2330
+ abstract listAgents(projectId: string): Promise<AgentListItem[]>;
2331
+ /** Every id `createAgent` refuses as taken, as names only, including a folder that does not list. */
2332
+ abstract takenAgentIds(projectId: string): Promise<string[]>;
2333
+ abstract deleteAgent(projectId: string, agentId: string): Promise<void>;
2334
+ abstract createAgent(projectId: string, agentId: string, name?: string, description?: string, skillNames?: readonly string[], directory?: {
1614
2335
  path: string;
1615
2336
  names: readonly string[];
1616
2337
  }, archive?: Buffer<ArrayBufferLike>): Promise<AgentListItem>;
1617
- pluginVersion(projectId: string, agentId: string, pluginName: string): Promise<{
2338
+ abstract pluginVersion(projectId: string, agentId: string, pluginName: string): Promise<{
1618
2339
  installed: string | null;
1619
2340
  library: string | null;
1620
2341
  }>;
1621
- updatePlugin(projectId: string, agentId: string, pluginName: string): Promise<void>;
1622
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
1623
- /** AgentLifecycle: the mechanism AgentService implements. */
1624
- declare abstract class AgentLifecycle extends AgentLifecycle_base {
2342
+ abstract updatePlugin(projectId: string, agentId: string, pluginName: string): Promise<void>;
1625
2343
  }
1626
2344
 
1627
2345
  interface AgentListItem {
@@ -1641,6 +2359,8 @@ interface AgentListItem {
1641
2359
  vaultKeyCount: number;
1642
2360
  /** Number of scheduled tasks (count of .toml files under schedule/, including invalid ones). */
1643
2361
  scheduleCount: number;
2362
+ /** Workbench folders with a manifest, including ones with a load error. */
2363
+ workbenchCount: number;
1644
2364
  /** Number of installed Skills (count of skills/<name>/ directories that contain a SKILL.md). */
1645
2365
  skillCount: number;
1646
2366
  /** Number of installed hook packages (hooks/<name>/ directories with a hooks.json). */
@@ -1658,6 +2378,14 @@ declare class AgentService implements AgentLifecycle {
1658
2378
  private readonly snapshots;
1659
2379
  /** Union of DB index ∪ directory scan; unmanaged directory Agents are backfilled into the DB. */
1660
2380
  listAgents(projectId: string): Promise<AgentListItem[]>;
2381
+ /** Count installed folders without compiling or walking their UI/runtime assets. */
2382
+ private workbenchCount;
2383
+ /**
2384
+ * Every id `createAgent` refuses as taken, as names only: the index rows and every entry under
2385
+ * `agents/`. Unlike `listAgents`, this includes a folder with no `system_config.yaml`, and it
2386
+ * opens no Agent.
2387
+ */
2388
+ takenAgentIds(projectId: string): Promise<string[]>;
1661
2389
  /** Number of vault keys (falls back to 0 on read failure). */
1662
2390
  private vaultKeyCount;
1663
2391
  /** Number of scheduled tasks: count of .toml files under schedule/ (0 if the directory doesn't exist). */
@@ -1665,7 +2393,9 @@ declare class AgentService implements AgentLifecycle {
1665
2393
  /**
1666
2394
  * One pass over `skills/` and one over `hooks/`, answering the card's questions: how many
1667
2395
  * skills and hook packages are installed, and which of them the plugin library has moved
1668
- * past — by plugin, since a plugin is what gets reinstalled to catch up.
2396
+ * past — each part against the library's same part (a skill's dated version against that
2397
+ * skill's, a hook package's against the plugin's `hooks.version`), reported by plugin, since
2398
+ * a plugin is what gets reinstalled to catch up.
1669
2399
  *
1670
2400
  * Only the HEAD of each SKILL.md is read, and that bound is load-bearing rather than a
1671
2401
  * micro-optimization: a preinstalled library is ~180 KB of SKILL.md across seventeen files
@@ -1686,18 +2416,20 @@ declare class AgentService implements AgentLifecycle {
1686
2416
  */
1687
2417
  private installedPlugins;
1688
2418
  /**
1689
- * Where one library plugin stands on one Agent: `installed` is the OLDEST version among the
1690
- * plugin's installed skills and its installed hook package — the same "any component behind
1691
- * the library is an update" rule {@link installedPlugins} draws the list cards' hints from —
1692
- * and null when the Agent carries none of them; `library` is the version the library offers
1693
- * now, null when the library has no such plugin. Only the head of each SKILL.md is read
1694
- * (see SKILL_HEAD_BYTES), so the answer costs one small read per skill the plugin ships.
2419
+ * Where one library plugin stands on one Agent, as one pair of dated versions of the same
2420
+ * part — so `library` newer than `installed` is exactly "some part is behind", the rule
2421
+ * {@link installedPlugins} draws the list cards' hints from. Each part the Agent carries (a
2422
+ * skill, the hook package) is paired with the library's same part; among the parts that are
2423
+ * behind — or, when none is, among all of them — the one with the OLDEST installed version
2424
+ * answers. `installed` is null when the Agent carries none of the plugin's parts, with
2425
+ * `library` then the plugin's content version; both are null when the library has no such
2426
+ * plugin. Only the head of each SKILL.md is read (see SKILL_HEAD_BYTES), so the answer costs
2427
+ * one small read per skill the plugin ships.
1695
2428
  *
1696
2429
  * The organization runtime asks this per employee per pass to keep company plugins current.
1697
2430
  * A skill the Agent does not carry — never installed, or removed by hand — counts for
1698
- * nothing: what is compared is the oldest version the Agent actually has, so a plugin whose
1699
- * skills are all current reads as current even where one of them is missing. Restoring a
1700
- * deleted skill is the Agents page's business, not a pass's.
2431
+ * nothing: a plugin whose carried skills are all current reads as current even where one of
2432
+ * them is missing. Restoring a deleted skill is the Agents page's business, not a pass's.
1701
2433
  */
1702
2434
  pluginVersion(projectId: string, agentId: string, pluginName: string): Promise<{
1703
2435
  installed: string | null;
@@ -1748,13 +2480,14 @@ declare class AgentService implements AgentLifecycle {
1748
2480
  }, archive?: Buffer): Promise<AgentListItem>;
1749
2481
  }
1750
2482
 
1751
- declare const MessagingBindings_base: (abstract new () => {
1752
- find(sessionId: string, channel: string): MessagingBindingRow | null;
1753
- findEnabled(sessionId: string): MessagingBindingRow | null;
1754
- listForSession(sessionId: string): MessagingBindingRow[];
1755
- findEnabledByAccount(channel: string, accountId: string): MessagingBindingRow | null;
1756
- listAll(): MessagingBindingRow[];
1757
- upsert(args: {
2483
+ /** MessagingBindings: the mechanism MessagingBindingsRepo implements. */
2484
+ declare abstract class MessagingBindings {
2485
+ abstract find(sessionId: string, channel: string): MessagingBindingRow | null;
2486
+ abstract findEnabled(sessionId: string): MessagingBindingRow | null;
2487
+ abstract listForSession(sessionId: string): MessagingBindingRow[];
2488
+ abstract findEnabledByAccount(channel: string, accountId: string): MessagingBindingRow | null;
2489
+ abstract listAll(): MessagingBindingRow[];
2490
+ abstract upsert(args: {
1758
2491
  sessionId: string;
1759
2492
  channel: string;
1760
2493
  accountId: string;
@@ -1763,14 +2496,11 @@ declare const MessagingBindings_base: (abstract new () => {
1763
2496
  finalReplyOnly?: boolean;
1764
2497
  renderMarkdown?: boolean;
1765
2498
  }): MessagingBindingRow;
1766
- setEnabled(sessionId: string, channel: string, enabled: boolean): void;
1767
- recordChat(sessionId: string, channel: string, chatId: string, isDirect: boolean): void;
1768
- recordInboundWatermark(sessionId: string, channel: string, messageId: string | null): void;
1769
- delete(sessionId: string, channel: string): void;
1770
- deleteSession(sessionId: string): void;
1771
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
1772
- /** MessagingBindings: the mechanism MessagingBindingsRepo implements. */
1773
- declare abstract class MessagingBindings extends MessagingBindings_base {
2499
+ abstract setEnabled(sessionId: string, channel: string, enabled: boolean): void;
2500
+ abstract recordChat(sessionId: string, channel: string, chatId: string, isDirect: boolean): void;
2501
+ abstract recordInboundWatermark(sessionId: string, channel: string, messageId: string | null): void;
2502
+ abstract delete(sessionId: string, channel: string): void;
2503
+ abstract deleteSession(sessionId: string): void;
1774
2504
  }
1775
2505
 
1776
2506
  interface MessagingBindingRow {
@@ -1846,8 +2576,7 @@ interface MessagingBindingRow {
1846
2576
  * every connector method validates its own shape and throws a readable error on a
1847
2577
  * malformed one — the bridge treats that like any other channel failure.
1848
2578
  */
1849
- /** Known messaging channels (the DB stores the discriminator as text; unknown values are skipped defensively). */
1850
- type MessagingChannel = "feishu" | "telegram" | "qq" | "wechat";
2579
+
1851
2580
  /** One inbound image's bytes, once fetched, with the MIME type the bridge needs for its data URL. */
1852
2581
  interface MessagingInboundImageData {
1853
2582
  data: Buffer;
@@ -1957,8 +2686,25 @@ interface MessagingConnectorHandlers {
1957
2686
  onMessage(msg: MessagingInboundMessage): void | Promise<void>;
1958
2687
  /** The connection completed a handshake (may fire again after an automatic reconnect). */
1959
2688
  onReady?(): void;
1960
- /** The connection failed and the channel gave up (or the initial connect failed). */
2689
+ /**
2690
+ * The connection failed and the channel gave up (or the initial connect failed). A
2691
+ * {@link MessagingChannelError} says whether the outage clears without anyone acting.
2692
+ */
1961
2693
  onError?(err: unknown): void;
2694
+ /**
2695
+ * The connection's account started or stopped holding replies it could not deliver yet
2696
+ * (see {@link MessagingReplyHeldError}): the ISO time since which it holds some, or null once
2697
+ * it holds none. Fired on every change, and once at connect for a backlog held from before.
2698
+ * Only a channel that holds replies (WeChat) ever calls it.
2699
+ */
2700
+ onHeldChange?(since: string | null): void;
2701
+ /**
2702
+ * A send the connector made on its OWN — a withheld tail flushed after the quiet period, held
2703
+ * replies delivered on the next inbound message — failed for good. No caller is waiting on
2704
+ * those sends, so this is the only way their failure reaches the binding's status and the
2705
+ * error table.
2706
+ */
2707
+ onSendFailed?(err: unknown): void;
1962
2708
  }
1963
2709
  /** A live inbound event stream; `close` ends it (idempotent). */
1964
2710
  interface MessagingConnection {
@@ -2020,6 +2766,13 @@ interface MessagingSendOptions {
2020
2766
  * reach the one message a user sends to check whether the binding works at all.
2021
2767
  */
2022
2768
  markdown?: boolean;
2769
+ /**
2770
+ * This send is a probe of the outbound leg ("send test message"), not a reply. A connector
2771
+ * that would HOLD a refused message for later delivery must throw
2772
+ * {@link MessagingNeedsRecentMessageError} instead: a held probe that arrives an hour later
2773
+ * proves nothing, and the person pressing the button needs to hear why it did not arrive now.
2774
+ */
2775
+ probe?: boolean;
2023
2776
  }
2024
2777
  /** Outbound half of one bound account. Every method throws on failure with a readable reason. */
2025
2778
  interface MessagingClient {
@@ -2071,7 +2824,8 @@ interface MessagingChannelConnector {
2071
2824
  * applies to every route behind this middleware, SSE endpoints included (the CLI
2072
2825
  * consumes SSE via fetch with headers). A Bearer header that does not match fails the
2073
2826
  * request rather than falling back to the cookie: silently downgrading a wrong explicit
2074
- * credential would mask misconfiguration.
2827
+ * credential would mask misconfiguration. A few writes refuse the token all the same and take a
2828
+ * person's sign-in (requireHuman).
2075
2829
  *
2076
2830
  * Accessing a protected API while logged out -> 401 `{error:{code:"unauthorized"}}`.
2077
2831
  * CSRF: SameSite=Lax plus a Content-Type an HTML form cannot forge (see jsonOnlyWrites).
@@ -2104,6 +2858,16 @@ interface MessagingTaskRunnerShape {
2104
2858
  sessionId: string;
2105
2859
  queued: boolean;
2106
2860
  }>;
2861
+ /**
2862
+ * Pins the Session's runtime against idle eviction while a connection is enabled (`on`), or
2863
+ * releases the pin.
2864
+ */
2865
+ keepLoaded(sessionId: string, on: boolean): void;
2866
+ /**
2867
+ * Loads the Session's runtime ahead of its first message, starting nothing; resolves quietly
2868
+ * for a Session that is gone or being deleted.
2869
+ */
2870
+ preload(sessionId: string): Promise<void>;
2107
2871
  }
2108
2872
  /**
2109
2873
  * Minimal dependency on the sessions index: existence checks for reconcile/cascade, the
@@ -2293,6 +3057,14 @@ declare class MessagingBridge {
2293
3057
  * the log, which is where "the replies moved to General a while ago" is answerable at all.
2294
3058
  */
2295
3059
  private noteSend;
3060
+ /** The connector's `onHeldChange`, behind the stale-entry guard every connector callback has. */
3061
+ private setHeldReplySince;
3062
+ /**
3063
+ * Whether a send's failure is the connector HOLDING the reply rather than losing it (see
3064
+ * MessagingReplyHeldError). Such a send is not a delivery failure — no error record, no
3065
+ * `lastDeliveryError` — and is logged once per holding episode, not once per message.
3066
+ */
3067
+ private wasHeld;
2296
3068
  private clientFor;
2297
3069
  /**
2298
3070
  * Has this binding already processed this message? A duplicate is a complete no-op —
@@ -2470,9 +3242,12 @@ declare class MessagingBridge {
2470
3242
  * A file the run did not write is dropped SILENTLY, and so is a name matching no file at
2471
3243
  * all: the rule reads prose, so a reply that mentions the config it read — or describes a
2472
3244
  * `hello-world.md` it never wrote — is the ordinary case, and announcing those puts an
2473
- * error-shaped line under correct answers. Both are logged instead. What IS named in the
2474
- * chat is a file that exists and whose upload failed: there the reply promised something
2475
- * that then did not arrive, which is the one case where silence reads as broken.
3245
+ * error-shaped line under correct answers. Both are logged instead. What IS recorded is a
3246
+ * file that exists and still did not go out — over a ceiling, past the count cap, or
3247
+ * refused on the way: the reply promised something that then did not arrive, so it is filed
3248
+ * under the Session's Project, once the batch is done, in one record per cause naming every
3249
+ * file it held back (see messagingFilesNotSentRecords). None of it is posted into the chat,
3250
+ * which carries the reply and its files and nothing about the ones that stayed behind.
2476
3251
  *
2477
3252
  * Always plain sends, never a threaded reply: a run that mentions a file has by
2478
3253
  * definition already sent the text that mentions it, and that message took the group's
@@ -2481,19 +3256,11 @@ declare class MessagingBridge {
2481
3256
  */
2482
3257
  private deliverFiles;
2483
3258
  /**
2484
- * Names one file that did not make it, in the chat.
2485
- *
2486
- * Every other way a file does not arrive is already visible there — over the byte cap,
2487
- * past the count cap, an inbound refusal. A failed upload reached `error_records` alone,
2488
- * which the person in the chat cannot open, so the feature just looked broken. A missing
2489
- * permission gets its own wording: it is the one failure they can fix themselves, in
2490
- * about ten seconds, given the scope name and the console link.
2491
- *
2492
- * Its own try/catch: a chat that will not take the notice must not take the rest of the
2493
- * batch down with it, and the upload failure behind it is already recorded.
3259
+ * One mirrored file: read under the outer ceiling, then sent as a picture or an attachment.
3260
+ * Resolves null once the file went out. A file over the ceiling for its kind is not sent and
3261
+ * resolves to that ceiling instead; a read or a send the channel refused throws. The caller
3262
+ * records both (see messagingFilesNotSentRecords).
2494
3263
  */
2495
- private noteFileFailure;
2496
- /** One mirrored file: read under the outer ceiling, then sent as a picture or an attachment. */
2497
3264
  private deliverOneFile;
2498
3265
  /** One-line notice (approval waiting) to the last known chat; silent before one exists. */
2499
3266
  private deliverNotice;
@@ -2521,7 +3288,7 @@ declare const MessagingTaskRunner_base: (abstract new () => MessagingTaskRunnerS
2521
3288
  /** What the bridge needs of the session runtime — declared at the consumer. */
2522
3289
  declare abstract class MessagingTaskRunner extends MessagingTaskRunner_base {
2523
3290
  }
2524
- declare const Messaging_base: (abstract new () => Pick<MessagingBridge, "start" | "stop" | "statusOf" | "sync" | "unbind" | "unbindSession" | "testCredentials" | "sendTestMessage">) & (<C>(target: C, context: ClassDecoratorContext) => C);
3291
+ declare const Messaging_base: (abstract new () => Pick<MessagingBridge, "stop" | "sync" | "start" | "statusOf" | "unbind" | "unbindSession" | "testCredentials" | "sendTestMessage">) & (<C>(target: C, context: ClassDecoratorContext) => C);
2525
3292
  /** The chat-channel bridge: bindings, delivery, status. */
2526
3293
  declare abstract class Messaging extends Messaging_base {
2527
3294
  }
@@ -2532,21 +3299,42 @@ interface MessagingSlots {
2532
3299
  }, MessagingChannelConnector>;
2533
3300
  }
2534
3301
 
2535
- declare const Http_base: (abstract new () => {
2536
- fetch(request: Opaque<"Request", Request>): Promise<Opaque<"Response", Response>>;
2537
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
3302
+ /** A settings group as a module declares it: a configuration, and where the page draws it. */
3303
+ interface SettingsGroupDecl extends PluginConfiguration {
3304
+ /** Another group's name (its contribution id): this one is drawn inside that card and saved with it. */
3305
+ parent?: string;
3306
+ /** Position among the groups: lower first (absent = 100), then declaration order. */
3307
+ order?: number;
3308
+ }
3309
+ /** What a module reads: the group it declared, and a watch on it. */
3310
+ declare abstract class PluginConfig {
3311
+ /** The stored values merged onto the declared defaults; `{}` for a name no group answers to. */
3312
+ abstract get(name: string): Record<string, unknown>;
3313
+ /** Fires with the new document after every save of `name`; returns the unsubscribe. */
3314
+ abstract watch(name: string, cb: (values: Record<string, unknown>) => void): () => void;
3315
+ /** Whether anything was ever saved under `name` — what tells a default from a choice. */
3316
+ abstract saved(name: string): boolean;
3317
+ /** The configuration `name` was declared with (what `resolveTable` reads a table by); undefined for none. */
3318
+ abstract schema(name: string): PluginConfiguration | undefined;
3319
+ }
3320
+ interface PluginConfigSlots {
3321
+ /** A settings group, as data: its id is its name, the data its configuration. */
3322
+ groups: Slot<SettingsGroupDecl>;
3323
+ }
3324
+
2538
3325
  /** The assembled business surface: one request in, one response (or a decline) out. */
2539
- declare abstract class Http extends Http_base {
3326
+ declare abstract class Http {
3327
+ abstract fetch(request: Opaque<"Request", Request>): Promise<Opaque<"Response", Response>>;
2540
3328
  }
2541
3329
  interface HttpSlots {
2542
3330
  /**
2543
- * A route group. `auth: "user"` mounts it behind the cookie gate, `"none"` in front of
2544
- * it; `order` is the mount position (a stable number, since Hono matches in order). The
2545
- * code half is the group's Hono app.
3331
+ * A route group. `auth: "user"` accepts a cookie or the local API token; `"token"` accepts
3332
+ * only the local API token, without renewing cookies; `"none"` is public. `order` is the
3333
+ * mount position (a stable number, since Hono matches in order). The code half is the Hono app.
2546
3334
  */
2547
3335
  routes: Slot<{
2548
3336
  prefix: string;
2549
- auth: "user" | "none";
3337
+ auth: "user" | "token" | "none";
2550
3338
  order: number;
2551
3339
  }, Opaque<"Hono", Hono<AppEnv>>>;
2552
3340
  }
@@ -2557,10 +3345,6 @@ interface HttpSlots {
2557
3345
  * own module.json and renders what it has a renderer for.
2558
3346
  */
2559
3347
 
2560
- declare const WebShell_base: (abstract new () => {
2561
- /** Every contribution to the web slots, by slot, in module order. */
2562
- contributions(): ContributionsResponse;
2563
- }) & (<C>(target: C, context: ClassDecoratorContext) => C);
2564
3348
  /**
2565
3349
  * The frontend's slots, declared on the SERVER so a server module (or a plugin) can
2566
3350
  * contribute a page, an Agent settings tab or a Session tab as manifest data. The web
@@ -2568,7 +3352,9 @@ declare const WebShell_base: (abstract new () => {
2568
3352
  * renderer it knows: a `builtin` name from its own registry, or an `iframe`. No code
2569
3353
  * crosses this boundary — only data.
2570
3354
  */
2571
- declare abstract class WebShell extends WebShell_base {
3355
+ declare abstract class WebShell {
3356
+ /** Every contribution to the web slots, by slot, in module order. */
3357
+ abstract contributions(): ContributionsResponse;
2572
3358
  }
2573
3359
  interface WebShellSlots {
2574
3360
  /** A page: its route, whether it sits in the main nav, whether it is admin-only. */
@@ -2585,11 +3371,22 @@ interface WebShellSlots {
2585
3371
  order: number;
2586
3372
  renderer: RendererRef;
2587
3373
  };
2588
- /** A tab beside a Session's chat (a workflow UI lives here). */
3374
+ /**
3375
+ * A tab beside a Session's chat. A workbench contributes here too, from its own tree, where
3376
+ * the host publishes this slot under this module's name and scopes the tab to its Agent.
3377
+ */
2589
3378
  sessionTabs: {
2590
3379
  key: string;
2591
- renderer: RendererRef;
3380
+ title: string;
3381
+ titleZh?: string;
3382
+ renderer: {
3383
+ builtin: string;
3384
+ } | {
3385
+ iframe: {
3386
+ src: string;
3387
+ };
3388
+ };
2592
3389
  };
2593
3390
  }
2594
3391
 
2595
- export { AgentConfigService, AgentService, Http, type HttpSlots, Messaging, type MessagingSlots, Sandbox, type SandboxSlots, SessionServiceIface, Sessions, Terminals, WebShell, type WebShellSlots };
3392
+ export { AgentConfigService, AgentService, Http, type HttpSlots, Messaging, type MessagingSlots, PluginConfig, type PluginConfigSlots, PluginConfiguration, Sandbox, type SandboxSlots, SessionServiceIface, Sessions, type SettingsGroupDecl, Terminals, WebShell, type WebShellSlots };