@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,6 +1,96 @@
1
- import { OmniMessage, ToolCallPayload, CompactionMode } from '@prismshadow/penguin-core/omnimessage';
1
+ import { SessionSource, OmniMessage, ToolCallPayload, CompactionMode } from '@prismshadow/penguin-core/omnimessage';
2
+ export { SessionSource } from '@prismshadow/penguin-core/omnimessage';
2
3
  import { ThinkingLevelName, ToolDefinitionConfig, MCPServerConfig } from '@prismshadow/penguin-core/interfaces';
3
- import { VersionReport } from '@prismshadow/penguin-core';
4
+ import { HarnessHistory, VersionReport } from '@prismshadow/penguin-core';
5
+ export { HarnessHistoryEntry, IfacesSummary } from '@prismshadow/penguin-core';
6
+ import { IfacesDiff } from '@prismshadow/penguin-hmr';
7
+ export { IfaceChange, IfacesDiff, MemberChange, ModuleChange } from '@prismshadow/penguin-hmr';
8
+
9
+ /** A request the workbench's handler receives (the HTTP shape, minus the transport). */
10
+ interface WorkbenchRequest {
11
+ method: string;
12
+ /** Path below the workbench's `api/` mount, always starting with `/`. */
13
+ path: string;
14
+ query: Record<string, string>;
15
+ /** Request headers, names lower-cased. The app's own credentials (`cookie`, `authorization`) are withheld. */
16
+ headers: Record<string, string>;
17
+ /** The parsed body of a JSON request; `null` otherwise (GET/HEAD, no body, another content type). */
18
+ body: unknown;
19
+ /** The body as sent, when there is one and it is not JSON: a form, an upload, plain text. */
20
+ bytes?: Uint8Array;
21
+ }
22
+ interface WorkbenchResponse {
23
+ /** HTTP status; 200 when absent. */
24
+ status?: number;
25
+ /**
26
+ * Response headers. Naming a `content-type` is what makes the response something other than
27
+ * JSON; `location` with a 3xx status redirects. `set-cookie` is dropped — a workbench does not
28
+ * write the app's cookies.
29
+ */
30
+ headers?: Record<string, string>;
31
+ /** JSON by default (`null` when absent); with a `content-type` header, a string sent as written. */
32
+ body?: unknown;
33
+ /** A binary body (an image, a font, a download); wins over `body`. */
34
+ bytes?: Uint8Array;
35
+ /**
36
+ * A body sent as it is produced — server-sent events, a model's answer token by token, a
37
+ * large download — each chunk written to the client when the iterator yields it; wins over
38
+ * `bytes` and `body`. A client that goes away ends the iteration: `return()` is called, so
39
+ * a `finally` in the generator is where an upstream request is aborted — but a generator
40
+ * suspended inside an `await` receives it only once that await settles, so a relay whose
41
+ * upstream can hang should carry its own deadline rather than rely on the disconnect.
42
+ */
43
+ stream?: AsyncIterable<Uint8Array | string>;
44
+ }
45
+ /** How the Web App draws a contributed tab: a page of the workbench, or a renderer it carries. */
46
+ type WorkbenchTabRenderer = {
47
+ iframe: {
48
+ src: string;
49
+ };
50
+ } | {
51
+ builtin: string;
52
+ };
53
+ /**
54
+ * One tab beside Chat, as the workbench contributed it (`WebModule.sessionTabs`). In a
55
+ * manifest `renderer.iframe.src` is a path inside the workbench folder, under `ui/`; in a
56
+ * `WorkbenchInfo` it is the URL that file is served from.
57
+ */
58
+ interface WorkbenchTab {
59
+ id: string;
60
+ /** Unique within the workbench; the tab's stable name (it appears in the full-page URL). */
61
+ key: string;
62
+ title: string;
63
+ titleZh?: string;
64
+ renderer: WorkbenchTabRenderer;
65
+ }
66
+ interface WorkbenchInfo {
67
+ id: string;
68
+ name: string;
69
+ version: string | null;
70
+ /** Content revision of the whole folder (what history records). */
71
+ revision: string;
72
+ /** Content revision of `ui/`: the cache key of the workbench's pages. Null when it has no `ui/`. */
73
+ uiRev: string | null;
74
+ /** The tabs of the instance that is SERVING — the previous one's while `error` is set. */
75
+ tabs: WorkbenchTab[];
76
+ loadedAt: string;
77
+ /** The boot error when the current files do not load (the previous instance, if any, keeps serving). */
78
+ error: string | null;
79
+ /**
80
+ * What loaded but is probably not what its author meant, each with the edit that settles it
81
+ * — pages under `ui/` that no tab shows, say. Never a failure: `error` is that.
82
+ */
83
+ hints: string[];
84
+ }
85
+ interface WorkbenchVersion {
86
+ revision: string;
87
+ savedAt: string;
88
+ name: string;
89
+ version: string | null;
90
+ uiRev: string | null;
91
+ /** The files of that version (relative paths), for display. */
92
+ files: string[];
93
+ }
4
94
 
5
95
  /**
6
96
  * Web API DTO contract — request/response types shared between server routes and the
@@ -18,19 +108,26 @@ import { VersionReport } from '@prismshadow/penguin-core';
18
108
  * public route/SSE reference for this contract — keep it in sync when changing DTOs.
19
109
  */
20
110
 
21
- /** Unified error response body; `code` is a machine-readable error code, `message` is a Chinese user-facing message. */
111
+ /**
112
+ * Unified error response body: `code` is a machine-readable error code, `message` an English
113
+ * user-facing message, and `details`, on the errors that have them, the facts a client acts on
114
+ * beside the code (the Benchmark id a 409 `benchmark_exists` is about, say) — read from there,
115
+ * never parsed out of the message.
116
+ */
22
117
  interface ErrorBody {
23
118
  error: {
24
119
  code: string;
25
120
  message: string;
121
+ details?: ErrorDetails;
26
122
  };
27
123
  }
124
+ /** An error's structured facts, by name; each code that carries them documents its own. */
125
+ type ErrorDetails = Readonly<Record<string, string>>;
28
126
  /** Session approval mode (reuses the CLI enum). */
29
127
  type ApprovalMode = "allow-all" | "deny-all" | "read-only" | "always-ask";
30
128
  /** Session run status: idle / Task in progress / compacting. */
31
129
  type SessionStatus = "idle" | "running" | "compacting";
32
- /** Session source marker (default = user-created): triggered by Schedule / registered as a subagent session / created by a Benchmark evaluation or optimization. */
33
- type SessionSource = "schedule" | "subagent" | "benchmark";
130
+
34
131
  interface UserInfo {
35
132
  /** Semantic id, i.e. login name: `^[a-z][a-z0-9_-]{1,31}$`, immutable after creation. */
36
133
  userId: string;
@@ -107,8 +204,9 @@ interface MeResponse {
107
204
  * "desktop" is the shell's own window (one-shot token); "setup" was claimed through the
108
205
  * first-login link on a server whose admin password has never been set. Both may set a
109
206
  * password without the old one (it is random and was never shown); only "desktop" opens
110
- * desktop-only routes. "token" marks a request authenticated by the local API token's
111
- * Bearer header (the CLI and agent-driven calls) — no stored session at all.
207
+ * desktop-only routes. "token" marks the local API token's authority, carried by a Bearer
208
+ * header or an exchanged Remote cookie; neither can perform writes requiring a person's
209
+ * sign-in.
112
210
  */
113
211
  sessionVia: "password" | "desktop" | "setup" | "token";
114
212
  /**
@@ -220,6 +318,19 @@ interface ServerSettings {
220
318
  * reports it so clients hide the mode switch. Organizations on disk are untouched.
221
319
  */
222
320
  companyMode: boolean;
321
+ /**
322
+ * Whether users may drive their own Chrome through the PenguinHarness Browser extension
323
+ * (default on). Off closes every connected extension (close code 4009), refuses new
324
+ * connections, and makes the chrome backend unavailable (`extension_disabled`); pairings are
325
+ * kept, so turning it on again lets the extensions reconnect.
326
+ */
327
+ browserExtensionsEnabled: boolean;
328
+ /**
329
+ * Whether external programs may talk to Agents through the Agent API (default on). Off answers
330
+ * every `/api/amsp/v1` request 403 `agent_api_disabled` (a CORS preflight excepted); every
331
+ * Agent's API switch, approval mode and keys are kept, so turning it on again restores them.
332
+ */
333
+ agentApiEnabled: boolean;
223
334
  }
224
335
  interface ServerSettingsResponse {
225
336
  settings: ServerSettings;
@@ -230,6 +341,10 @@ interface ServerSettingsUpdateRequest {
230
341
  proxyForAgent?: boolean;
231
342
  /** Company mode master switch; see `ServerSettings.companyMode`. */
232
343
  companyMode?: boolean;
344
+ /** Chrome extension switch; see `ServerSettings.browserExtensionsEnabled`. */
345
+ browserExtensionsEnabled?: boolean;
346
+ /** Agent API switch; see `ServerSettings.agentApiEnabled`. */
347
+ agentApiEnabled?: boolean;
233
348
  /**
234
349
  * New proxy address. Accepted forms: any proxy URL undici's dispatcher takes —
235
350
  * `http://`, `https://`, `socks5://` / `socks://`, credentials allowed — or bare
@@ -311,6 +426,19 @@ interface DraftShortcut {
311
426
  title: string;
312
427
  prompt: string;
313
428
  }
429
+ /**
430
+ * The account's keyboard shortcut overrides, one section per platform (the Web App's `Mod` token
431
+ * is ⌘ on macOS and Ctrl elsewhere, so a deliberate macOS-only binding must not leak to Windows).
432
+ * Each section maps a command id to a chord string ("Mod+Shift+KeyW") or null (explicitly
433
+ * unbound); only rows that differ from the Web App's default are present. Bounded on write by
434
+ * services/keybindings.ts.
435
+ */
436
+ interface StoredKeybindings {
437
+ v: 1;
438
+ mac?: Record<string, string | null>;
439
+ windows?: Record<string, string | null>;
440
+ linux?: Record<string, string | null>;
441
+ }
314
442
  /** User UI preferences (SQLite ui_prefs, free-form JSON; known keys declared here). */
315
443
  interface UiPrefs {
316
444
  theme?: "light" | "dark";
@@ -334,14 +462,29 @@ interface UiPrefs {
334
462
  * known key holding user-authored text rather than a flag or an id.
335
463
  */
336
464
  draftShortcuts?: DraftShortcut[];
465
+ /** Keyboard shortcut overrides, replaced whole on every write and validated by services/keybindings.ts. */
466
+ keybindings?: StoredKeybindings;
337
467
  /** Personal company-mode switch (default on): off only hides this user's mode switch; organizations keep running. */
338
468
  companyMode?: boolean;
339
469
  /** The work mode the user last chose in the shell: development (default) or company. */
340
470
  workMode?: "dev" | "company";
341
471
  /** The organization last opened in company mode, as `<projectId>/<orgId>`. */
342
472
  lastOrgKey?: string;
473
+ /** The one group balance shown beside the user name (pinned on the models page); null when unpinned. */
474
+ pinnedBalance?: PinnedBalance | null;
475
+ /**
476
+ * The browser this user's agents drive. Written only through PUT /api/builtin-browser/backend
477
+ * (PUT /api/me/prefs refuses it): absent means built-in for an admin on the desktop, chrome
478
+ * everywhere else.
479
+ */
480
+ browserBackend?: BrowserBackend;
343
481
  [key: string]: unknown;
344
482
  }
483
+ /** A group balance pinned beside the user name: which Project's key reads which group's balance. */
484
+ interface PinnedBalance {
485
+ projectId: string;
486
+ provider: string;
487
+ }
345
488
  interface PrefsResponse {
346
489
  prefs: UiPrefs;
347
490
  }
@@ -395,7 +538,7 @@ interface MemberAddResponse {
395
538
  }
396
539
  /**
397
540
  * Model reference DTO: `(provider, modelId)` pair.
398
- * `modelId` is the upstream request id, sent to AgentHub as-is — `<provider>/<id>` string
541
+ * `modelId` is the upstream request id, sent to MMSP as-is — `<provider>/<id>` string
399
542
  * concatenation is forbidden throughout the pipeline.
400
543
  */
401
544
  interface ModelRefDto {
@@ -408,6 +551,37 @@ interface ModelPricingDto {
408
551
  cacheWrite: number;
409
552
  output: number;
410
553
  }
554
+ /**
555
+ * One group's `[providers.<id>]` connection as the page reads it: the values the GROUP stores
556
+ * (never the catalog's), the key masked. An absent field is unset: a row with no value of its
557
+ * own then gets the client's default.
558
+ */
559
+ interface ProviderConnectionDto {
560
+ baseUrl?: string;
561
+ clientType?: string;
562
+ apiKeyMasked?: string;
563
+ /** When the group key was written (ISO 8601). */
564
+ createdAt?: string;
565
+ }
566
+ /** Where an effective connection value comes from: the row itself, its group, or nowhere (the client's default). */
567
+ type ConnectionSourceDto = "model" | "provider" | "none";
568
+ /**
569
+ * What a row is actually used with, after core's effectiveConnection (row -> group -> none,
570
+ * per field; the file's values only — the catalog is not consulted), and where each value
571
+ * came from. The group's key counts only where it reaches the row (core's groupKeyReaches).
572
+ * The row's own values stay in `clientType` / `credential` (what the form edits); this is for
573
+ * display.
574
+ */
575
+ interface ModelEffectiveConnection {
576
+ baseUrl?: string;
577
+ baseUrlSource: ConnectionSourceDto;
578
+ clientType?: string;
579
+ clientTypeSource: ConnectionSourceDto;
580
+ /** `env` = no row or group key, and the environment lends one on the effective endpoint (modelEnvFallback). */
581
+ apiKeySource: "model" | "provider" | "env" | "none";
582
+ /** Mask of the key the row would use, whichever source; absent when none. */
583
+ apiKeyMasked?: string;
584
+ }
411
585
  /** Read-only credential display: masked key and creation time; plaintext is never sent. */
412
586
  interface CredentialInfo {
413
587
  apiKeyMasked?: string;
@@ -417,7 +591,7 @@ interface CredentialInfo {
417
591
  interface ModelInfo {
418
592
  /** Provider group id (anthropic / openai / …, see core's MODEL_PROVIDERS; custom models use `custom`). */
419
593
  provider: string;
420
- /** Upstream model id (the request id actually sent to AgentHub); paired with `provider` forms the entry's unique key. */
594
+ /** Upstream model id (the request id actually sent to MMSP); paired with `provider` forms the entry's unique key. */
421
595
  modelId: string;
422
596
  /**
423
597
  * Display name: explicit TOML field (user-edited) takes priority, then the built-in catalog;
@@ -427,12 +601,15 @@ interface ModelInfo {
427
601
  */
428
602
  displayName?: string;
429
603
  contextWindow?: number;
430
- /** AgentHub client protocol (`openai-chat`, `openai-responses`, etc.); defaults to AgentHub inferring it from modelId. */
604
+ /**
605
+ * The row's OWN MMSP client type (`openai-chat`, `openai-responses`, etc.) — an override of
606
+ * its group's; absent = the row follows its group (see `effective`).
607
+ */
431
608
  clientType?: string;
432
609
  /**
433
- * Whether image input (vision/multimodal) is supported: the TOML `vision` annotation takes
434
- * priority, falling back to the built-in catalog annotation; if neither exists, defaults to
435
- * unset (= treated as supported).
610
+ * Whether image input (vision/multimodal) is supported: the TOML `vision` annotation only
611
+ * (a new Project writes `false` where the catalog says so); absent = unset, treated as
612
+ * supported. The catalog is not consulted.
436
613
  */
437
614
  vision?: boolean;
438
615
  /**
@@ -445,22 +622,36 @@ interface ModelInfo {
445
622
  /**
446
623
  * Per-model fast mode (TOML `fast_mode` annotation; user-only, never preset by the
447
624
  * built-in catalog): when true, session requests opt into the provider's faster serving
448
- * tier at premium pricing (AgentHub UniConfig `fast_mode`). Only `true` is reported;
625
+ * tier at premium pricing (MMSP UniConfig `fast_mode`). Only `true` is reported;
449
626
  * unset = off. Models without a fast tier reject requests carrying it.
450
627
  */
451
628
  fastMode?: boolean;
452
629
  pricing?: ModelPricingDto;
453
- /** Environment variable name to fall back to when api_key is empty (e.g. ANTHROPIC_API_KEY); unset if no known fallback. */
630
+ /** Running promotion for this row — a fraction in (0, 1) off `pricing`, which is the list price — read from web.db. Absent when the row has none. */
631
+ discount?: number;
632
+ /**
633
+ * Environment variable name the entry falls back to when api_key is empty (e.g.
634
+ * ANTHROPIC_API_KEY); unset when it gets no fallback — an id nothing routes, or an entry
635
+ * whose base URL is not the vendor's own official endpoint (a gateway preset, a custom /
636
+ * vLLM / user-created row with its own endpoint, a vendor row re-pointed at a proxy).
637
+ */
454
638
  envKey?: string;
455
639
  /**
456
640
  * Masked preview (same rule as `credential.apiKeyMasked`) of the value the server process
457
- * currently holds for `envKey` — the plaintext is never serialized. Reported only for
458
- * first-party official entries (vendor group, catalog shape unmodified); gateway, custom
459
- * and user-defined groups never carry it. Absent = the variable is unset or empty, or the
460
- * entry is not first-party.
641
+ * currently holds for `envKey` — the plaintext is never serialized. Reported only where the
642
+ * fallback may be presented as covering the entry (core's modelEnvPreviewKey): a row whose
643
+ * effective base URL is a vendor endpoint (or the Penguin Go relay), or a row with no base URL
644
+ * in a built-in group other than custom, on the client its id's family names. Gateway rows
645
+ * never carry it, and neither do custom / vLLM / user-defined rows with no base URL,
646
+ * although those do fall back. Absent = the variable is unset or empty, or the entry is not
647
+ * previewable. Computed on the effective shape, and only when no row or group key reaches
648
+ * the row.
461
649
  */
462
650
  envKeyMasked?: string;
651
+ /** The row's OWN key (masked) and base URL — overrides of its group's; see `effective`. */
463
652
  credential?: CredentialInfo;
653
+ /** The connection the row is actually used with, and where each value comes from. */
654
+ effective: ModelEffectiveConnection;
464
655
  isDefault: boolean;
465
656
  }
466
657
  interface ModelsResponse {
@@ -475,13 +666,32 @@ interface ModelsResponse {
475
666
  * (the key was fixed since). Absent when the Project has no config file yet.
476
667
  */
477
668
  updatedAt?: string;
669
+ /** Every group's stored connection (`[providers.<id>]`), keyed by provider id; a group with none is absent. */
670
+ providers: Record<string, ProviderConnectionDto>;
478
671
  models: ModelInfo[];
479
672
  }
673
+ /**
674
+ * A change to one group's connection (`PUT /models/providers/:provider`, or a member of
675
+ * `ModelsUpdateRequest.providers`). Omitted fields are kept.
676
+ */
677
+ interface ProviderConnectionUpdate {
678
+ /** `null` clears it; omitted keeps it. */
679
+ baseUrl?: string | null;
680
+ /**
681
+ * `null` or `""` clears it; omitted keeps it. Any group takes one: a row that stores its own
682
+ * protocol keeps it.
683
+ */
684
+ clientType?: string | null;
685
+ /** A non-empty key; sets `createdAt`. Rows with their own key keep it. */
686
+ apiKey?: string;
687
+ /** When true, clears the group key (rows with their own key keep it). */
688
+ clearApiKey?: boolean;
689
+ }
480
690
  /** PUT full-table replace semantics: models not present are deleted; omitting apiKey = keep existing value. Key = (provider, modelId). */
481
691
  interface ModelUpdateEntry {
482
692
  /** Provider group (an independent entry field, always submitted with the request). */
483
693
  provider: string;
484
- /** Upstream model id (sent to AgentHub as-is). */
694
+ /** Upstream model id (sent to MMSP as-is). */
485
695
  modelId: string;
486
696
  /**
487
697
  * Display name; the server does not persist it when it matches the built-in catalog (keeps
@@ -497,7 +707,10 @@ interface ModelUpdateEntry {
497
707
  */
498
708
  renamedFrom?: ModelRefDto;
499
709
  contextWindow?: number;
500
- /** Empty string/omitted = unspecified (AgentHub infers it from modelId). */
710
+ /**
711
+ * Empty string/omitted = the row stores none: it follows its group's protocol, else MMSP
712
+ * routes by the vendor family the modelId begins with.
713
+ */
501
714
  clientType?: string;
502
715
  /** Whether image input (vision/multimodal) is supported; omitted = supported (not persisted). */
503
716
  vision?: boolean;
@@ -506,6 +719,12 @@ interface ModelUpdateEntry {
506
719
  /** Per-model fast mode: only `true` is persisted; omitted or `false` clears the annotation (absent = off). */
507
720
  fastMode?: boolean;
508
721
  pricing?: ModelPricingDto;
722
+ /**
723
+ * Promotion to store for this row, a fraction in (0, 1) off `pricing`; `null` clears it. Omitted
724
+ * keeps the stored promotion — unless this entry renames the row or changes its pricing, which
725
+ * clears it.
726
+ */
727
+ discount?: number | null;
509
728
  /** Providing it overwrites and updates createdAt; omitting it keeps the existing value. */
510
729
  apiKey?: string;
511
730
  /** When true, clears the stored api_key. */
@@ -518,8 +737,30 @@ interface ModelsUpdateRequest {
518
737
  defaultModel?: ModelRefDto;
519
738
  /** Vision model used as a proxy reader for read_file: must be included in models and not annotated vision=false; omitted keeps the existing value. */
520
739
  visionModel?: ModelRefDto;
740
+ /** Group connection changes, merged per provider; a provider absent here keeps its table. */
741
+ providers?: Record<string, ProviderConnectionUpdate>;
521
742
  models: ModelUpdateEntry[];
522
743
  }
744
+ /**
745
+ * `POST /models/sync-presets` (owner): `add` adds the catalog's presets the Project lacks
746
+ * (retired rows never), as a new Project stores them, writes the catalog's
747
+ * `[providers.<id>]` table for a group that has neither rows nor a table yet, and seeds the
748
+ * added rows' promotions, touching nothing else; `restore` makes the model configuration a new
749
+ * Project's and keeps only the API keys: the presets in catalog order, the catalog's group
750
+ * tables, default model and promotions, no vision model, and every other row and every
751
+ * user-defined group deleted. Penguin Go included: its platform's next sync brings back the
752
+ * platform's own models and promotions.
753
+ */
754
+ type PresetSyncMode = "add" | "restore";
755
+ interface PresetSyncRequest {
756
+ mode: PresetSyncMode;
757
+ }
758
+ interface PresetSyncResponse extends ModelsResponse {
759
+ /** Preset rows added (both modes). */
760
+ added: number;
761
+ /** Preset rows the Project already had that `restore` changed (0 for `add`). */
762
+ restored: number;
763
+ }
523
764
  /**
524
765
  * Connectivity test (POST /api/projects/:p/models/test): the model reference is submitted as
525
766
  * a pair in the request body; the rest are optional overrides (for trying out an unsaved
@@ -529,7 +770,7 @@ interface ModelsUpdateRequest {
529
770
  interface ModelTestRequest {
530
771
  /** Provider group of the model under test (paired with modelId). */
531
772
  provider: string;
532
- /** Upstream id of the model under test (sent to AgentHub as-is). */
773
+ /** Upstream id of the model under test (sent to MMSP as-is). */
533
774
  modelId: string;
534
775
  /** Newly entered API key (plaintext); used for the test if provided. */
535
776
  apiKey?: string;
@@ -543,7 +784,7 @@ interface ModelTestRequest {
543
784
  * `undefined` means fall back to the stored value only when not provided.
544
785
  */
545
786
  baseUrl?: string | null;
546
- /** AgentHub client protocol; required for unsaved custom models (otherwise the id can't be auto-routed). */
787
+ /** MMSP client type; required for an unsaved custom model whose id begins with no known vendor family (otherwise it can't be routed). */
547
788
  clientType?: string;
548
789
  /**
549
790
  * Test with fast mode (the frontend always sends the form's current value, so an unsaved
@@ -606,7 +847,7 @@ interface ModelTestResponse {
606
847
  }
607
848
  /**
608
849
  * Protocol auto-detection (POST /api/projects/:p/models/detect, owner): probes which of
609
- * AgentHub's generic protocol clients a custom base URL serves — `openai-responses` first,
850
+ * MMSP's generic protocol clients a custom base URL serves — `openai-responses` first,
610
851
  * then `ant-messages`, then `openai-chat` — and reports the first hit. Used by the custom
611
852
  * model dialog to fill `clientType` from the endpoint itself; costs no tokens (each probe
612
853
  * is a minimal invalid request whose error reveals the protocol shape).
@@ -618,7 +859,12 @@ interface ModelProtocolDetectRequest {
618
859
  apiKey?: string;
619
860
  /** "Clear saved API key" is checked: do not fall back to the stored key (probe the current draft). */
620
861
  clearApiKey?: boolean;
621
- /** Optional paired reference: when it names a stored entry and no apiKey is given, that entry's saved key backs the probes (mirrors the connectivity test). */
862
+ /**
863
+ * Optional paired reference: when it names a stored entry and no apiKey is given, that
864
+ * entry's saved key backs the probes (mirrors the connectivity test), else its group's.
865
+ * `provider` may be sent without `modelId` (the group settings dialog has no model): the
866
+ * group key backs the probes.
867
+ */
622
868
  provider?: string;
623
869
  modelId?: string;
624
870
  }
@@ -632,7 +878,7 @@ interface ModelProtocolDetectRequest {
632
878
  type ProtocolProbeOutcome = "served" | "route_missing" | "server_error" | "junk" | "timeout" | "network_error";
633
879
  /** One probe, for debugging display: the probed URL derives from baseUrl only (never contains the key). */
634
880
  interface ModelProtocolProbeDto {
635
- /** AgentHub client type this probe stands for (`openai-responses` / `ant-messages` / `openai-chat`). */
881
+ /** MMSP client type this probe stands for (`openai-responses` / `ant-messages` / `openai-chat`). */
636
882
  clientType: string;
637
883
  /** Full URL probed (base URL + the protocol's path). */
638
884
  url: string;
@@ -646,7 +892,7 @@ interface ModelProtocolProbeDto {
646
892
  * ones actually run, in order.
647
893
  */
648
894
  interface ModelProtocolDetectResponse {
649
- /** The first protocol an endpoint serves (an AgentHub client type); absent when none of the three matched. */
895
+ /** The first protocol an endpoint serves (an MMSP client type); absent when none of the three matched. */
650
896
  detected?: string;
651
897
  /**
652
898
  * The base URL the detected protocol answered at — the typed URL normalized (endpoint
@@ -660,14 +906,14 @@ interface ModelProtocolDetectResponse {
660
906
  }
661
907
  /**
662
908
  * Endpoint model listing (POST /api/projects/:p/models/list, owner): given a base URL and
663
- * the protocol `/detect` reported, returns every model id the endpoint serves (AgentHub's
909
+ * the protocol `/detect` reported, returns every model id the endpoint serves (MMSP's
664
910
  * `listModels()` on the routed client). Used by the add-group dialog to import a provider's
665
911
  * whole listing in one go; the ids come back in the endpoint's own order.
666
912
  */
667
913
  interface EndpointModelListRequest {
668
914
  /** Endpoint base URL (as typed in the add-group dialog). */
669
915
  baseUrl: string;
670
- /** AgentHub client type to speak (normally a detected generic protocol; whole-endpoint listings need one). */
916
+ /** MMSP client type to speak (normally a detected generic protocol; whole-endpoint listings need one). */
671
917
  clientType: string;
672
918
  /** Newly entered API key (plaintext); omitted = the SDK's environment fallback for the protocol. */
673
919
  apiKey?: string;
@@ -677,7 +923,7 @@ interface EndpointModelListResponse {
677
923
  ok: boolean;
678
924
  /** The model ids the endpoint serves, in the order the endpoint returned them (ok only). */
679
925
  models?: string[];
680
- /** The routed client has no models endpoint (AgentHub UnsupportedOperationError) — callers offer the manual path. */
926
+ /** The routed client has no models endpoint (MMSP UnsupportedOperationError) — callers offer the manual path. */
681
927
  unsupported?: boolean;
682
928
  message?: string;
683
929
  }
@@ -695,6 +941,54 @@ interface DefaultModelUpdateRequest {
695
941
  interface DefaultModelResponse {
696
942
  defaultModel: ModelRefDto;
697
943
  }
944
+ /** One balance in one currency. */
945
+ interface ModelBalanceAmount {
946
+ /** Decimal string in `currency`'s major unit, as the vendor states it (TokenDance's micro-yuan are converted to yuan). */
947
+ amount: string;
948
+ /** ISO 4217 code: `CNY`, `USD`, … */
949
+ currency: string;
950
+ }
951
+ /**
952
+ * Why a group's balance could not be read:
953
+ * - `unsupported`: the group publishes no balance endpoint (no `balance` descriptor in the catalog);
954
+ * - `no_key`: the group has no API key to ask with — none stored, and no environment variable
955
+ * the credential rule lends its rows for the balance endpoint's host;
956
+ * - `upstream_failed`: the vendor could not be reached in time, refused the request, or answered
957
+ * without a readable balance.
958
+ */
959
+ type ModelBalanceErrorCode = "unsupported" | "no_key" | "upstream_failed";
960
+ /**
961
+ * GET /api/projects/:p/models/balance?provider=<group>[&force=1] (Project member): the account
962
+ * balance behind the group's API key — the stored one, or else the environment key its rows
963
+ * fall back to under the credential rule (DeepSeek's DEEPSEEK_API_KEY) — read server-side from
964
+ * the endpoint the catalog's `balance` descriptor names. The key never reaches the browser, and
965
+ * neither does the vendor's own text. Like the connectivity test, a balance that cannot be read is an answer
966
+ * (`ok: false` with a code), not a failed request. Readings and vendor failures are cached for
967
+ * 60 s per Project and group, for as long as the group's key is the same; `force=1` skips the
968
+ * cache and refreshes it.
969
+ */
970
+ type ModelBalanceResponse = ModelBalanceReading | ModelBalanceFailure;
971
+ interface ModelBalanceReading extends ModelBalanceAmount {
972
+ ok: true;
973
+ provider: string;
974
+ /** Whether the account can make requests right now (DeepSeek's `is_available`); absent when the vendor does not say. */
975
+ available?: boolean;
976
+ /** Further currencies the account holds, after the one in `amount` / `currency`; absent when there is only one. */
977
+ others?: ModelBalanceAmount[];
978
+ /** When the server read it from the vendor (ISO 8601); a cached answer keeps its original time. */
979
+ fetchedAt: string;
980
+ }
981
+ interface ModelBalanceFailure {
982
+ ok: false;
983
+ provider: string;
984
+ error: ModelBalanceErrorCode;
985
+ /** The vendor's HTTP status, when it answered with an error. */
986
+ status?: number;
987
+ /** English detail for logs and bug reports; the Web App words each code itself. */
988
+ message: string;
989
+ /** When the answer was settled (ISO 8601). */
990
+ fetchedAt: string;
991
+ }
698
992
  /**
699
993
  * How the authorization code travels back. `callback` sends the browser to a harness URL
700
994
  * the provider redirects to; `manual` asks the provider for a one-time code the user copies
@@ -749,6 +1043,33 @@ interface ModelOAuthCodeResponse {
749
1043
  applied?: number;
750
1044
  error?: ModelOAuthErrorCode;
751
1045
  }
1046
+ interface PlatformAuthStartResponse {
1047
+ flowId: string;
1048
+ authorizeUrl: string;
1049
+ expiresAt: string;
1050
+ }
1051
+ type PlatformAuthFlowErrorCode = "unreachable" | "upstream_failed" | "invalid_key" | "expired" | "locked" | "already_delivered" | "apply_failed";
1052
+ interface PlatformAuthFlowStatusResponse {
1053
+ status: "pending" | "applying" | "completed" | "cancelled" | "apply_failed" | "error";
1054
+ error?: PlatformAuthFlowErrorCode;
1055
+ applied?: number;
1056
+ }
1057
+ /** Result of refreshing Penguin Go's catalog with the Project's stored platform key. */
1058
+ interface PlatformModelSyncResponse extends ModelsResponse {
1059
+ added: number;
1060
+ updated: number;
1061
+ }
1062
+ /**
1063
+ * The wire shapes are Penguin Go's, deliberately: both flows create a request, poll it, and
1064
+ * report the same six states and the same seven failure codes, so the App drives both with one
1065
+ * dialog. They are aliases rather than a second declaration because a copy would be the same
1066
+ * fields with the same meaning, and a shape that drifted apart in one of the two would be a
1067
+ * bug rather than a distinction. The endpoints stay separate regardless — a flow id issued by
1068
+ * one is not accepted by the other.
1069
+ */
1070
+ type ModelScopeAuthStartResponse = PlatformAuthStartResponse;
1071
+ type ModelScopeAuthFlowErrorCode = PlatformAuthFlowErrorCode;
1072
+ type ModelScopeAuthFlowStatusResponse = PlatformAuthFlowStatusResponse;
752
1073
  /**
753
1074
  * Per-Project new-chat defaults: prefill for the chat draft page. Every key is optional —
754
1075
  * an absent key means "not set" (the pre-existing behavior). Serves as the GET response,
@@ -770,6 +1091,11 @@ interface ChatDefaultsDto {
770
1091
  * "none" — only the selectable tiers.
771
1092
  */
772
1093
  thinkingLevel?: Exclude<ThinkingLevelName, "none">;
1094
+ /**
1095
+ * Read-only, GET only: the sandbox policy a new Session starts with — the server's Sandbox
1096
+ * settings. Not part of the Project's block; PUT ignores it.
1097
+ */
1098
+ sandbox?: SessionSandbox;
773
1099
  }
774
1100
  /**
775
1101
  * One deny rule of the sandbox command policy — plain project-editable data. The factory
@@ -820,11 +1146,15 @@ interface VaultEntryUpdate {
820
1146
  interface VaultUpdateRequest {
821
1147
  entries: VaultEntryUpdate[];
822
1148
  }
823
- /** One installed plugin (a skill or a hook package) the library carries a higher version of (see {@link AgentSummary.pluginUpdates}). */
1149
+ /** One library plugin an Agent's installed copy is behind on — one of its skills or its hook package carries an older dated version than the library's same part (see {@link AgentSummary.pluginUpdates}). */
824
1150
  interface PluginUpdateRef {
825
1151
  /** Plugin name — what `POST …/plugins` reinstalls to bring the Agent up to date. */
826
1152
  name: string;
827
- /** The LIBRARY's version (`YYYY.MM.DD.N`), i.e. what installing again would bring, not what is on disk. */
1153
+ /**
1154
+ * The LIBRARY's content version: the newest dated version (`YYYY.MM.DD.N`) among the plugin's
1155
+ * parts, i.e. the date of what installing again would bring, not what is on disk. A label —
1156
+ * it moves whenever any part changes, so a dismissed update comes back for the next one.
1157
+ */
828
1158
  version: string;
829
1159
  }
830
1160
  interface AgentSummary {
@@ -850,21 +1180,43 @@ interface AgentSummary {
850
1180
  vaultKeyCount: number;
851
1181
  /** Schedule count (number of .toml files under agent_state/schedule/, including invalid ones). */
852
1182
  scheduleCount: number;
1183
+ /** Workbench folders with a manifest, including ones with a load error. */
1184
+ workbenchCount: number;
853
1185
  /** Installed Skill count (number of agent_state/skills/<name>/ directories with a SKILL.md). */
854
1186
  skillCount: number;
855
1187
  /** Installed hook-package count (number of agent_state/hooks/<name>/ directories with a hooks.json). */
856
1188
  hookCount: number;
857
1189
  /**
858
1190
  * Installed plugins the built-in library has moved past — a skill or a hook package whose
859
- * on-disk version is behind the library plugin that ships it — each with the library version
860
- * on offer: the plugin-library update gate, riding along on the Agent list so a badge over
861
- * that page costs no request of its own. Empty when nothing is behind. Skills the library
862
- * does not carry (installed from a zip or a picked directory) are never listed: there is no
863
- * library version for them to be behind.
1191
+ * on-disk dated version is behind the library's same part (compared part by part, never
1192
+ * against the plugin's npm version) — each with the library's content version on offer: the
1193
+ * plugin-library update gate, riding along on the Agent list so a badge over that page costs
1194
+ * no request of its own. Empty when nothing is behind. Skills the library does not carry
1195
+ * (installed from a zip or a picked directory) are never listed: there is no library version
1196
+ * for them to be behind.
864
1197
  */
865
1198
  pluginUpdates: PluginUpdateRef[];
866
1199
  /** Memory count (topic files summed over the scope directories under agent_state/memory/, independent of the memory switch). */
867
1200
  memoryCount: number;
1201
+ /** Whether the Agent's API tab switch is on (this server's web.db, see AgentApiSettings.enabled): the list card's API mark. */
1202
+ apiEnabled: boolean;
1203
+ /**
1204
+ * The organizations of this Project that employ the Agent, one entry per organization:
1205
+ * every organization directory the Project holds, paused ones included, plus the CEO id of
1206
+ * an organization whose chart does not parse. Absent or empty = not an employee. An
1207
+ * employed Agent cannot be deleted (`DELETE` answers 409 `agent_employed`) until it leaves
1208
+ * every one of them; development mode lists employees in a section of their own.
1209
+ */
1210
+ employments?: Employment[];
1211
+ }
1212
+ /** One organization employing an Agent, as the Agent list carries it (see {@link AgentSummary.employments}). */
1213
+ interface Employment {
1214
+ orgId: string;
1215
+ /** The organization's display name (`org_config.toml`), the id when the config does not parse. */
1216
+ orgName: string;
1217
+ /** The Agent's title in that organization's chart; "CEO" for the CEO of a chart that does not parse. */
1218
+ title: string;
1219
+ status: OrgStatus;
868
1220
  }
869
1221
  interface AgentsResponse {
870
1222
  agents: AgentSummary[];
@@ -1012,13 +1364,20 @@ interface AgentKernelUpdateResponse {
1012
1364
  /** The kernel stamp written (the current defaults generation). */
1013
1365
  kernelVersion: string;
1014
1366
  }
1015
- /** POST …/config/mcp-test result: reachability of one MCP Server entry. */
1367
+ /**
1368
+ * POST …/config/mcp-test result: reachability of one MCP Server entry. Its `${KEY}` references
1369
+ * are filled in from the Agent's vault only when the entry is saved on the Agent exactly as
1370
+ * tested (same name, same config — the user's rule); a new or changed entry that references the
1371
+ * vault is not filled in and not contacted (`saveFirst`).
1372
+ */
1016
1373
  interface McpServerTestResponse {
1017
1374
  ok: boolean;
1018
1375
  /** Discovered tool names (`mcp__<server>__<tool>`), present on success. */
1019
1376
  tools?: string[];
1020
- /** Failure detail (connect error, timeout, server stderr tail), present on failure. */
1377
+ /** Failure detail (connect error, timeout, server stderr tail; "needs setup: …" naming a vault key a saved entry references and the vault lacks, with nothing contacted), present on failure. */
1021
1378
  error?: string;
1379
+ /** The entry references the vault and is not saved as it is: nothing was filled in or contacted — save it, then test (`error` says so in English). */
1380
+ saveFirst?: boolean;
1022
1381
  /** Connect + discovery wall time (both outcomes) — the models test reports latency, this matches. */
1023
1382
  latencyMs?: number;
1024
1383
  }
@@ -1054,6 +1413,73 @@ interface AgentConfigUpdateRequest {
1054
1413
  mcpServers?: MCPServerConfig[];
1055
1414
  };
1056
1415
  }
1416
+ /** One API key of an Agent, as listed: the secret itself is shown once, at creation, and never again. */
1417
+ interface AgentApiKeyInfo {
1418
+ keyId: string;
1419
+ /** 1-64 characters, given by whoever created it. */
1420
+ name: string;
1421
+ /** The key's first 16 characters (`penguin_` + 8), enough to tell keys apart. */
1422
+ prefix: string;
1423
+ /** user_id of the Project owner who created it. */
1424
+ createdBy: string;
1425
+ createdAt: string;
1426
+ /** Last run request that authenticated with it; null = never used. */
1427
+ lastUsedAt: string | null;
1428
+ }
1429
+ /**
1430
+ * One Agent's public API settings — this server's, kept in web.db, never in the Agent State or
1431
+ * the Project file, so the Agent cannot expose itself or loosen its own approvals. An Agent
1432
+ * never configured reads as disabled, keyed, allow-all, with no keys.
1433
+ */
1434
+ interface AgentApiSettings {
1435
+ /** The API tab's switch; off = every AMSP request for this Agent is 404 `agent_not_found`. */
1436
+ enabled: boolean;
1437
+ /** Keyless access: a request without `Authorization` is accepted; off = 401 `unauthorized`. */
1438
+ open: boolean;
1439
+ /**
1440
+ * The approval mode an API Session is created with. It is copied onto each API Session when
1441
+ * that Session is created, and the Session's own mode is what every decision reads, so a
1442
+ * change here applies to API Sessions created afterwards.
1443
+ */
1444
+ approvalMode: ApprovalMode;
1445
+ /** Null follows the Project's default model. Applied only to new API Sessions. */
1446
+ model: ModelRefDto | null;
1447
+ /** Null follows the Agent/Project thinking configuration. */
1448
+ thinkingLevel: ThinkingLevelName | null;
1449
+ /** Null creates a temporary Workspace for each new API Session. */
1450
+ workspace: string | null;
1451
+ /** Positive integer. Checked for each API run; defaults to four concurrent Agent sessions. */
1452
+ maxConcurrentRuns: number;
1453
+ keys: AgentApiKeyInfo[];
1454
+ }
1455
+ interface AgentApiResponse {
1456
+ api: AgentApiSettings;
1457
+ /**
1458
+ * The admin's server-wide switch (`ServerSettings.agentApiEnabled`), which only an admin can
1459
+ * read through /api/admin/settings: off, every Agent's API is refused (403) whatever `api` says.
1460
+ */
1461
+ serverEnabled: boolean;
1462
+ }
1463
+ /** PUT body (owner only): every field optional, omitted fields keep their current value. */
1464
+ interface AgentApiUpdateRequest {
1465
+ enabled?: boolean;
1466
+ open?: boolean;
1467
+ approvalMode?: ApprovalMode;
1468
+ model?: ModelRefDto | null;
1469
+ thinkingLevel?: ThinkingLevelName | null;
1470
+ workspace?: string | null;
1471
+ maxConcurrentRuns?: number;
1472
+ }
1473
+ /** POST …/api/keys body (owner only). */
1474
+ interface AgentApiKeyCreateRequest {
1475
+ /** 1-64 characters. */
1476
+ name: string;
1477
+ }
1478
+ /** The one response that carries the secret (`penguin_` + 43 base64url characters); only its hash is stored. */
1479
+ interface AgentApiKeyCreateResponse {
1480
+ key: AgentApiKeyInfo;
1481
+ secret: string;
1482
+ }
1057
1483
  /** One Memory scope directory: `agent_state/memory/user/` or `agent_state/memory/<workspaceKey>/`. */
1058
1484
  interface MemoryScopeInfo {
1059
1485
  /** Directory name under `memory/`: `user`, or a Workspace's `<safe-basename>-<hash>` key. */
@@ -1163,16 +1589,119 @@ interface MemoryImportResponse {
1163
1589
  /** Whether the scope's `MEMORY.md` was written or extended. */
1164
1590
  indexWritten: boolean;
1165
1591
  }
1592
+ /** How far a Session's commands may reach the filesystem (the sandbox's confinement mode). */
1593
+ type SessionSandboxMode = "read-only" | "workspace-write" | "danger-full-access";
1594
+ /**
1595
+ * The part of a Session's sandbox policy a person picks from the composer: the filesystem
1596
+ * mode and the network level. The Session keeps its own copy — taken from the server's
1597
+ * Sandbox settings when it was created — so editing those settings only changes what NEW
1598
+ * Sessions start with.
1599
+ */
1600
+ interface SessionSandbox {
1601
+ mode: SessionSandboxMode;
1602
+ /** `open` = unrestricted, `local` = only the host's localhost, `none` = no network. */
1603
+ network: SessionSandboxNetwork;
1604
+ /**
1605
+ * Response only, ignored in requests: whether a sandbox backend on this server can enforce
1606
+ * the `read-only` and `workspace-write` modes. False on a deployment with no backend
1607
+ * installed, where either mode would refuse every command: the composer shows them greyed
1608
+ * out, saying no backend is installed.
1609
+ */
1610
+ confinementSupported?: boolean;
1611
+ /** Response only, ignored in requests: the same for the `none` network level. */
1612
+ noNetworkSupported?: boolean;
1613
+ /**
1614
+ * Response only, ignored in requests: whether a sandbox backend on this server can enforce
1615
+ * the `local` level. When false the composer shows it greyed out, and picking it is refused
1616
+ * (400 `sandbox_unsupported`).
1617
+ */
1618
+ localNetworkSupported?: boolean;
1619
+ /** Response only, ignored in requests: whether a backend here can enforce `masksPaths`. */
1620
+ maskPathsSupported?: boolean;
1621
+ /**
1622
+ * Response only, ignored in requests: true when this policy hides paths (the Sandbox card's
1623
+ * masked paths, kept by every pick). Where `maskPathsSupported` is false, every level then
1624
+ * refuses every command, and the composer says so.
1625
+ */
1626
+ masksPaths?: boolean;
1627
+ /**
1628
+ * Response only, ignored in requests: the names of the sandbox backends in use here, which the
1629
+ * composer names when a level is beyond what they enforce. Absent with none in use.
1630
+ */
1631
+ backendsInUse?: string[];
1632
+ /**
1633
+ * Response only, ignored in requests: the sandbox backends that are enabled here but failed
1634
+ * to load or failed their check (a WSL distro not set up, a wrong program path), each with
1635
+ * why. A backend for another platform is not listed. With none mounted and one listed, the
1636
+ * composer marks the confining levels unavailable and gives the first one's reason.
1637
+ */
1638
+ unavailableBackends?: UnavailableSandboxBackend[];
1639
+ /**
1640
+ * Response only, ignored in requests: the server's sandbox presets (the Sandbox card's table),
1641
+ * in table order, disabled rows included. The composer lists the enabled ones and names the
1642
+ * Session's level by the first row matching its mode, network and approval mode. A server
1643
+ * that does not report it gets the composer's built-in table.
1644
+ */
1645
+ presets?: SessionSandboxPreset[];
1646
+ /**
1647
+ * Response only, ignored in requests: true when this Session's policy holds something the
1648
+ * presets do not show — masked paths, or the temp directory not writable.
1649
+ */
1650
+ advanced?: boolean;
1651
+ /**
1652
+ * Response only, chat defaults only: the approval mode a new Session starts with when its
1653
+ * request names none — the Sandbox card's default preset's, while the switch is on. Absent:
1654
+ * the server's own fallback (`allow-all`).
1655
+ */
1656
+ defaultApprovalMode?: ApprovalMode;
1657
+ /**
1658
+ * Response only, ignored in requests: the Sandbox card's switch on this server — whether new
1659
+ * Sessions start confined. Off, the composer offers the approval modes alone (a Session keeps
1660
+ * its own policy either way). A server that does not report it is read as on.
1661
+ */
1662
+ switchOn?: boolean;
1663
+ }
1664
+ /** One sandbox preset: a named mode, network level and approval mode. */
1665
+ interface SessionSandboxPreset {
1666
+ id: string;
1667
+ /** The name an administrator gave it, or its declared English name. */
1668
+ name: string;
1669
+ /** Its declared Chinese name, while it has not been renamed. */
1670
+ nameZh?: string;
1671
+ /** Whether the composer's menu lists it. */
1672
+ enabled: boolean;
1673
+ mode: SessionSandboxMode;
1674
+ network: SessionSandboxNetwork;
1675
+ approvalMode: ApprovalMode;
1676
+ /**
1677
+ * Response-only: its file mode or network is wider than the server's sandbox settings (the
1678
+ * ceiling for non-admins), so a non-admin's pick of it is refused with `403 sandbox_forbidden`.
1679
+ * The same comparison as that refusal; absent when it is within them.
1680
+ */
1681
+ aboveCeiling?: true;
1682
+ }
1683
+ /** An enabled sandbox backend that is not in use on this server, and why. */
1684
+ interface UnavailableSandboxBackend {
1685
+ name: string;
1686
+ reason: string;
1687
+ }
1688
+ /** The network levels, widest first. */
1689
+ type SessionSandboxNetwork = "open" | "local" | "none";
1166
1690
  interface SessionInfo {
1167
1691
  sessionId: string;
1168
1692
  projectId: string;
1169
1693
  agentId: string;
1170
- /** Provider group of the session's model (paired with `modelId` to form a model reference). */
1694
+ /**
1695
+ * Provider group of the session's **current** model (paired with `modelId` to form a model
1696
+ * reference). It moves with each in-session switch (`POST /api/sessions/:id/switch-model`).
1697
+ */
1171
1698
  provider: string;
1172
- /** Upstream model_id of the session's model (the request id sent to AgentHub). */
1699
+ /** Upstream model_id of the session's current model (the request id sent to MMSP). */
1173
1700
  modelId: string;
1174
1701
  workspace: string;
1175
1702
  approvalMode: ApprovalMode;
1703
+ /** The Session's own sandbox policy (see {@link SessionSandbox}). */
1704
+ sandbox: SessionSandbox;
1176
1705
  /**
1177
1706
  * Thinking level pinned for this Session (set via PATCH; the Web App's in-chat picker).
1178
1707
  * Unset = never pinned: each model context the Session opens reads the Agent config's
@@ -1185,7 +1714,18 @@ interface SessionInfo {
1185
1714
  thinkingLevel?: ThinkingLevelName;
1186
1715
  /** Short title auto-generated by the model after the first turn; unset until generated (frontend shows "New Chat"). */
1187
1716
  title?: string;
1188
- /** Session source (for list badges/folders), derived from core session_meta — the single source of truth (not stored in the DB); unset for user-created sessions. */
1717
+ /**
1718
+ * What kind of conversation this is, read from core session_meta — the single source of
1719
+ * truth (the DB stores no source). With the user's marks it decides where the Session is
1720
+ * listed (`archived`): a `user` Session is in the sidebar until archived; an `api`,
1721
+ * `schedule`, `cli` or `workbench` Session is archived until the user keeps it, and in the sidebar marked
1722
+ * with its source once kept; a `subagent` or `company` Session is in no list — its parent's
1723
+ * Agent panel and company mode's own views open them. A Trace head that records no source
1724
+ * (written before it was required) reads as `company` for a row the organization runtime
1725
+ * opened (`client: "org"`), else as `user`; so does such a row before its first run. Absent
1726
+ * only while any other row is not yet classified (its Trace head has not been read yet),
1727
+ * which renders as `user`.
1728
+ */
1189
1729
  source?: SessionSource;
1190
1730
  createdAt: string;
1191
1731
  /**
@@ -1211,7 +1751,13 @@ interface SessionInfo {
1211
1751
  pendingFollowUpCount: number;
1212
1752
  /** Whether a Trace record exists (a Task has been started). */
1213
1753
  hasTrace: boolean;
1214
- /** Whether archived (hidden from the default list, grouped under "Archived"). */
1754
+ /**
1755
+ * Whether the Session is archived — the listing rule's answer, never the bare mark: the user
1756
+ * archived it, or a program opened it (source `api`, `schedule`, `cli` or `workbench`) and the user has
1757
+ * not kept it. An archived Session is left out of the sidebar and listed on the archive page
1758
+ * (`/api/me/archived-sessions`). Always false for a `subagent` or `company` Session, which is
1759
+ * in no list.
1760
+ */
1215
1761
  archived: boolean;
1216
1762
  /**
1217
1763
  * Absolute path of the session's latest Trace file (the current context shard); absent
@@ -1226,22 +1772,23 @@ interface SessionInfo {
1226
1772
  /**
1227
1773
  * Company mode: the organization that owns this Session — a desk session of one of its
1228
1774
  * employees, or a session contributing to one of its tickets — read from the organization
1229
- * caches. Absent for every ordinary Session. Development mode's list and its time buckets
1230
- * hide every row that carries it (only while company mode is available to that user: it is
1231
- * stamped either way, and company mode is what lists these Sessions instead), and the
1232
- * company sidebar's 工位 / 工单会话 groups are where they are listed.
1775
+ * caches. Absent for every ordinary Session. Development mode's list asks the server to
1776
+ * leave every such row out (`excludeOrg=1` on the list route — page and totals alike) and
1777
+ * still drops one that reaches it by another door; the company sidebar's 工位 group and the
1778
+ * tickets are where these Sessions are reached instead.
1233
1779
  */
1234
1780
  orgId?: string;
1235
1781
  /**
1236
1782
  * Which client opened the Session, as stored on the index row: "cli" from the CLI (a
1237
1783
  * Session adopted from a legacy CLI-direct Trace included), "org" from the organization
1238
- * runtime (a desk or a ticket session), "web" otherwise. Absent only on a row that
1239
- * predates the column, which reads as "web". Unlike {@link SessionInfo.orgId} — projected
1240
- * from the organization caches, so it disappears with the organization and is not read
1241
- * while company mode is off — this is a durable stamp on the row: development mode's list
1242
- * hides an "org" Session either way.
1784
+ * runtime (a desk or a ticket session, and every sub-session one of those spawns), "api"
1785
+ * from an Agent API run (the only Sessions the API may continue), "web" otherwise. Absent
1786
+ * only on a row that predates the column, which reads as "web". Unlike
1787
+ * {@link SessionInfo.orgId} — projected from the organization caches, so it disappears with
1788
+ * the organization and is not read while company mode is off — this is a durable stamp on
1789
+ * the row: development mode's list hides an "org" Session either way.
1243
1790
  */
1244
- client?: "web" | "cli" | "org";
1791
+ client?: "web" | "cli" | "org" | "api" | "workbench";
1245
1792
  /**
1246
1793
  * Background work the Session's loaded runtime still owns: command sessions running past
1247
1794
  * their yield window (`exec_command` promotions and `run_in_background` launches) and
@@ -1261,45 +1808,241 @@ interface SessionBackgroundTasks {
1261
1808
  subagents: number;
1262
1809
  }
1263
1810
  /**
1264
- * Session list category, the sidebar's five-way split applied server-side: archived wins
1265
- * regardless of origin (archiving is an explicit user action), then the origin's bucket,
1266
- * and a Session with no (or an unknown) source is `active` — user-created rows.
1811
+ * Session list category, the listing rule applied server-side: `archived` when the user
1812
+ * archived the Session, or when a program opened it (source `api`, `schedule`, `cli` or `workbench`) and the
1813
+ * user has not kept it — unarchiving keeps it; otherwise `active`. A `subagent` or `company`
1814
+ * Session belongs to neither, whatever its marks: the list leaves it out of every category and
1815
+ * every total (a parent's Agent panel and company mode's own views open them).
1267
1816
  */
1268
- type SessionCategory = "active" | SessionSource | "archived";
1817
+ type SessionCategory = "active" | "archived";
1269
1818
  /** Per-category totals across an Agent's whole Session list (returned when the list is requested with counts). */
1270
1819
  type SessionCategoryCounts = Record<SessionCategory, number>;
1820
+ /**
1821
+ * GET /api/projects/:projectId/agents/:agentId/sessions. Every query parameter is optional:
1822
+ *
1823
+ * - `order`: `created` (the default) lists newest creation first; `activity` lists most recent
1824
+ * `lastActiveAt` first, ties broken by `sessionId` descending, both compared as plain strings
1825
+ * (code points, never locale collation) so a client can compute the same order.
1826
+ * - `limit` (1–1000) with `offset` (≥ 0): an offset page, in either order. Offsets suit only the
1827
+ * `created` order, which activity never reshuffles.
1828
+ * - `before=<lastActiveAt>,<sessionId>` with `limit`: under `order=activity` only, the rows
1829
+ * strictly below that cursor — the key of the last row the client holds. A row that becomes
1830
+ * active between pages moves above the cursor and is not served again, and no other row is
1831
+ * skipped (`session_state` tells the client about the moved one). 400 without
1832
+ * `order=activity`, beside `offset`, without `limit`, or when not a parseable stamp and a
1833
+ * valid id split at the first comma.
1834
+ * - `category`, `workspaceGroup`, `excludeOrg=1` filter before paging; `counts=1` adds the
1835
+ * whole-list totals below, which no cursor or offset narrows. A request with `category`,
1836
+ * `workspaceGroup` or `counts=1` leaves every `subagent` and `company` Session out of the page
1837
+ * and the totals (they are in no category); one with none of them serves every row.
1838
+ */
1271
1839
  interface SessionsResponse {
1840
+ /**
1841
+ * The page. With `excludeOrg=1` on the request, the rows an organization owns — its desk
1842
+ * and ticket sessions and the sub-sessions they spawned — are left out of it and of every
1843
+ * total below, so a caller drawing the user's own conversations is never handed a total
1844
+ * for rows it will not be handed. Without the flag every row is served, whichever client
1845
+ * created it.
1846
+ */
1272
1847
  sessions: SessionInfo[];
1273
1848
  /** Present when the request asked for counts (`counts=1`): totals per category over the full list, not just the returned page. */
1274
1849
  counts?: SessionCategoryCounts;
1275
1850
  /**
1276
1851
  * Present with `counts`: the same totals broken down by Workspace path (only paths
1277
1852
  * with at least one Session appear). The sidebar's workspace grouping decides each
1278
- * group's folders and "More" from its own share, so a group never advertises
1853
+ * group's existence and "More" from its own active share, so a group never advertises
1279
1854
  * content that lives in other Workspaces.
1280
1855
  */
1281
1856
  workspaceCounts?: Record<string, SessionCategoryCounts>;
1282
1857
  /**
1283
1858
  * Present with `counts`: each Workspace path's newest Session (any category), as its
1284
- * `createdAt`. With `workspaceCounts` this is what lets the sidebar list every Workspace
1285
- * that holds Sessions and place the groups by recency before — or without — loading any
1286
- * of their rows.
1859
+ * `createdAt` — the Workspace picker's "recently used", which counts every Session that
1860
+ * worked there.
1287
1861
  */
1288
1862
  workspaceLatest?: Record<string, string>;
1863
+ /**
1864
+ * Present with `counts`: each Workspace path's newest `active` Session, as its `createdAt`
1865
+ * (a path holding none is absent). With `workspaceCounts` this is what lets the sidebar list
1866
+ * every Workspace that holds conversations and place the groups by recency before — or
1867
+ * without — loading any of their rows; an archived Session does not move a group it is not
1868
+ * shown in.
1869
+ */
1870
+ workspaceLatestActive?: Record<string, string>;
1871
+ }
1872
+ /**
1873
+ * The archive page's source filter: `user` is a conversation the user archived by hand (source
1874
+ * `user`, or a row whose Trace has not been read yet); the other three are what a program
1875
+ * opened. `subagent` and `company` Sessions are never archived, so never listed.
1876
+ */
1877
+ type ArchivedSessionSource = "user" | "api" | "schedule" | "cli" | "workbench";
1878
+ /** Query of GET /api/me/archived-sessions/groups, and the part every archive query shares. */
1879
+ interface ArchivedSessionsQuery {
1880
+ /** Case-insensitive substring of the title (the DB title, else the Trace's first-prompt title); at most 200 characters. */
1881
+ q?: string;
1882
+ source?: ArchivedSessionSource;
1883
+ /** One Project the user can open (404 `project_not_found` otherwise); absent = every Project they own or are a member of. */
1884
+ projectId?: string;
1885
+ }
1886
+ /** One group of the archive page: one Workspace of one Project. */
1887
+ interface ArchivedSessionGroup {
1888
+ projectId: string;
1889
+ projectName: string;
1890
+ /** The Workspace path, as the sidebar groups it; `temp` for the merged temporary Workspaces. */
1891
+ workspace: string;
1892
+ /** Archived Sessions in the group matching the query. */
1893
+ count: number;
1894
+ /** The newest `lastActiveAt` in the group — the order the groups come in, newest first. */
1895
+ latest: string;
1896
+ }
1897
+ /** GET /api/me/archived-sessions/groups. */
1898
+ interface ArchivedSessionGroupsResponse {
1899
+ groups: ArchivedSessionGroup[];
1900
+ /** Sum of the groups' counts. */
1901
+ total: number;
1902
+ /**
1903
+ * The server's time when these counts were taken (ISO). A group's batch passes it as its
1904
+ * filter's `until`, so it acts on the Sessions the count saw and on none that came after.
1905
+ */
1906
+ asOf: string;
1907
+ }
1908
+ /** Query of GET /api/me/archived-sessions: one group's page, newest activity first. */
1909
+ interface ArchivedSessionsRowsQuery extends ArchivedSessionsQuery {
1910
+ projectId: string;
1911
+ /** The group's `workspace` (a path, or `temp`). */
1912
+ workspace: string;
1913
+ /** 1–100. */
1914
+ limit: number;
1915
+ /** `<lastActiveAt>,<sessionId>` of the last row held, as the Session list's cursor. */
1916
+ before?: string;
1917
+ }
1918
+ /** GET /api/me/archived-sessions. */
1919
+ interface ArchivedSessionsResponse {
1920
+ /** `title` falls back to the Trace's first-prompt title when the row has none. */
1921
+ sessions: SessionInfo[];
1922
+ hasMore: boolean;
1923
+ }
1924
+ /** What an archive-page batch acts on: explicit ids (1–500), or every archived row a filter matches. */
1925
+ type ArchivedSessionsBatch = {
1926
+ sessionIds: string[];
1927
+ } | {
1928
+ filter: ArchivedSessionsBatchFilter;
1929
+ };
1930
+ /** A batch's filter: the archive query, one group's Workspace, and the snapshot it was counted at. */
1931
+ interface ArchivedSessionsBatchFilter extends ArchivedSessionsQuery {
1932
+ workspace?: string;
1933
+ /**
1934
+ * The groups response's `asOf` the user acted on (ISO): only Sessions archived by then and
1935
+ * not active since are acted on, so one archived or run after the count is left alone.
1936
+ */
1937
+ until?: string;
1938
+ /** Sessions left out although the filter matches them — rows unticked after Select all (1–500). */
1939
+ exclude?: string[];
1940
+ }
1941
+ /** POST /api/me/archived-sessions/unarchive and /delete. */
1942
+ interface ArchivedSessionsBatchResponse {
1943
+ /**
1944
+ * Sessions acted on. Unarchive counts only the ones that were archived. Delete counts every
1945
+ * named one; a filter's delete leaves a running Session alone (it is not aborted) and does not
1946
+ * count it.
1947
+ */
1948
+ done: number;
1949
+ /** Ids that name nothing the user can open (the ids form only; a filter never reports any). */
1950
+ missing: string[];
1951
+ }
1952
+ /**
1953
+ * One page request inside POST /api/projects/:p/sessions/batch — the same knobs the
1954
+ * Agent-level list takes, carried per entry so one call can stand in for a whole sidebar
1955
+ * reload (one first page per Agent, plus every other page pair already open).
1956
+ */
1957
+ interface SessionBatchPageRequest {
1958
+ agentId: string;
1959
+ /** Rows per page, 1–1000. */
1960
+ limit: number;
1961
+ /** `activity`: last activity first — the sidebar's order. Omitted: creation order. */
1962
+ order?: "created" | "activity";
1963
+ /** Rows to skip from the top (default 0). Not beside `before`. */
1964
+ offset?: number;
1965
+ /**
1966
+ * Activity order only: rows strictly below this cursor — the last row of the previous page
1967
+ * (`lastActiveAt` + `sessionId`, the pair the list endpoint's `before` param carries).
1968
+ */
1969
+ before?: {
1970
+ lastActiveAt: string;
1971
+ sessionId: string;
1972
+ };
1973
+ category?: SessionCategory;
1974
+ workspaceGroup?: string;
1975
+ /**
1976
+ * Totals per category over the Agent's whole list, with the per-Workspace counts and newest
1977
+ * stamps beside them (`counts=1` on the Agent-level list).
1978
+ */
1979
+ withCounts?: boolean;
1980
+ /** The user's own rows only: leave an organization's own sessions out of page and totals. */
1981
+ excludeOrg?: boolean;
1982
+ }
1983
+ /**
1984
+ * One entry's answer. `ok: false` is a real answer about THAT Agent, never about the batch:
1985
+ * `absent` is an Agent this server does not host (its rows come from the machine that does),
1986
+ * `error` is a failure to answer at all — which the caller must not read as "no rows".
1987
+ */
1988
+ type SessionBatchResult = ({
1989
+ agentId: string;
1990
+ ok: true;
1991
+ } & SessionsResponse) | {
1992
+ agentId: string;
1993
+ ok: false;
1994
+ reason: "absent" | "error";
1995
+ };
1996
+ interface SessionsBatchRequest {
1997
+ requests: SessionBatchPageRequest[];
1998
+ }
1999
+ interface SessionsBatchResponse {
2000
+ /** One entry per requested page, in request order (repeated Agent ids stay repeated). */
2001
+ results: SessionBatchResult[];
1289
2002
  }
1290
- /** Server directory browsing (advanced new-Workspace picker): starts from the home directory by default, can navigate up to the root. */
2003
+ /** Server directory browsing (the Workspace picker): starts from the home directory by default, can navigate up to the root. */
1291
2004
  interface DirEntryInfo {
1292
2005
  name: string;
1293
- /** Absolute path of this subdirectory (can be submitted directly as a Workspace). */
2006
+ /** Absolute path of this entry (a folder's can be submitted directly as a Workspace). */
2007
+ path: string;
2008
+ /**
2009
+ * `file` for anything that is not a folder (a symlink counts as what it points to). Absent
2010
+ * means a folder: a machine listed over ssh reports folders only.
2011
+ */
2012
+ kind?: "dir" | "file";
2013
+ /** Last modification, epoch milliseconds; absent when unknown (listings over ssh, an entry stat could not reach). */
2014
+ mtime?: number;
2015
+ /** Hidden by the machine's own rules beyond the dot-name convention (Windows hidden/system attribute). */
2016
+ hidden?: true;
2017
+ }
2018
+ /** A storage location the machine offers beside its folders: a Windows drive, a macOS volume, a Linux root or mount. */
2019
+ interface DirLocation {
2020
+ /** Absolute path it opens (`C:\`, `/`, `/Volumes/USB`, `/media/me/USB`, `/mnt/c`). */
1294
2021
  path: string;
2022
+ kind: "drive" | "removable" | "network" | "optical" | "volume" | "root";
2023
+ /** The machine's own name for it (volume label, volume name, share `\\nas\media`, WSL `C:`); absent when none. */
2024
+ label?: string;
2025
+ }
2026
+ /** The platform's standard folders as that machine resolves them; only those that exist. */
2027
+ interface DirStandardFolders {
2028
+ desktop?: string;
2029
+ documents?: string;
2030
+ downloads?: string;
2031
+ pictures?: string;
1295
2032
  }
1296
2033
  interface DirListResponse {
1297
2034
  /** Absolute path of the current directory (realpath). */
1298
2035
  path: string;
1299
2036
  /** Absolute path of the parent directory; null when already at the root. */
1300
2037
  parent: string | null;
1301
- /** Subdirectory list (sorted by name, files excluded). */
2038
+ /** Entries sorted by name, hidden ones included (the picker drops them). */
1302
2039
  entries: DirEntryInfo[];
2040
+ /** The listed machine's `process.platform`; absent for a machine listed over ssh. */
2041
+ platform?: string;
2042
+ /** Home request with `places=1` only. */
2043
+ standardFolders?: DirStandardFolders;
2044
+ /** Home request with `places=1` only, in the order the sidebar lists them. */
2045
+ locations?: DirLocation[];
1303
2046
  }
1304
2047
  /** One Skill found in a picked directory: metadata plus which of the two layouts it came from. */
1305
2048
  interface DirectorySkillItem extends SkillMetadataItem {
@@ -1325,6 +2068,11 @@ interface SessionCreateRequest {
1325
2068
  workspace?: string;
1326
2069
  /** Defaults to allow-all. */
1327
2070
  approvalMode?: ApprovalMode;
2071
+ /**
2072
+ * The Session's sandbox policy; either half omitted takes the server's Sandbox settings.
2073
+ * A non-admin may not pick anything looser than those settings (403 `sandbox_forbidden`).
2074
+ */
2075
+ sandbox?: Partial<SessionSandbox>;
1328
2076
  /**
1329
2077
  * Creating-client hint stored on the Session row: "cli" when the CLI creates the
1330
2078
  * Session through the API, "org" when the organization runtime opened it (a desk or a
@@ -1332,16 +2080,17 @@ interface SessionCreateRequest {
1332
2080
  * not the organization still exists); defaults to "web". A REQUEST may send only "web" or
1333
2081
  * "cli": the runtime writes "org" by calling the service directly, so no caller can claim
1334
2082
  * an organization's provenance for its own Session. Lists serve every row regardless of
1335
- * client; only development mode's session list filters on it.
2083
+ * client unless asked for the user's own rows only (`excludeOrg=1`), which is what
2084
+ * development mode's session list asks for.
1336
2085
  */
1337
2086
  client?: "web" | "cli" | "org";
1338
2087
  /**
1339
- * Marks the Session as created by a Benchmark evaluation or optimization (the Evaluation
1340
- * Center's Use flows, and the Test Sessions agent-evaluation launches through the CLI). Only
1341
- * this value is accepted from a client: `subagent` and `schedule` are written by the server
1342
- * itself. The Web App files such Sessions into the Evaluations folder of the session list.
2088
+ * The Session's source, when it is not a person's conversation: only `cli` is accepted from
2089
+ * a client (`penguin run` sends it); every other source is the server's own to write, and
2090
+ * absent means `user`. The retired `benchmark` is still accepted, as `cli`, until 0.3.0, for
2091
+ * an older CLI or Web App that still sends it.
1343
2092
  */
1344
- source?: "benchmark";
2093
+ source?: "cli";
1345
2094
  }
1346
2095
  interface SessionCreateResponse {
1347
2096
  session: SessionInfo;
@@ -1369,6 +2118,11 @@ interface SessionResponse {
1369
2118
  }
1370
2119
  interface SessionPatchRequest {
1371
2120
  approvalMode?: ApprovalMode;
2121
+ /**
2122
+ * Change this Session's sandbox policy; applies from its next command. A non-admin may not
2123
+ * pick anything looser than the server's Sandbox settings (403 `sandbox_forbidden`).
2124
+ */
2125
+ sandbox?: Partial<SessionSandbox>;
1372
2126
  /**
1373
2127
  * Pin this Session's thinking level (`none | low | medium | high | xhigh | max`, anything else
1374
2128
  * is a 400). It replaces the Agent-config fallback for this Session and applies from the
@@ -1377,7 +2131,11 @@ interface SessionPatchRequest {
1377
2131
  * is no unpin: the picker only offers concrete levels.
1378
2132
  */
1379
2133
  thinkingLevel?: ThinkingLevelName;
1380
- /** Archive / unarchive (default list hides archived). */
2134
+ /**
2135
+ * `true` archives (clears the keep mark); `false` unarchives and writes the keep mark, so a
2136
+ * background Session stays in the sidebar through later runs. Announced as `session_archived`.
2137
+ * 409 `session_not_listed` on a `subagent` or `company` Session, which is in no list.
2138
+ */
1381
2139
  archived?: boolean;
1382
2140
  /** Manual rename; non-empty string, overrides the auto-generated title. */
1383
2141
  title?: string;
@@ -1413,7 +2171,10 @@ interface MessagesLiveTail {
1413
2171
  * only; the parameterless full read never carries it). A window is a run of whole
1414
2172
  * message-bearing units — one unit = one Task in the Web reducer's sense, opened by a
1415
2173
  * main-session user prompt — cut so that no pairing (tool_call/output), compaction span
1416
- * or steering group ever splits across windows.
2174
+ * or steering group ever splits across windows. Besides the unit count, a window stops
2175
+ * before the unit that would take its serialized messages past 4 MiB, but always holds at
2176
+ * least one unit; such a window carries `before` like any other, with fewer units than
2177
+ * asked for.
1417
2178
  */
1418
2179
  interface MessagesPageInfo {
1419
2180
  /**
@@ -1449,9 +2210,31 @@ interface MessagesPageInfo {
1449
2210
  sessionTokens: number;
1450
2211
  contextTokens: number;
1451
2212
  };
2213
+ /**
2214
+ * The model of the context this window starts in — what the `session_meta` heading that
2215
+ * context's Trace file names. A Session can switch models between contexts, and a window
2216
+ * that starts partway into one does not hold that record: the client seeds it, so the Tasks
2217
+ * ahead of the window's first `session_meta` have their model, and a switch further down the
2218
+ * window is told from a plain rotation. Absent when the Session has no Trace yet.
2219
+ */
2220
+ contextModel?: ModelRefDto;
2221
+ /**
2222
+ * Agent State directory recorded by the starting context's `session_meta`. Seeds memory
2223
+ * change detection when that record falls before the window. Absent without a Trace.
2224
+ */
2225
+ agentState?: string;
1452
2226
  }
1453
2227
  /** Message history: the full messages and events from concatenating all of this Session's Trace files in order (excludes partial_*). */
1454
2228
  interface MessagesResponse {
2229
+ /**
2230
+ * On windowed requests, images are served by reference: in every record that carries a
2231
+ * `tracePosition`, an inline PNG / JPEG / GIF / WebP `data:` URL — a user `image_url`, or
2232
+ * an entry of a tool output's `images` — is replaced by
2233
+ * `/api/sessions/<sessionId>/trace-image?file=<fileIndex>&ordinal=<ordinal>[&i=<k>]`
2234
+ * (`i` = the index into `images`), an immutable, access-checked image response. Expanded
2235
+ * subagent messages, held inputs not yet in the Trace, other image types and the
2236
+ * parameterless full read keep their data URLs.
2237
+ */
1455
2238
  messages: HistoryMessage[];
1456
2239
  /**
1457
2240
  * Present only while the Session is running/compacting: the in-progress stream tail
@@ -1534,6 +2317,32 @@ interface GoalResponse {
1534
2317
  /** The Session's most recent goal run; null if it never ran one. */
1535
2318
  goal: GoalStateView | null;
1536
2319
  }
2320
+ /**
2321
+ * `POST /api/sessions/:sessionId/switch-model`: switch this Session to another model in place.
2322
+ * The running context is closed on the model it ran on, then the next one opens on the target.
2323
+ *
2324
+ * - **202** {@link TaskCreateResponse} — the switch streams, the Session status `compacting`.
2325
+ * A context with a completed turn is closed by an ordinary `manual` compaction pair (always
2326
+ * summarize); one without — just compacted, or its first request never finished — has
2327
+ * nothing to summarize and streams no pair. Then come the new context's opener records
2328
+ * and, last, its `session_meta`: its `provider` / `model_id` name the model the Session now
2329
+ * runs on, the Session reads report that pair from this record on, and `task_state` turns
2330
+ * idle after it.
2331
+ * A compaction that ends other than `completed` means no switch — no `session_meta` follows
2332
+ * and the Session keeps its model.
2333
+ * - **200** {@link SessionResponse} — a Session that never ran has no context to close: it
2334
+ * switched inside the request, and nothing is streamed.
2335
+ * - **409**, one code per refusal, before any event: `task_in_progress` / `compacting` (busy),
2336
+ * `same_model`, `model_not_configured` (the target is not in the Project config),
2337
+ * `model_unavailable` (its client cannot be constructed, e.g. no credential), and
2338
+ * `compaction_not_configured`.
2339
+ */
2340
+ interface SessionSwitchModelRequest {
2341
+ /** Provider group of the target model. */
2342
+ provider: string;
2343
+ /** Upstream model_id of the target model. */
2344
+ modelId: string;
2345
+ }
1537
2346
  interface TaskCreateResponse {
1538
2347
  /** Current actual session_id: a Trace-less invalid Session self-heals and returns a new id; the frontend updates its route accordingly. */
1539
2348
  sessionId: string;
@@ -1735,6 +2544,20 @@ interface MessagingRuntimeStatus {
1735
2544
  at: string;
1736
2545
  detail: string;
1737
2546
  };
2547
+ /**
2548
+ * Since when (ISO 8601) the bot has been HOLDING replies it could not deliver yet, to send
2549
+ * them with the chat's next inbound message; absent while nothing is held. WeChat only: the
2550
+ * platform accepts a bot's messages only for a while after the user's last one, and only
2551
+ * about ten per message, so a long run or a quiet user leaves replies waiting rather than
2552
+ * lost.
2553
+ *
2554
+ * Not a failure, and deliberately not `lastDeliveryError`: nothing is broken and nobody has
2555
+ * to act beyond what the panel says — message the bot in WeChat and the held replies arrive
2556
+ * first. Cleared the moment they have been delivered. Unlike the fields above it is not
2557
+ * scoped to one connection: held replies are stored, so a backlog from before a restart is
2558
+ * reported again as soon as the connection opens.
2559
+ */
2560
+ heldReplySince?: string;
1738
2561
  }
1739
2562
  /**
1740
2563
  * What a messaging binding carries whatever channel it is: the connection intent, the three
@@ -2228,11 +3051,17 @@ type ServerEvent =
2228
3051
  * that has now run from one that never has — a first run would otherwise settle back into the
2229
3052
  * blank "never ran" row the client still believes in.
2230
3053
  *
3054
+ * `projectId` names the Session's Project: a list showing one Project ignores another's flips
3055
+ * without a request, and a flip for a Session of its own Project that it holds no row for is a
3056
+ * row it is missing — one that became active below its activity cursor (see SessionsResponse) —
3057
+ * which it fetches by id.
3058
+ *
2231
3059
  * Published only to the user channels of the Project's owner and members.
2232
3060
  */
2233
3061
  | {
2234
3062
  type: "session_state";
2235
3063
  sessionId: string;
3064
+ projectId: string;
2236
3065
  state: SessionStatus;
2237
3066
  lastActiveAt: string;
2238
3067
  hasTrace: boolean;
@@ -2250,6 +3079,19 @@ type ServerEvent =
2250
3079
  processes: number;
2251
3080
  subagents: number;
2252
3081
  }
3082
+ /**
3083
+ * A Session's count of tool calls waiting for a person's approval changed: a call was
3084
+ * escalated (every call under always-ask, rw/unknown calls under read-only), answered, or
3085
+ * denied by an interrupt. The count is the row's `pendingApprovalCount` as it stands after
3086
+ * the change, zeros included, so a list moves its mark without refetching; the calls
3087
+ * themselves are on the Session's own stream (`approval_request`, replayed on subscribe).
3088
+ * Published to the user channels of the Project's owner and members, like `session_state`.
3089
+ */
3090
+ | {
3091
+ type: "session_approvals";
3092
+ sessionId: string;
3093
+ count: number;
3094
+ }
2253
3095
  /** Last-Event-ID has been evicted from the buffer: the frontend should re-fetch the history endpoint before continuing to consume this connection. */
2254
3096
  | {
2255
3097
  type: "resync_required";
@@ -2273,22 +3115,75 @@ type ServerEvent =
2273
3115
  type: "web_updated";
2274
3116
  rev: string;
2275
3117
  }
2276
- /** New session registered (pushed over the parent session's channel for subagent sessions): frontend refreshes the list in place. */
3118
+ /**
3119
+ * A Session now exists. On the user channel for every creation — the CLI, another tab,
3120
+ * a schedule, an agent spawning a child — so the list learns about rows it did not make;
3121
+ * and on the parent Session's channel for a subagent, which no list shows. `source` is what
3122
+ * the new Session's meta records, and it tells a client which list, if any, the row joins.
3123
+ */
2277
3124
  | {
2278
3125
  type: "session_created";
2279
3126
  projectId: string;
2280
3127
  agentId: string;
2281
3128
  sessionId: string;
2282
3129
  source: SessionSource;
2283
- } | ScheduleServerEvent | GoalServerEvent | CompanyServerEvent;
2284
- /** Goal-mode progress on the session channel (the chat page drives its goal banner from these). */
2285
- type GoalServerEvent =
2286
- /** A goal run began (published before the first round). */
2287
- {
2288
- type: "goal_started";
2289
- sessionId: string;
2290
- objective: string;
2291
- budget: number;
3130
+ }
3131
+ /**
3132
+ * The user's listing decision on one Session changed (PATCH `archived`, the archive page's
3133
+ * batch unarchive): an archived row leaves the sidebar, an unarchived one joins it. One per
3134
+ * Session, to the Project's users.
3135
+ */
3136
+ | {
3137
+ type: "session_archived";
3138
+ sessionId: string;
3139
+ projectId: string;
3140
+ agentId: string;
3141
+ archived: boolean;
3142
+ }
3143
+ /**
3144
+ * A Session was deleted (DELETE, the subagent children deleted with it, the archive page's
3145
+ * batch delete): every client drops the row. One per Session, to the Project's users.
3146
+ */
3147
+ | {
3148
+ type: "session_deleted";
3149
+ sessionId: string;
3150
+ projectId: string;
3151
+ } | ScheduleServerEvent | GoalServerEvent | CompanyServerEvent | BuiltinBrowserServerEvent | MachineServerEvent;
3152
+ /**
3153
+ * The machines' two events on the user channel, published to admins only — the Machines page,
3154
+ * and the `/server/<id>` proxy everything else about a machine goes through, are admin-only.
3155
+ */
3156
+ type MachineServerEvent =
3157
+ /**
3158
+ * Something the machines list reports changed — a job queued, started, moved to its next
3159
+ * step, logged a line or ended; a connection was held or dropped; a reconnect began, failed
3160
+ * or succeeded; a machine joined or left a Project; a checkout's install image started or
3161
+ * finished building. No payload: a page re-reads `GET …/machines`, which is its one account
3162
+ * of every machine. Coalesced, so a burst of log lines costs a few reads, not one per line.
3163
+ */
3164
+ {
3165
+ type: "machines_changed";
3166
+ }
3167
+ /**
3168
+ * One of a connected machine's own user-channel events, relayed by this server from that
3169
+ * machine's `GET /api/events` (machines/event-relay.ts). The page holds one event stream to
3170
+ * this server whatever the number of machines: a stream per machine made five connected
3171
+ * machines take the browser's whole HTTP/1.1 allowance of six connections to one origin, and
3172
+ * every other request of the page then waited behind them for good.
3173
+ */
3174
+ | {
3175
+ type: "machine_event";
3176
+ machineId: string;
3177
+ event: ServerEvent;
3178
+ };
3179
+ /** Goal-mode progress on the session channel (the chat page drives its goal banner from these). */
3180
+ type GoalServerEvent =
3181
+ /** A goal run began (published before the first round). */
3182
+ {
3183
+ type: "goal_started";
3184
+ sessionId: string;
3185
+ objective: string;
3186
+ budget: number;
2292
3187
  }
2293
3188
  /** A round is starting; `used` is the runner's accounting up to this point. */
2294
3189
  | {
@@ -2323,6 +3218,20 @@ type ScheduleServerEvent =
2323
3218
  agentId: string;
2324
3219
  name: string;
2325
3220
  sessionId: string;
3221
+ }
3222
+ /** A workbench of the Agent was (re)loaded — its folder changed, a reload was requested, or a version was restored. */
3223
+ | {
3224
+ type: "workbench_updated";
3225
+ projectId: string;
3226
+ agentId: string;
3227
+ workbench: WorkbenchInfo;
3228
+ }
3229
+ /** A workbench's folder is gone — removed from the Web App or deleted on disk. */
3230
+ | {
3231
+ type: "workbench_removed";
3232
+ projectId: string;
3233
+ agentId: string;
3234
+ workbenchId: string;
2326
3235
  };
2327
3236
  interface TraceFileInfo {
2328
3237
  /** Trace file index (one file corresponds to one complete model context). */
@@ -2405,7 +3314,10 @@ interface TraceEventsResponse {
2405
3314
  events: OmniMessage[];
2406
3315
  offset: number;
2407
3316
  limit: number;
2408
- /** Total line count of the file (basis for pagination). */
3317
+ /**
3318
+ * Record count of the file (basis for pagination), in the ordinals `tracePosition` and the
3319
+ * analysis' `messageFrom` / `messageTo` use: malformed lines are not records.
3320
+ */
2409
3321
  total: number;
2410
3322
  }
2411
3323
  /** Duration span of a single LLM Request (request_begin/request_end paired by proximity). */
@@ -2557,7 +3469,11 @@ interface WorkspaceFilesResponse {
2557
3469
  path: string;
2558
3470
  entries: WorkspaceFileEntry[];
2559
3471
  }
2560
- /** Write one Workspace file whole (the Upload button, a drop, and the Files panel's editor). */
3472
+ /**
3473
+ * Write one Workspace file whole (the Upload button, a drop, and the Files panel's editor). A
3474
+ * write answers 204 with the written file's version in `ETag` — the marker the next conditional
3475
+ * write of the same file carries, so an editor can keep editing after a save.
3476
+ */
2561
3477
  interface FilesWriteRequest {
2562
3478
  /** The entire file, base64-encoded (≤14MB decoded). */
2563
3479
  dataBase64: string;
@@ -2572,7 +3488,7 @@ interface FilesWriteRequest {
2572
3488
  */
2573
3489
  ifVersion?: string;
2574
3490
  }
2575
- /** Move or rename one Workspace file (the Files panel's context menu). */
3491
+ /** Move or rename one Workspace file or folder (the Files panel's context menu). */
2576
3492
  interface FilesMoveRequest {
2577
3493
  /** Source path, relative to the Workspace root. */
2578
3494
  from: string;
@@ -2585,12 +3501,23 @@ interface FilesMoveRequest {
2585
3501
  * has none, because the caller never read it, which is why an occupied destination is
2586
3502
  * refused with 409 `target_exists` rather than overwritten.
2587
3503
  *
2588
- * A directory `from` is a 400 whatever this field says: a directory carries no single
2589
- * version marker, so the precondition that protects this operation cannot be expressed for
2590
- * one, and silently moving a tree without that protection is worse than refusing to move it.
3504
+ * A directory `from` takes none, and one sent with it is a 400: a directory carries no
3505
+ * single version marker, so no precondition can be stated for it. A folder moves whole and
3506
+ * unconditionally — nothing is lost that way, since whatever the Agent wrote into it meanwhile
3507
+ * moves with it — and is refused only when the destination is occupied or lies inside it.
2591
3508
  */
2592
3509
  ifVersion?: string;
2593
3510
  }
3511
+ /**
3512
+ * Create one empty text file or one folder in a Workspace (the Files panel's New menu). Missing
3513
+ * parent directories are created as a write creates them. Anything already at `path` — a file,
3514
+ * a folder or a link — is refused with 409 `target_exists` and nothing is written.
3515
+ */
3516
+ interface FilesCreateRequest {
3517
+ /** The new entry's path, relative to the Workspace root. */
3518
+ path: string;
3519
+ kind: "file" | "dir";
3520
+ }
2594
3521
  /**
2595
3522
  * One Workspace search match. `kind`, `sizeBytes` and `mtime` are named and typed exactly as
2596
3523
  * {@link WorkspaceFileEntry} names them: a hit is drawn by the same row renderer as a tree
@@ -2701,6 +3628,19 @@ interface TraceAnalysisResponse {
2701
3628
  * scope every total here shares). Absent exactly when the turns carry no `cost`.
2702
3629
  */
2703
3630
  cost?: number;
3631
+ /**
3632
+ * `cost` split by pricing bucket: every Request's Tokens at the rates and tier its share of
3633
+ * `cost` was priced at, summed per bucket over the file. Present exactly when `cost` is; the
3634
+ * turns carry no split.
3635
+ */
3636
+ costParts?: CostParts;
3637
+ /**
3638
+ * The model's context window as the file's head `session_meta` records it
3639
+ * (`model_context_window`), for the context ring. The panel no longer reads every event, so the
3640
+ * analysis carries it; absent when the head records none (an older server, or a file without
3641
+ * one), and the panel then falls back as it always has.
3642
+ */
3643
+ modelContextWindow?: number | string;
2704
3644
  requests: RequestSpan[];
2705
3645
  /** Token / duration aggregated per Task (used directly by the Trace page's context ring and per-turn TPS). */
2706
3646
  tasks: TraceTaskStats[];
@@ -2793,6 +3733,17 @@ interface TraceImportResponse {
2793
3733
  date: string;
2794
3734
  }
2795
3735
  type UsageGroupBy = "date" | "agent" | "model" | "session";
3736
+ /**
3737
+ * A cost split by the three pricing buckets, in USD: cached input (`cacheRead`), uncached input
3738
+ * (`cacheWrite`, input Tokens on a cache miss) and output. Each part is its bucket's Tokens at the
3739
+ * rate the whole cost was priced at, so the three sum to the cost they accompany (up to
3740
+ * floating-point rounding).
3741
+ */
3742
+ interface CostParts {
3743
+ cacheRead: number;
3744
+ cacheWrite: number;
3745
+ output: number;
3746
+ }
2796
3747
  interface UsageBucket {
2797
3748
  total: number;
2798
3749
  requests: number;
@@ -2812,6 +3763,12 @@ interface UsageGroupRow {
2812
3763
  total: number;
2813
3764
  requests: number;
2814
3765
  cost: number | null;
3766
+ /**
3767
+ * `cost` split by pricing bucket, each record at the same rates, tier and promotion that
3768
+ * priced it in `cost`. Present exactly when `cost` is not null; a row's unpriced usage is in
3769
+ * neither.
3770
+ */
3771
+ costParts?: CostParts;
2815
3772
  hasUncosted: boolean;
2816
3773
  }
2817
3774
  /** Time-series precision for the usage series (`granularity` query parameter). `minute` requires the `fromTs`/`toTs` window bounds. */
@@ -2886,13 +3843,22 @@ interface UsageErrorCount {
2886
3843
  kind: string;
2887
3844
  count: number;
2888
3845
  }
2889
- /** A single error summary (one row in the stats center's error panel table). */
3846
+ /**
3847
+ * One row of the stats center's error panel table: the records of one calendar day that share
3848
+ * a source, code, kind and message. The day is the viewer's when the request carried
3849
+ * `utcOffsetMinutes`, and the server's own otherwise.
3850
+ */
2890
3851
  interface UsageErrorItem {
3852
+ /** The latest of the records this row stands for. */
2891
3853
  ts: string;
2892
3854
  source: string;
2893
3855
  code: string;
2894
3856
  kind: string;
2895
3857
  message: string;
3858
+ /** How many records this row stands for (the panel's "×N"). */
3859
+ count: number;
3860
+ /** The earliest of them; equal to `ts` for a single record. */
3861
+ firstTs: string;
2896
3862
  }
2897
3863
  /**
2898
3864
  * Server-side error capture stats: not affected by the model
@@ -2903,14 +3869,16 @@ interface UsageErrorItem {
2903
3869
  * items.
2904
3870
  */
2905
3871
  interface UsageErrors {
2906
- /** Filtered row count — also what a clear of the same filter takes (see {@link UsageErrorsClearResponse}). */
3872
+ /** Filtered record count — also what a clear of the same filter takes (see {@link UsageErrorsClearResponse}). */
2907
3873
  total: number;
2908
3874
  /** Count of unexpected ones (500 / runtime exceptions) among them — the part the frontend highlights. */
2909
3875
  unexpected: number;
2910
3876
  /** The most frequent source · code (null when there are no errors). */
2911
3877
  topCode: UsageErrorCount | null;
2912
- /** Most recent N items (reverse chronological) — the first page; older ones come from `GET /usage/errors`. */
3878
+ /** Most recent N rows (reverse chronological) — the first page; older ones come from `GET /usage/errors`. */
2913
3879
  recent: UsageErrorItem[];
3880
+ /** How many rows the table has for this filter (records folded per {@link UsageErrorItem}) — what the pager counts. */
3881
+ rows: number;
2914
3882
  }
2915
3883
  /**
2916
3884
  * GET /api/projects/:projectId/usage/errors — one page of the error detail table, newest
@@ -2920,12 +3888,15 @@ interface UsageErrors {
2920
3888
  * or neither), so a page never widens what the summary counted, plus an optional `kind`
2921
3889
  * ({@link UsageErrorKind}) narrowing to one of the two categories — which
2922
3890
  * is how the cost-center badge asks "are there unexpected errors, and how new is the newest"
2923
- * with `limit=1` instead of pulling the whole dashboard aggregate.
3891
+ * with `limit=1` instead of pulling the whole dashboard aggregate. `utcOffsetMinutes` (minutes
3892
+ * east of UTC, the dashboard takes it too) says whose calendar day a row's records share.
2924
3893
  */
2925
3894
  interface UsageErrorsPage {
2926
3895
  items: UsageErrorItem[];
2927
- /** Filtered row count, so the caller knows when it has reached the end. */
3896
+ /** Filtered record count — the occurrences, which is what the cost-center badge counts. */
2928
3897
  total: number;
3898
+ /** Filtered row count (records folded per {@link UsageErrorItem}), so the caller knows when it has reached the end. */
3899
+ rows: number;
2929
3900
  }
2930
3901
  /**
2931
3902
  * DELETE /api/projects/:projectId/usage/errors — empties the error table for the filter the
@@ -3016,6 +3987,23 @@ interface SchedulesResponse {
3016
3987
  error: string;
3017
3988
  }>;
3018
3989
  }
3990
+ /**
3991
+ * One task in the Project-wide listing (GET /api/projects/:projectId/schedules): the agent's
3992
+ * item plus the agent whose schedule directory holds it, since the list spans every agent.
3993
+ */
3994
+ interface ProjectScheduleItem extends ScheduleItem {
3995
+ agentId: string;
3996
+ }
3997
+ interface ProjectSchedulesResponse {
3998
+ /** Every agent's tasks: agents in id order, each agent's tasks in the order its own listing returns them. */
3999
+ schedules: ProjectScheduleItem[];
4000
+ /** Files that failed to parse (skipped from scheduling and logged as errors), with the agent that holds each. */
4001
+ invalidFiles: Array<{
4002
+ agentId: string;
4003
+ name: string;
4004
+ error: string;
4005
+ }>;
4006
+ }
3019
4007
  interface ScheduleUpsertRequest {
3020
4008
  prompt: string;
3021
4009
  enabled: boolean;
@@ -3095,13 +4083,43 @@ interface BenchmarkEvaluation {
3095
4083
  * `BenchmarkSummary.status`.
3096
4084
  */
3097
4085
  type BenchmarkStatus = "draft" | "published" | "failed";
4086
+ /**
4087
+ * Where a Benchmark's copy came from: `builtin` (seeded when the Project was created), `manual`
4088
+ * (the create form), `agent` (the benchmark-design Skill wrote it), `git` (an Agent imported a
4089
+ * repository folder), `zip` (an uploaded package).
4090
+ */
4091
+ type BenchmarkOriginKind = "builtin" | "manual" | "agent" | "git" | "zip";
4092
+ /**
4093
+ * Why a Benchmark's manifest cannot be read: the code of core's `BenchmarkManifestError`
4094
+ * (`benchmark_id_mismatch` for an `id` that is not the directory's, `benchmark_manifest_invalid`
4095
+ * for a file that is not TOML or a `version` or `[origin]` out of shape) and its message, which
4096
+ * names the key or the file.
4097
+ */
4098
+ interface BenchmarkManifestProblem {
4099
+ code: string;
4100
+ message: string;
4101
+ }
4102
+ /** The `[origin]` table of a Benchmark's benchmark_config.toml. */
4103
+ interface BenchmarkOrigin {
4104
+ kind: BenchmarkOriginKind;
4105
+ /** git: the folder link as the user gave it (a GitHub tree URL). Always an http(s) URL. */
4106
+ url?: string;
4107
+ /** git: the 40-hex commit the import resolved the link to. */
4108
+ ref?: string;
4109
+ /** git: the folder inside the repository. */
4110
+ path?: string;
4111
+ /** git / zip: when this copy was written (ISO 8601; `imported_at` in the file). */
4112
+ importedAt?: string;
4113
+ }
3098
4114
  interface BenchmarkSummary {
3099
4115
  /** Directory name is the identifier (semantic naming, e.g. swe-bench-v1). */
3100
4116
  id: string;
3101
- /** Title from benchmark_config.toml; falls back to the directory name if unset. */
4117
+ /** Title from benchmark_config.toml; the directory name when it gives none or the manifest cannot be read. */
3102
4118
  title: string;
3103
4119
  description?: string;
3104
- /** Number of runs per case (the `runs` field in benchmark_config.toml, ≥1; defaults to 1). */
4120
+ /** The description in Chinese (`description_zh` in benchmark_config.toml), when the manifest gives one. */
4121
+ descriptionZh?: string;
4122
+ /** Number of runs per case (the `runs` key in benchmark_config.toml, an integer ≥ 1); absent when it gives none or the manifest cannot be read, which reads as 1. */
3105
4123
  runs?: number;
3106
4124
  /**
3107
4125
  * `draft` while the Skill that builds the Benchmark is still writing its cases and
@@ -3109,10 +4127,29 @@ interface BenchmarkSummary {
3109
4127
  * Benchmark is frozen and its Formal Baseline recorded; `failed` when calibration ended
3110
4128
  * without a Pilot result that could be frozen, which leaves the Benchmark unusable — the Web
3111
4129
  * App masks it too and says it has to be deleted and created again. benchmark_config.toml is
3112
- * read literally: only `draft` is a draft and only `failed` is a failure, so a missing field,
3113
- * or any other value, reads as published.
4130
+ * read literally: only `draft` is a draft and only `failed` is a failure, so a missing key, or
4131
+ * any other value, reads as published. A Benchmark whose manifest cannot be read lists as
4132
+ * failed, with `manifestError`.
3114
4133
  */
3115
4134
  status: BenchmarkStatus;
4135
+ /**
4136
+ * The manifest's date version, `YYYY.MM.DD.N`: the revision of the Benchmark's content. Absent
4137
+ * when the manifest has none (one written before versions is unversioned), when it cannot be
4138
+ * read, and from a machine whose server predates versions.
4139
+ */
4140
+ version?: string;
4141
+ /**
4142
+ * Where this copy came from, the manifest's `[origin]`. Absent when the manifest has none (where
4143
+ * a copy written before origins came from is unknown), when it cannot be read, and from a
4144
+ * machine whose server predates origins.
4145
+ */
4146
+ origin?: BenchmarkOrigin;
4147
+ /**
4148
+ * Set when the manifest cannot be read. Such a Benchmark lists under its directory name, as
4149
+ * `failed`, with no version or origin, and nothing may use or export it until the file is
4150
+ * fixed; the Web App masks it with this reason.
4151
+ */
4152
+ manifestError?: BenchmarkManifestProblem;
3116
4153
  /** Case count (number of case subfolders). */
3117
4154
  caseCount: number;
3118
4155
  /** Time-ordered evaluation records (the evaluations[] in scoreboard.yaml). */
@@ -3139,10 +4176,11 @@ interface BenchmarkCasesResponse {
3139
4176
  }
3140
4177
  /**
3141
4178
  * POST /api/projects/:p/benchmarks (owner only): create a Benchmark by hand. The
3142
- * server writes the on-disk layout the evaluation Skills read — `benchmark_config.toml`, a
3143
- * `scoreboard.yaml` holding `evaluations: []`, and one `<case id>/` per case with
3144
- * `statement/README.md` and `rubric/README.md`. 409 `benchmark_exists` when the directory is
3145
- * already there; nothing is merged into an existing Benchmark.
4179
+ * server writes the on-disk layout the evaluation Skills read — `benchmark_config.toml`
4180
+ * (published, the day's first version, origin `manual`), a `scoreboard.yaml` holding
4181
+ * `evaluations: []`, and one `<case id>/` per case with `statement/README.md` and
4182
+ * `rubric/README.md`. 409 `benchmark_exists` when the directory is already there; nothing is
4183
+ * merged into an existing Benchmark.
3146
4184
  */
3147
4185
  interface BenchmarkCreateRequest {
3148
4186
  /** Directory name, which is the identifier: letters, digits, `_` and `-` only. */
@@ -3167,6 +4205,47 @@ interface BenchmarkCreateCase {
3167
4205
  interface BenchmarkCreateResponse {
3168
4206
  benchmark: BenchmarkSummary;
3169
4207
  }
4208
+ /**
4209
+ * POST /api/projects/:p/benchmarks/archive (any Project member): import a Benchmark package from
4210
+ * a zip. The zip holds `benchmark_config.toml` and the `CASE-*` directories, at its root (where the
4211
+ * manifest must name its `id`) or inside exactly one top-level directory named by the Benchmark's
4212
+ * id — the shape `GET …/benchmarks/:benchmarkId/archive` exports. A zip carrying
4213
+ * `scoreboard.yaml`, `.jobs/` or anything else at its top level, a link, a manifest past the
4214
+ * create form's limits, a package that is not `published` or a case without both READMEs is
4215
+ * refused (400); so is a zip past the caps (413 `benchmark_too_large`: 14MB
4216
+ * zipped, 1000 files, 5MB a file, 20MB inflated). The copy is written with origin `zip` and an
4217
+ * empty scoreboard — or origin `git`, when the request says the package is a repository folder
4218
+ * fetched at a commit. An id already taken is 409 `benchmark_exists` (`details.benchmarkId` names
4219
+ * it) unless `overwrite`.
4220
+ */
4221
+ interface BenchmarkArchiveImportRequest {
4222
+ /** The zip, base64-encoded. */
4223
+ dataBase64: string;
4224
+ /**
4225
+ * Replace the Benchmark of the same id whole — its evaluation records and `.jobs/` included.
4226
+ * Refused with 409 `benchmark_busy` while an evaluation of it is still running a trial there.
4227
+ */
4228
+ overwrite?: boolean;
4229
+ /**
4230
+ * The repository folder the package was fetched from, pinned to one commit. The copy's origin
4231
+ * is then `git` with these fields and the import time, instead of `zip`.
4232
+ */
4233
+ origin?: BenchmarkArchiveGitOrigin;
4234
+ }
4235
+ /** Where a package fetched from a git repository came from. */
4236
+ interface BenchmarkArchiveGitOrigin {
4237
+ kind: "git";
4238
+ /** The folder link as the user gave it: an http(s) URL, at most 2048 characters. */
4239
+ url: string;
4240
+ /** The 40-character commit the link was resolved to. */
4241
+ ref: string;
4242
+ /** The folder inside the repository, at most 1024 characters. */
4243
+ path: string;
4244
+ }
4245
+ interface BenchmarkArchiveImportResponse {
4246
+ /** The imported Benchmark, as the list now reads it. */
4247
+ benchmark: BenchmarkSummary;
4248
+ }
3170
4249
  interface SkillMetadataItem {
3171
4250
  /** Skill directory name (the identity key for install / uninstall / Prompt addressing). */
3172
4251
  name: string;
@@ -3181,7 +4260,7 @@ interface SkillMetadataItem {
3181
4260
  * which the plugin item carries once. Absent, the frontend draws the book glyph.
3182
4261
  */
3183
4262
  icon?: string;
3184
- /** Version (`YYYY.MM.DD.N`, frontmatter version; a copy installed before that spelling still carries `YYYY-MM-DD.N`); an empty string when the frontmatter carries none or a malformed one. */
4263
+ /** The skill's own dated version (`YYYY.MM.DD.N`, frontmatter version; a copy installed before that spelling still carries `YYYY-MM-DD.N`); an empty string when the frontmatter carries none or a malformed one. */
3185
4264
  version: string;
3186
4265
  }
3187
4266
  /** One installed hook package (`agent_state/hooks/<name>/`): its manifest, without the scripts. */
@@ -3197,21 +4276,81 @@ interface HookItem {
3197
4276
  /** The plugin's raw icon.svg, written beside the manifest at install time; absent when the plugin ships none (the frontend draws the hook glyph). */
3198
4277
  icon?: string;
3199
4278
  }
3200
- /** One library plugin as the listing describes it: the manifest fields plus what it ships (skill bodies and scripts are never sent). */
4279
+ /**
4280
+ * One library plugin as the listing describes it: the manifest fields (its package.json — npm's
4281
+ * fields and the `penguin` block) plus what it ships (skill bodies and scripts are never sent).
4282
+ * Every field but the identity, the version and the contents is optional: a field the package
4283
+ * does not carry is missing here, and the page says so rather than inventing one.
4284
+ */
3201
4285
  interface PluginItem {
3202
4286
  name: string;
4287
+ /** Display name (`penguin.title`, `title_zh`); absent → the page shows `name`. */
4288
+ title?: string;
4289
+ titleZh?: string;
4290
+ /** English one-liner (package.json `description`); "" when the package carries none. */
3203
4291
  description: string;
3204
4292
  descriptionZh?: string;
3205
4293
  shortDescription?: string;
3206
4294
  shortDescriptionZh?: string;
3207
- /** `YYYY.MM.DD.N`. */
4295
+ /** The plugin's npm version (its package.json, following the release) — what the card shows. Installed copies are compared part by part instead: each skill's `version`, and {@link hookVersion}. */
3208
4296
  version: string;
3209
- /** The plugin's skills (metadata only). */
4297
+ /** The npm package the plugin is, e.g. `@penguinharness/a2ui` — what Delete and a server module's row name it by. */
4298
+ package: string;
4299
+ /** Where the plugin comes from: `builtin` — a package this build ships; `installed` — one an admin installed on the server (by name, link or zip). */
4300
+ source: "builtin" | "installed";
4301
+ /** The package's npm `author` (a name), `license`, `homepage` and `repository` URL, for the detail dialog. */
4302
+ author?: string;
4303
+ license?: string;
4304
+ homepage?: string;
4305
+ repository?: string;
4306
+ /** The plugin's skills (metadata only), each with its own dated `version`. */
3210
4307
  skills: SkillMetadataItem[];
4308
+ workbenches?: Array<{
4309
+ name: string;
4310
+ version: string;
4311
+ }>;
3211
4312
  /** The hook points the plugin's hook package answers at (`[]` without one). */
3212
4313
  hooks: string[];
3213
- /** The plugin's raw icon.svg (beside plugin.json — every built-in plugin ships one), the icon of everything it ships; the frontend draws the puzzle-piece plugin glyph without it. */
4314
+ /** The hook package's own dated version (`penguin.hooks.version`, `YYYY.MM.DD.N`); absent without a hook package. */
4315
+ hookVersion?: string;
4316
+ /** The plugin's raw icon SVG (`icon.svg` at the package root, or `penguin.icon` — every built-in plugin ships one; an unsafe one is never sent), the icon of everything it ships; the frontend draws the puzzle-piece plugin glyph without it. */
3214
4317
  icon?: string;
4318
+ /** The demo the Plugins page's quick start pre-fills (`penguin.quick_start`); absent = pre-select its first skill. */
4319
+ quickStart?: QuickStartItem;
4320
+ /** The MCP servers the plugin installs into an Agent's `tools.mcpServers` (`penguin.mcp_servers`); `[]` without any. */
4321
+ mcpServers: PluginMcpServerItem[];
4322
+ }
4323
+ /** An MCP transport, as an entry resolves it (`transport`, else inferred from `command` / `url`). */
4324
+ type McpTransport = "stdio" | "http" | "sse";
4325
+ /** One vault key a plugin's MCP server needs before it can connect: declared in the manifest, or only referenced (then the key is all there is to show). */
4326
+ interface PluginSetupKeyItem {
4327
+ key: string;
4328
+ label?: string;
4329
+ labelZh?: string;
4330
+ /** Where to get the value (a URL), or one sentence. */
4331
+ help?: string;
4332
+ }
4333
+ /** A plugin's MCP server as the library lists it: identity and target only — no header, env or oauth value. */
4334
+ interface PluginMcpServerItem {
4335
+ name: string;
4336
+ transport: McpTransport;
4337
+ /** The URL (http/sse), or `command args…` (stdio), as written: `${PLUGIN_ROOT}` and `${KEY}` references unresolved. */
4338
+ target: string;
4339
+ /** Every vault key the server's config references, declared ones first. */
4340
+ setup: PluginSetupKeyItem[];
4341
+ /** The server offers OAuth sign-in (`config.oauth`). */
4342
+ oauth: boolean;
4343
+ /** It needs that sign-in — OAuth and no `Authorization` header of its own — which this version cannot do: an Agent skips it. */
4344
+ signIn: boolean;
4345
+ }
4346
+ /** A quick start: a prompt pre-filled into a new-chat draft — never sent by the page. */
4347
+ interface QuickStartItem {
4348
+ prompt: string;
4349
+ promptZh?: string;
4350
+ /** Skills to pre-select (a library plugin's own). */
4351
+ skills?: string[];
4352
+ /** Open the draft in goal mode. */
4353
+ goal?: boolean;
3215
4354
  }
3216
4355
  interface PluginGroupItem {
3217
4356
  id: string;
@@ -3243,8 +4382,35 @@ interface PluginInstallRequest {
3243
4382
  names: string[];
3244
4383
  }
3245
4384
  interface AgentPluginsInstallResponse {
4385
+ workbenches?: Array<{
4386
+ name: string;
4387
+ plugin: string;
4388
+ version: string;
4389
+ }>;
3246
4390
  skills: SkillMetadataItem[];
3247
4391
  hooks: HookItem[];
4392
+ /** The Agent's MCP servers after the install (a plugin's servers join its `tools.mcpServers`). A server name the Agent already has from someone else is 409 `mcp_server_name_taken`, nothing installed. */
4393
+ mcpServers: AgentMcpServerItem[];
4394
+ }
4395
+ /**
4396
+ * One `tools.mcpServers` entry of an Agent, as the Plugins page and the Agent settings read it:
4397
+ * identity, target and what keeps it from connecting — never a header, env or oauth value.
4398
+ */
4399
+ interface AgentMcpServerItem {
4400
+ name: string;
4401
+ transport: McpTransport;
4402
+ /** The URL (http/sse), or `command args…` (stdio): `${PLUGIN_ROOT}` already resolved at install, `${KEY}` references left as written. */
4403
+ target: string;
4404
+ /** The plugin that installed it; absent for an entry the user added. */
4405
+ plugin?: string;
4406
+ /** `${KEY}` references of its config that the Agent's vault does not hold (names only); empty = nothing to set up. */
4407
+ missingKeys: string[];
4408
+ /** It needs an OAuth sign-in, which this version cannot do: skipped at connect time. */
4409
+ signIn: boolean;
4410
+ }
4411
+ /** GET /api/projects/:p/agents/:a/mcp-servers: the Agent's MCP servers in config order; DELETE …/mcp-servers/:name removes one (204, 404 `unknown_mcp_server`). */
4412
+ interface AgentMcpServersResponse {
4413
+ servers: AgentMcpServerItem[];
3248
4414
  }
3249
4415
  /** GET /api/projects/:p/agents/:a/skills: Skills installed on this Agent. */
3250
4416
  interface AgentSkillsResponse {
@@ -3280,6 +4446,95 @@ interface SkillArchiveInstallRequest {
3280
4446
  /** Replace an already-installed Skill of the same name (deletes its directory first). */
3281
4447
  overwrite?: boolean;
3282
4448
  }
4449
+ /**
4450
+ * One published version of a plugin — the index entry format every plugin registry
4451
+ * speaks (modeled on the typst/packages `index.json` schema: a flat array of
4452
+ * per-version entries; a plugin published at several versions appears once per
4453
+ * version). Installation is out of scope here: an entry's `name` is the package
4454
+ * specifier a Project's plugin list names.
4455
+ */
4456
+ interface PluginIndexEntry {
4457
+ /** Package specifier — the string a Project's plugin list names. */
4458
+ name: string;
4459
+ /** Localized display names; the UI falls back to the package name. */
4460
+ title?: string;
4461
+ titleZh?: string;
4462
+ /** Semantic version of this entry. */
4463
+ version: string;
4464
+ description: string;
4465
+ /** Chinese description (optional; the UI picks per language, English without it). */
4466
+ descriptionZh?: string;
4467
+ /** One-line descriptions for the card (optional; the card falls back to `description`). */
4468
+ shortDescription?: string;
4469
+ shortDescriptionZh?: string;
4470
+ /**
4471
+ * The plugin's raw icon SVG (optional; the card draws the puzzle piece without it). The
4472
+ * listing fills these display fields from the package's own package.json (its `penguin`
4473
+ * block) and icon when the package is on this server, over whatever the index row says.
4474
+ */
4475
+ icon?: string;
4476
+ authors: string[];
4477
+ /** SPDX license identifier. */
4478
+ license: string;
4479
+ /** Source repository URL. */
4480
+ repository?: string;
4481
+ homepage?: string;
4482
+ /** Free-form searchable terms; the Web App renders them as chips (e.g. the target OS). */
4483
+ keywords?: string[];
4484
+ /** Capability floor(s) the plugin provides on (e.g. "sandbox"). */
4485
+ categories?: string[];
4486
+ /** Unix timestamp (seconds) of the entry's last update. */
4487
+ updatedAt?: number;
4488
+ }
4489
+ /** GET /api/plugins/registry: the merged index of every configured registry (currently the builtin one). */
4490
+ interface PluginIndexResponse {
4491
+ plugins: PluginIndexEntry[];
4492
+ /**
4493
+ * Sources that could not be read, by `source` and reason. Present and empty when every
4494
+ * source answered. A remote index that is down shortens the listing rather than emptying
4495
+ * it, so the page needs to be able to say so instead of silently showing less.
4496
+ */
4497
+ failures: {
4498
+ source: string;
4499
+ error: string;
4500
+ }[];
4501
+ /**
4502
+ * Sources the server was still reading when it answered, by `source`. Present and empty when
4503
+ * every source answered. The listing does not wait long for a published index, so one on a slow
4504
+ * or unreachable network is named here and its entries are missing; a later read lists them
4505
+ * once they have arrived, or names the source under `failures` once it has failed.
4506
+ */
4507
+ pending: string[];
4508
+ }
4509
+ /**
4510
+ * A plugin's long-form docs: the README.md at its package root, as npm ships it.
4511
+ * GET /api/plugins/registry/readme?name= answers for an index entry, with `readme` null when
4512
+ * the package is not on this server or has none; GET /api/plugins/:plugin/readme answers for a
4513
+ * library plugin, 404 when it ships none.
4514
+ */
4515
+ interface PluginReadmeResponse {
4516
+ name: string;
4517
+ /** Markdown, rendered by the Web App. Null when this entry has no readme. */
4518
+ readme: string | null;
4519
+ }
4520
+ /**
4521
+ * One language a plugin contributes, as the listing reports it. The grammar itself is not
4522
+ * here: it is tens to hundreds of kilobytes, and only the languages a conversation actually
4523
+ * shows are worth fetching (`GET /api/languages/:id/grammar`).
4524
+ */
4525
+ interface LanguageSummary {
4526
+ /** Canonical id: the fence info string, and the id both endpoints address. */
4527
+ id: string;
4528
+ displayName: string;
4529
+ /** Alternative fence info strings, needed BEFORE the grammar loads (it is what decides to load it). */
4530
+ aliases?: string[];
4531
+ /** File extensions without the dot, for the Workspace file viewer. */
4532
+ extensions?: string[];
4533
+ }
4534
+ /** GET /api/languages: every language this App's plugins contributed, by id. */
4535
+ interface LanguageIndexResponse {
4536
+ languages: LanguageSummary[];
4537
+ }
3283
4538
  /**
3284
4539
  * GET /api/version: the running build's identity plus this root's pushed harness, verbatim
3285
4540
  * from `versionReport()` — the same record `penguin version --json` prints, so the two
@@ -3289,6 +4544,17 @@ interface SkillArchiveInstallRequest {
3289
4544
  * predating the stamping — v0.1.2 and earlier — where the UI shows the version alone).
3290
4545
  */
3291
4546
  type VersionResponse = VersionReport;
4547
+
4548
+ /** GET /api/version/history: the harness versions this data root has committed, newest first. */
4549
+ type VersionHistoryResponse = HarnessHistory;
4550
+ /** POST /api/version/history/rollback `{ id }`: the push back has started; the swap follows. */
4551
+ interface VersionRollbackResponse {
4552
+ started: true;
4553
+ id: string;
4554
+ }
4555
+ /** GET /api/version/history/diff?from=&to=: what changed between two stored interface tables. */
4556
+ type VersionHistoryDiffResponse = IfacesDiff;
4557
+
3292
4558
  /**
3293
4559
  * GET /api/version/update-check: newest published release vs the running version.
3294
4560
  * Always HTTP 200 (fail-soft): a lookup failure sets `error` and leaves `latestVersion`
@@ -3407,6 +4673,44 @@ interface DesktopTrayCommandMessage {
3407
4673
  showTrayIcon?: boolean;
3408
4674
  locale?: DesktopTrayLocale;
3409
4675
  }
4676
+ /** What the shell's read of one folder came to. */
4677
+ interface DesktopFolderAccessResult {
4678
+ /** The folder could be read. Always true off macOS, where the shell reads nothing. */
4679
+ granted: boolean;
4680
+ /** The failed read's errno code (`EPERM`, `EACCES`, `ENOENT`, …); `EINVAL` for a path that is not absolute. */
4681
+ code?: string;
4682
+ /**
4683
+ * Whether the app is a packaged build (Electron's `app.isPackaged`). An unpackaged one is a
4684
+ * development instance started from a terminal, and macOS holds that terminal responsible
4685
+ * for its reads rather than the app.
4686
+ */
4687
+ packaged: boolean;
4688
+ }
4689
+ /** Server → shell request over the utilityProcess message channel (relayed from POST /api/projects/:projectId/dirs/access). */
4690
+ interface DesktopFolderAccessMessage {
4691
+ type: "desktop-folder-access";
4692
+ /** Pairs the reply with its request. */
4693
+ id: string;
4694
+ /** The folder to read, as an absolute path. */
4695
+ path: string;
4696
+ }
4697
+ /** Shell → server reply to one {@link DesktopFolderAccessMessage}, carrying its `id`. */
4698
+ interface DesktopFolderAccessResultMessage extends DesktopFolderAccessResult {
4699
+ type: "desktop-folder-access-result";
4700
+ id: string;
4701
+ }
4702
+ /** POST /api/projects/:projectId/dirs/access: the shell's answer, for the picker to act on. */
4703
+ interface DirAccessResponse {
4704
+ granted: boolean;
4705
+ packaged: boolean;
4706
+ }
4707
+ /** The Privacy & Security panes the page may open: Files and Folders, and Full Disk Access. */
4708
+ type DesktopPrivacyPane = "files" | "fullDisk";
4709
+ /** Server → shell command over the utilityProcess message channel (relayed from POST /api/desktop/privacy-settings). No reply. */
4710
+ interface DesktopOpenPrivacySettingsMessage {
4711
+ type: "desktop-open-privacy-settings";
4712
+ pane: DesktopPrivacyPane;
4713
+ }
3410
4714
  /**
3411
4715
  * The outcome of one self-update run (`penguin update --yes` on the server host), carried
3412
4716
  * by {@link UpdateJobStatus.result}. `unsupported` covers both a server not launched via
@@ -3497,6 +4801,16 @@ interface MachineInfo {
3497
4801
  version: string;
3498
4802
  at: string;
3499
4803
  };
4804
+ /** This server signs in to the machine with a password it stores for the alias, not a key. */
4805
+ passwordSaved?: true;
4806
+ /**
4807
+ * In this Project's list: the Machines page shows a card for it. A machine joins the list when
4808
+ * it is added (`POST …/machines/add`, which installs nothing) or when an install or a use for
4809
+ * this Project runs on it, and leaves it with stop-using or release. Absent for a host that is
4810
+ * only in the ssh config, or only in another Project's list; never set on `local`, which is
4811
+ * always shown.
4812
+ */
4813
+ member?: true;
3500
4814
  /**
3501
4815
  * The machine's OWN id — 16 base64url characters minted by the server that runs there,
3502
4816
  * stable across renames, re-aliasing and reinstalls. Null until a server has started on
@@ -3521,6 +4835,19 @@ interface MachineInfo {
3521
4835
  connection: {
3522
4836
  pid: number;
3523
4837
  } | null;
4838
+ /**
4839
+ * Present while this server is bringing a machine it keeps connected back — the connection
4840
+ * dropped, its server stopped or restarted, or this server itself just started — and absent
4841
+ * otherwise. A machine is kept connected from the moment it is enabled until it is stopped
4842
+ * (`POST …/stop`) or deleted (`POST …/stop-using`); see machines/service.ts's supervision.
4843
+ */
4844
+ reconnect?: MachineReconnect;
4845
+ /**
4846
+ * This server keeps a connection to it: it was enabled, or started, and has not been stopped
4847
+ * or deleted since. Absent otherwise — a machine only added, one whose server was stopped
4848
+ * here, one that never got as far as a connection.
4849
+ */
4850
+ keepConnected?: true;
3524
4851
  /**
3525
4852
  * The machine's API as last seen by this server's proxy — stamped by traffic passing
3526
4853
  * through, never by a probe of its own. `answeredAt` when the last forwarded request got
@@ -3536,6 +4863,12 @@ interface MachineInfo {
3536
4863
  failedAt: string;
3537
4864
  detail: string;
3538
4865
  } | null;
4866
+ /**
4867
+ * What the last time this server handed it the Project config came to: at a connect, a Sync
4868
+ * config job, or an automatic sync after a config edit here. Absent until the first one since
4869
+ * this server started; kept in memory only.
4870
+ */
4871
+ configSync?: MachineConfigSync;
3539
4872
  /**
3540
4873
  * What the last probe found over there, or null when none has been taken. Never filled in
3541
4874
  * at list time: a probe costs an ssh round trip per machine, so the list reports the last
@@ -3543,6 +4876,80 @@ interface MachineInfo {
3543
4876
  * since each probe is an ssh round trip while the list is only the config's text.
3544
4877
  */
3545
4878
  status: MachineServerStatus | null;
4879
+ /**
4880
+ * The data root the server there runs on (`PENGUIN_HOME`). This instance's PROFILE decides
4881
+ * it — a dev instance names the machine's dev root and never the release one beside it
4882
+ * (machines/layout.ts) — which is exactly what a reader looking at two instances of this
4883
+ * page needs to tell them apart. Written in that machine's own spelling once its platform
4884
+ * is known, and in the POSIX one before that; for `local` it is this process's own root,
4885
+ * already resolved to an absolute path.
4886
+ */
4887
+ root: string;
4888
+ }
4889
+ /**
4890
+ * One config sync to a machine: when it ended, whether every part of it reached the machine for
4891
+ * every Project using it, and what did not, in the far side's words.
4892
+ */
4893
+ interface MachineConfigSync {
4894
+ /** ISO timestamp the sync ended. */
4895
+ at: string;
4896
+ ok: boolean;
4897
+ /** The parts that did not go through, one per line; absent when `ok`. */
4898
+ detail?: string;
4899
+ /**
4900
+ * The values the Project's models take from this server's environment variables rather than
4901
+ * its config (an API key, or a base URL), which no sync can carry: one row per variable. On
4902
+ * the list answered for a Project, that Project's rows only.
4903
+ */
4904
+ envOnly: MachineEnvOnly[];
4905
+ /**
4906
+ * The model entries the machine refused by name — a hand-added model in a group that takes
4907
+ * only its built-in ones there — left out while the rest of the table went. On the list
4908
+ * answered for a Project, that Project's rows only.
4909
+ */
4910
+ skipped: MachineSkippedModel[];
4911
+ }
4912
+ /** A model entry a machine would not take: its error code and its own words for why. */
4913
+ interface MachineSkippedModel {
4914
+ projectId: string;
4915
+ provider: string;
4916
+ modelId: string;
4917
+ /** The far side's error code (`model_not_addable`, `model_not_routable`). */
4918
+ code: string;
4919
+ reason: string;
4920
+ }
4921
+ /** A model entry that takes `variable` from this server's environment. */
4922
+ interface MachineEnvOnly {
4923
+ projectId: string;
4924
+ provider: string;
4925
+ modelId: string;
4926
+ variable: string;
4927
+ }
4928
+ /**
4929
+ * A machine this server keeps connected, while it is being brought back. Attempts back off
4930
+ * from 5 s, doubling to a 5-minute cap, so a machine that is down for a day costs a probe every
4931
+ * five minutes; after three failures in a row the connection check runs on its own, and its
4932
+ * answer is what the page shows as the explanation.
4933
+ */
4934
+ interface MachineReconnect {
4935
+ /** When this streak began: the moment the machine was found not connected. */
4936
+ since: string;
4937
+ /** Attempts that failed in this streak; 0 while the first is still to run or running. */
4938
+ attempts: number;
4939
+ /** When the next attempt runs; null while one is running. */
4940
+ nextAt: string | null;
4941
+ /** The last attempt's failure in the far side's words; null before the first one fails. */
4942
+ lastError: string | null;
4943
+ /** Why ssh refused, when the last failure was ssh's own — for the page to say in its language. */
4944
+ sshReason?: MachineSshFailure;
4945
+ /** The connection check run after three failures in a row; null until then. */
4946
+ diagnosis: MachineDiagnosis | null;
4947
+ /**
4948
+ * The password stored for the machine was refused, or its host takes none (`sshReason`
4949
+ * says which): no attempt runs until a password is saved for it again or removed, since
4950
+ * retrying could lock the account. `nextAt` is null meanwhile.
4951
+ */
4952
+ waitsForPassword?: true;
3546
4953
  }
3547
4954
  /**
3548
4955
  * One machine's server state. There is deliberately no separate "ssh" status: ssh is the
@@ -3565,11 +4972,38 @@ interface MachineServerStatus {
3565
4972
  * refused key or an unusable Node than a paraphrase would.
3566
4973
  */
3567
4974
  interface MachineJob {
3568
- kind: "install" | "connect" | "restart";
4975
+ /**
4976
+ * `use` is the whole pipeline — install if needed, hand over, connect, sync — as one job;
4977
+ * `connect` is what the page calls Start service; `stop` stops the machine's server and stops
4978
+ * keeping it connected; `uninstall` stops it and removes the program, and the data directory
4979
+ * when asked; `sync` hands a connected machine the Project config.
4980
+ */
4981
+ kind: "install" | "connect" | "restart" | "use" | "stop" | "uninstall" | "sync";
3569
4982
  machineId: string;
3570
4983
  alias: string;
4984
+ /** Waiting its turn: a few machines are worked on at once, and a batch queues the rest. */
4985
+ queued: boolean;
3571
4986
  running: boolean;
4987
+ /**
4988
+ * The steps this job goes through, in order — what the page draws as its stepper. Fixed when
4989
+ * the job is queued; a step the job turns out not to need is passed over, never revisited.
4990
+ */
4991
+ steps: MachinePhase[];
4992
+ /**
4993
+ * Which of `steps` the job is on. Only moves forward. Null until the first step is named; a
4994
+ * finished job keeps its last phase.
4995
+ */
4996
+ phase: MachinePhase | null;
3572
4997
  log: string[];
4998
+ /**
4999
+ * The connection check run after the job failed at a step it can explain — checking,
5000
+ * installing, connecting: running, then its answer. Absent for every other job.
5001
+ */
5002
+ diagnosis?: {
5003
+ state: "running";
5004
+ } | ({
5005
+ state: "done";
5006
+ } & MachineDiagnosis);
3573
5007
  result: null | {
3574
5008
  ok: true;
3575
5009
  installed: "installed" | "already-installed";
@@ -3577,66 +5011,305 @@ interface MachineJob {
3577
5011
  } | {
3578
5012
  ok: true;
3579
5013
  connected: true;
5014
+ }
5015
+ /** The machine's server is stopped and stays so until it is started again. */
5016
+ | {
5017
+ ok: true;
5018
+ stopped: true;
5019
+ }
5020
+ /** The program is gone from the machine; `dataRemoved` says whether its data root went too. */
5021
+ | {
5022
+ ok: true;
5023
+ uninstalled: true;
5024
+ dataRemoved: boolean;
5025
+ }
5026
+ /** Every part of the Project config reached the machine (its `configSync` says the same). */
5027
+ | {
5028
+ ok: true;
5029
+ synced: true;
3580
5030
  } | {
3581
5031
  ok: false;
3582
5032
  step: string;
3583
5033
  message: string;
3584
5034
  /**
3585
5035
  * The failure has a next step this side can take, and it needs saying yes to:
3586
- * installing the PROGRAM over there and restarting it. Set when a hot update could
3587
- * not be handed over — the machine's store holds no CLI this server can talk to, and
3588
- * installing is what replicates one. Offered rather than done, because it restarts a
3589
- * server this Project does not own alone.
5036
+ * installing the PROGRAM over there and restarting it. Every failed install or
5037
+ * connect offers it — a failure that leaves no next step leaves a person stuck —
5038
+ * except a run that was itself that install, and one this server could not act on
5039
+ * (no build of its own to send), which says `false`. Offered rather than done,
5040
+ * because it restarts a server this Project does not own alone.
3590
5041
  */
3591
- canReplaceProgram?: true;
5042
+ canReplaceProgram?: boolean;
5043
+ /**
5044
+ * Why ssh refused, when the failure is ssh's own (a host key, a key, a host name, a
5045
+ * route), for the page to say in its language; `message` says it in English, with
5046
+ * ssh's own words after `(ssh: …)`.
5047
+ */
5048
+ sshReason?: MachineSshFailure;
3592
5049
  };
3593
5050
  }
5051
+ /**
5052
+ * A source checkout's install image (machines/checkout-image.ts), built from the checkout
5053
+ * again at each install or use: not built yet, building, built, or failed with the build's
5054
+ * own last words.
5055
+ */
5056
+ type MachinesCheckoutImage = {
5057
+ state: "unbuilt";
5058
+ } | {
5059
+ state: "building";
5060
+ } | {
5061
+ state: "built";
5062
+ } | {
5063
+ state: "failed";
5064
+ detail: string;
5065
+ };
3594
5066
  /** GET /api/machines, and the 202 body of POST /api/machines/:machineId/install. */
3595
5067
  interface MachinesResponse {
3596
5068
  machines: MachineInfo[];
3597
5069
  /**
3598
5070
  * The version an install would leave on the remote — the base release, plus a `+hmr.<sha>`
3599
- * suffix when this server carries a pushed version to replicate. Null for a development
3600
- * checkout, which stands on no release the remote could download.
5071
+ * suffix when this server carries a pushed version to replicate. A source checkout answers
5072
+ * with the image it last built, and null before its first build (see `checkoutImage`).
5073
+ * Null otherwise only for a server that carries no image and builds none.
3601
5074
  */
3602
5075
  imageVersion: string | null;
5076
+ /**
5077
+ * Present only when this server runs from a source checkout: it builds its install image
5078
+ * from the checkout at each install or use, so no image yet is not a reason to refuse one.
5079
+ */
5080
+ checkoutImage?: MachinesCheckoutImage;
5081
+ /** The most recently started job, running or finished. */
3603
5082
  job: MachineJob | null;
5083
+ /**
5084
+ * Every job worth showing this generation: the queued ones, the running one, and the last
5085
+ * finished one per machine — so a batch reads as a list of rows each saying where it is.
5086
+ */
5087
+ jobs: MachineJob[];
3604
5088
  }
3605
5089
  /**
3606
- * Organization status: `paused` stops every automatic trigger; humans can still talk to any
3607
- * desk. These two values are the whole lifecycle — no route deletes an organization, so a
3608
- * paused one keeps its conversations, employees, desks and tickets.
5090
+ * Every step a job may name, in the order they can occur. A job lists its own subset as
5091
+ * `MachineJob.steps`: a source checkout's enable starts by building its install image
5092
+ * (`build`); a stop is `check`, `stop`; an uninstall `check`, `stop`, `remove`; a config sync
5093
+ * `check`, `sync`.
3609
5094
  */
3610
- type OrgStatus = "active" | "paused";
3611
- /** Approval mode for desk and ticket sessions; unattended runs never get always-ask. */
3612
- type OrgApprovalMode = "allow-all" | "read-only" | "deny-all";
3613
- /** The five kanban columns, each a directory under `tickets/<yyyy-mm>/`. */
3614
- type OrgTicketStatus = "proposed" | "in_progress" | "review" | "done" | "rejected";
3615
- type OrgTicketPriority = "P0" | "P1" | "P2";
3616
- /** Live employee state: running when the desk or any ticket session has a Task in progress; paused when budget-paused. */
3617
- type OrgEmployeeState = "running" | "idle" | "paused";
5095
+ declare const MACHINE_PHASES: readonly ["build", "check", "install", "handover", "restart", "connect", "sync", "stop", "remove"];
5096
+ type MachinePhase = (typeof MACHINE_PHASES)[number];
5097
+ /** `POST /api/projects/:projectId/machines/:machineId/uninstall`. */
5098
+ interface MachineUninstallRequest {
5099
+ /** Remove the machine's data root too — its Sessions, Agents and config there. Default false. */
5100
+ removeData?: boolean;
5101
+ }
3618
5102
  /**
3619
- * The trigger kinds an `[org_trigger]` block carries. `ticket_notice` is read-only: a ticket
3620
- * change starts no run of its own, so nothing writes that kind any more, and it stays in the
3621
- * union because Traces recorded while it did still carry it.
5103
+ * `POST /api/projects/:projectId/machines/ssh-hosts`: append a host block to this server's
5104
+ * `~/.ssh/config`. Answers the machines list (201), or 400 `ssh_host_invalid` naming the
5105
+ * field, or 409 `ssh_host_exists`.
3622
5106
  */
3623
- type OrgTriggerKind = "init" | "event" | "mention" | "ticket_notice" | "ticket_work";
3624
- /** The ticket changes an employee is told about, listed in the body of its next calendar sweep. */
3625
- type OrgTicketChange = "assigned" | "blocked" | "blocker_closed" | "done" | "rejected";
3626
- /** What the last evaluation of a calendar event did. */
3627
- type OrgCalendarOutcome = "fired" | "queued" | "paused" | "missed" | "error";
5107
+ interface SshHostRequest {
5108
+ /** The alias — what `ssh <alias>` will take, and the machine's name everywhere here. */
5109
+ alias: string;
5110
+ hostName: string;
5111
+ user?: string;
5112
+ port?: number;
5113
+ identityFile?: string;
5114
+ /**
5115
+ * Sign in with this password instead of a key: stored on this server in a 0600 file under its
5116
+ * data root, never in the ssh config, and never sent back. On a PUT, `null` deletes the stored
5117
+ * one (back to key sign-in) and leaving it out keeps it. A PUT on a block written by hand may
5118
+ * set it when the other fields are the block as it stands. 400 `ssh_password_invalid` for an
5119
+ * empty or multi-line one; 409 `ssh_password_unsupported` (`details.ssh`: what `ssh -V` said)
5120
+ * when this computer's ssh cannot be handed a password.
5121
+ */
5122
+ password?: string | null;
5123
+ }
3628
5124
  /**
3629
- * The organization's working language: the language its handbook, its CEO's initialization
3630
- * run, every employee brief and every desk and ticket run are written in. Detected from the
3631
- * mission at creation (CJK text → `zh`, anything else → `en`) unless the request names one.
5125
+ * `GET /api/projects/:projectId/machines/ssh-hosts/:alias`: a host's block read back, and
5126
+ * whether this app wrote it. Only a block this app wrote may be rewritten
5127
+ * (`PUT …/ssh-hosts/:alias`, the same fields less the alias): a hand-written one may carry
5128
+ * options this app does not know, and rewriting it would drop them.
3632
5129
  */
3633
- type OrgLanguage = "zh" | "en";
3634
- /** The organization's settings as `org_config.toml` records them. */
3635
- interface OrganizationSettings {
3636
- name: string;
3637
- mission: string;
3638
- status: OrgStatus;
3639
- /** IANA timezone: budget periods (natural months) and chat day files follow it. */
5130
+ interface SshHostResponse extends Omit<SshHostRequest, "password"> {
5131
+ editable: boolean;
5132
+ /** A password is stored for this host, which then signs in with it rather than with a key. */
5133
+ passwordSaved?: true;
5134
+ }
5135
+ /**
5136
+ * `GET /api/projects/:projectId/machines/ssh-hosts`: every host this server's ssh config declares,
5137
+ * its included files' too, with what each one's own block says about reaching it — the literal
5138
+ * lines, not what ssh resolves (no `ssh -G`): a `Host *` default or a `Match` block is not
5139
+ * applied. For the add dialog's rows; an option the block leaves out is absent.
5140
+ */
5141
+ interface SshHostSummary {
5142
+ alias: string;
5143
+ hostName?: string;
5144
+ user?: string;
5145
+ port?: number;
5146
+ }
5147
+ interface SshHostsResponse {
5148
+ hosts: SshHostSummary[];
5149
+ }
5150
+ /**
5151
+ * `POST /api/projects/:projectId/machines/add`: put these machines in this Project's list without
5152
+ * installing anything there — each then has a card whose one thing to do is enabling it. Answers
5153
+ * like `…/use`, refusals by id (`unknown-machine`, `self`).
5154
+ */
5155
+ interface MachinesAddRequest {
5156
+ machines: string[];
5157
+ }
5158
+ /** `POST /api/projects/:projectId/machines/use`: bring these machines into use, as one queued batch. */
5159
+ interface MachinesUseRequest {
5160
+ /** Machine ids (`ssh:<alias>`). Every one is queued; refusals come back by id. */
5161
+ machines: string[];
5162
+ /** Install the program even where its version matches, and restart there — the answer to a job that asked for it. */
5163
+ replaceProgram?: boolean;
5164
+ }
5165
+ /** Why one machine of a batch was not queued; the rest were. */
5166
+ type MachineUseRefusal = "unknown-machine" | "self" | "no-image";
5167
+ /**
5168
+ * Why ssh could not get a session to a machine, read from its own diagnostic
5169
+ * (machines/ssh-failure.ts); `other` when it printed none of the ones with a fixed meaning.
5170
+ */
5171
+ type MachineSshFailure = "host-key-unknown" | "host-key-changed" | "key-file" | "auth" | "password-rejected" | "password-not-offered" | "host-not-found" | "refused" | "timeout" | "closed" | "ssh-config" | "other";
5172
+ /** Whether a machine reaches one release source: the package is there, the source answered without it, or no answer. */
5173
+ type MachineSourceReach = "ok" | "missing" | "unreachable";
5174
+ /**
5175
+ * One check of a machine (machines/diagnose.ts), as facts for the page to word. `pass` is
5176
+ * fine, `warn` works with a caveat, `fail` stops an install or a connect, `skip` could not be
5177
+ * asked (an earlier check failed, or it does not apply).
5178
+ */
5179
+ type MachineCheck =
5180
+ /** A session came up (keys, or the stored password), as this account on that host. */
5181
+ {
5182
+ id: "ssh";
5183
+ state: "pass";
5184
+ user: string;
5185
+ host: string;
5186
+ }
5187
+ /** It did not; `said` is ssh's own words, led by what they mean. */
5188
+ | {
5189
+ id: "ssh";
5190
+ state: "fail";
5191
+ reason: MachineSshFailure;
5192
+ said: string;
5193
+ }
5194
+ /**
5195
+ * `warn` for Windows (installable, not connectable yet), and for a Linux whose glibc runs the
5196
+ * release but is older than `terminalsNeed`, the one its terminal binding loads on.
5197
+ */
5198
+ | {
5199
+ id: "platform";
5200
+ state: "pass" | "warn";
5201
+ os: string;
5202
+ arch: string;
5203
+ glibc?: string;
5204
+ terminalsNeed?: string;
5205
+ }
5206
+ /**
5207
+ * What the release's Node cannot run on: an OS or architecture no release is published for
5208
+ * (`unsupported`, `said` what the machine answered), a glibc older than `need` (`glibc`), or
5209
+ * a musl libc such as Alpine's (`musl`).
5210
+ */
5211
+ | {
5212
+ id: "platform";
5213
+ state: "fail";
5214
+ reason: "unsupported" | "glibc" | "musl";
5215
+ os?: string;
5216
+ arch?: string;
5217
+ glibc?: string;
5218
+ need?: string;
5219
+ said?: string;
5220
+ }
5221
+ /**
5222
+ * What the release installer needs and the machine lacks. `warn` when curl alone is missing:
5223
+ * the release is then sent over ssh rather than downloaded there.
5224
+ */
5225
+ | {
5226
+ id: "tools";
5227
+ state: "pass" | "warn" | "fail";
5228
+ missing: string[];
5229
+ }
5230
+ /**
5231
+ * Whether the machine itself reaches release `version` for its platform. `warn`: out of
5232
+ * reach, so an install cannot download it there; `fail`: both sources answered and neither
5233
+ * has it (not published).
5234
+ */
5235
+ | {
5236
+ id: "download";
5237
+ state: "pass" | "warn" | "fail";
5238
+ version: string;
5239
+ github: MachineSourceReach;
5240
+ oss: MachineSourceReach;
5241
+ }
5242
+ /** Free space in the machine's home against what an install needs, in MB. */
5243
+ | {
5244
+ id: "disk";
5245
+ state: "pass" | "warn" | "fail";
5246
+ freeMb: number;
5247
+ needMb: number;
5248
+ }
5249
+ /** The port its server would start on: free, already its PenguinHarness server's, or taken by something else. */
5250
+ | {
5251
+ id: "port";
5252
+ state: "pass" | "fail";
5253
+ port: number;
5254
+ holder: "free" | "penguin" | "other";
5255
+ } | {
5256
+ id: "platform" | "tools" | "download" | "disk" | "port";
5257
+ state: "skip";
5258
+ };
5259
+ /**
5260
+ * `POST /api/projects/:projectId/machines/:machineId/diagnose`: the checks, in order. Nothing
5261
+ * is written on the machine; 409 `job_running` while a job works on it.
5262
+ */
5263
+ interface MachineDiagnosis {
5264
+ machineId: string;
5265
+ checkedAt: string;
5266
+ checks: MachineCheck[];
5267
+ }
5268
+ interface MachinesUseResponse extends MachinesResponse {
5269
+ refused: {
5270
+ machineId: string;
5271
+ why: MachineUseRefusal;
5272
+ }[];
5273
+ }
5274
+ /** `POST /api/projects/:projectId/machines/stop-using`: let go of these machines — connection dropped, Project membership released; the install stays. */
5275
+ interface MachinesStopUsingRequest {
5276
+ machines: string[];
5277
+ }
5278
+ /**
5279
+ * Organization status: `paused` stops every automatic trigger; humans can still talk to any
5280
+ * desk. These two values are the whole lifecycle — no route deletes an organization, so a
5281
+ * paused one keeps its conversations, employees, desks and tickets.
5282
+ */
5283
+ type OrgStatus = "active" | "paused";
5284
+ /** Approval mode for desk and ticket sessions; unattended runs never get always-ask. */
5285
+ type OrgApprovalMode = "allow-all" | "read-only" | "deny-all";
5286
+ /** The five kanban columns, each a directory under `tickets/<yyyy-mm>/`. */
5287
+ type OrgTicketStatus = "proposed" | "in_progress" | "review" | "done" | "rejected";
5288
+ type OrgTicketPriority = "P0" | "P1" | "P2";
5289
+ /** Live employee state: running when the desk or any ticket session has a Task in progress; paused when budget-paused. */
5290
+ type OrgEmployeeState = "running" | "idle" | "paused";
5291
+ /**
5292
+ * The trigger kinds an `[org_trigger]` block carries. `ticket_notice` is read-only: a ticket
5293
+ * change starts no run of its own, so nothing writes that kind any more, and it stays in the
5294
+ * union because Traces recorded while it did still carry it.
5295
+ */
5296
+ type OrgTriggerKind = "init" | "event" | "mention" | "ticket_notice" | "ticket_work";
5297
+ /** The ticket changes an employee is told about, listed in the body of its next calendar sweep. */
5298
+ type OrgTicketChange = "assigned" | "blocked" | "blocker_closed" | "done" | "rejected";
5299
+ /** What the last evaluation of a calendar event did. */
5300
+ type OrgCalendarOutcome = "fired" | "queued" | "paused" | "missed" | "error";
5301
+ /**
5302
+ * The organization's working language: the language its handbook, its CEO's initialization
5303
+ * run, every employee brief and every desk and ticket run are written in. Detected from the
5304
+ * mission at creation (CJK text → `zh`, anything else → `en`) unless the request names one.
5305
+ */
5306
+ type OrgLanguage = "zh" | "en";
5307
+ /** The organization's settings as `org_config.toml` records them. */
5308
+ interface OrganizationSettings {
5309
+ name: string;
5310
+ mission: string;
5311
+ status: OrgStatus;
5312
+ /** IANA timezone: budget periods (natural months) and chat day files follow it. */
3640
5313
  timezone: string;
3641
5314
  approvalMode: OrgApprovalMode;
3642
5315
  /** Chat @-chain limit: a message whose hop reaches it records its mentions without triggering anyone. */
@@ -3685,8 +5358,20 @@ interface OrganizationsResponse {
3685
5358
  }
3686
5359
  interface OrgEmployeeItem {
3687
5360
  agentId: string;
3688
- /** Agent display name (system_config.yaml); falls back to the id. */
5361
+ /**
5362
+ * What this employee goes by in the organization, unique across it: the chart's `name`, else
5363
+ * the Agent's display name, else the id — with the id noted (`name (id)`) when the name is
5364
+ * not this employee's alone. It is what every surface shows and what works after `@`.
5365
+ */
3689
5366
  name: string;
5367
+ /** The `name` as written in the chart, for editing; absent when the entry has none. */
5368
+ givenName?: string;
5369
+ /**
5370
+ * Present when the employee has an avatar: the content revision of the image served at
5371
+ * `GET …/employees/:agentId/avatar` (append it as `?rev=` — the image is cached for good).
5372
+ * Absent, surfaces draw the letter tile.
5373
+ */
5374
+ avatarRev?: string;
3690
5375
  title: string;
3691
5376
  /** null for the CEO (the root). */
3692
5377
  reportsTo: string | null;
@@ -3702,6 +5387,11 @@ interface OrgEmployeeItem {
3702
5387
  modelId: string;
3703
5388
  };
3704
5389
  state: OrgEmployeeState;
5390
+ /**
5391
+ * The employee's current desk. Absent while the ledger names none, and while the Session
5392
+ * it names no longer exists (deleted by hand, or with its Agent): a desk is listed only
5393
+ * once it can be opened, and the reconcile pass re-opens a missing one when it can.
5394
+ */
3705
5395
  desk?: {
3706
5396
  sessionId: string;
3707
5397
  workspace: string;
@@ -3715,6 +5405,12 @@ interface OrgEmployeeItem {
3715
5405
  };
3716
5406
  /** Why the entry cannot be triggered (missing Agent, missing workspace directory). */
3717
5407
  invalid?: string;
5408
+ /**
5409
+ * The entry's Agent no longer exists (`invalid` carries the sentence). Its desk cannot be
5410
+ * opened; the entry stays until a person makes the employee leave, or until an Agent with
5411
+ * the same id is created again (which is how a deleted CEO is restored).
5412
+ */
5413
+ agentMissing?: true;
3718
5414
  }
3719
5415
  interface OrgChartResponse {
3720
5416
  ceoAgentId: string;
@@ -3977,8 +5673,15 @@ interface OrgDeskItem {
3977
5673
  status: SessionStatus;
3978
5674
  workspace: string;
3979
5675
  lastActiveAt?: string;
5676
+ /**
5677
+ * Present when the desk session has an ENABLED messaging binding: its channel, read as
5678
+ * `SessionInfo.messagingChannel` is (the company sidebar's desk row draws the same mark as a
5679
+ * development row, and the development list never holds a desk).
5680
+ */
5681
+ messagingChannel?: MessagingChannel;
3980
5682
  }
3981
5683
  interface OrgSessionsResponse {
5684
+ /** One per employee whose current desk Session exists; a desk the ledger names but the server no longer has is left out. */
3982
5685
  desks: OrgDeskItem[];
3983
5686
  tickets: Array<{
3984
5687
  ticketId: string;
@@ -4069,19 +5772,30 @@ interface OrganizationPatchRequest {
4069
5772
  language?: OrgLanguage;
4070
5773
  }
4071
5774
  /**
4072
- * A semantic id proposed for a display name — the organization and channel dialogs let the
4073
- * user name the thing first and derive the id from that name. The server asks the Project's
4074
- * default model for a short English snake_case id (a Chinese name has no mechanical
5775
+ * What a proposed semantic id names. Each kind has its own shape — organization and channel
5776
+ * ids carry a `co_` / `ch_` prefix, a non-admin's Project id its owner's `<username>-`, a
5777
+ * Benchmark id is kebab-case — and its own set of ids to avoid.
5778
+ */
5779
+ type SemanticIdKind = "org" | "channel" | "project" | "agent" | "benchmark";
5780
+ /**
5781
+ * A semantic id proposed for a display name — every create dialog that names an object with a
5782
+ * semantic id (a Project, an Agent, a Benchmark, an organization, a channel) lets the user name
5783
+ * the thing first and derive the id from that name. The server asks the Project's default
5784
+ * model for a short English id in the kind's spelling (a Chinese name has no mechanical
4075
5785
  * transliteration), falling back to an ASCII slug of the name when the model is unavailable,
4076
5786
  * and to a dated placeholder when neither can name it. The request never fails for a name it
4077
5787
  * cannot translate: a dialog that asked for an id always gets one back.
4078
5788
  */
4079
5789
  interface SemanticIdSuggestRequest {
4080
- /** The display name typed so far (or the mission, when nothing else names the thing). */
5790
+ /** The display name typed so far (or the mission / description, when nothing else names the thing). */
4081
5791
  name: string;
4082
- /** What the id is for: decides the prompt's examples and the fallback's prefix. */
4083
- kind: "org" | "channel";
4084
- /** Ids already in use in the target scope; the proposal avoids them. */
5792
+ /** What the id is for: decides the id's shape, the prompt's examples and the ids avoided. */
5793
+ kind: SemanticIdKind;
5794
+ /**
5795
+ * Ids already in use in the target scope; the proposal avoids them. For a Project, an Agent or
5796
+ * a Benchmark, the server also avoids, on its own, every id that kind's create route refuses
5797
+ * as taken, a leftover folder no list shows included.
5798
+ */
4085
5799
  taken?: string[];
4086
5800
  }
4087
5801
  /**
@@ -4093,7 +5807,10 @@ interface SemanticIdSuggestRequest {
4093
5807
  */
4094
5808
  type SemanticIdSuggestReason = "no_default_model" | "model_failed" | "unusable_answer" | "no_ascii";
4095
5809
  interface SemanticIdSuggestResponse {
4096
- /** A valid semantic id (`^[a-z][a-z0-9_]{1,63}$`), not in `taken`. */
5810
+ /**
5811
+ * An id the kind's create route accepts, not in `taken`: `^[a-z][a-z0-9_]{1,63}$` for most
5812
+ * kinds, `<username>-<suffix>` for a non-admin's Project, kebab-case for a Benchmark.
5813
+ */
4097
5814
  id: string;
4098
5815
  /** Who produced it: the model, the ASCII fallback, or the dated placeholder that names nothing. */
4099
5816
  source: "model" | "fallback" | "placeholder";
@@ -4110,6 +5827,11 @@ interface OrgHireRequest {
4110
5827
  description?: string;
4111
5828
  plugins?: string[];
4112
5829
  };
5830
+ /**
5831
+ * What the organization calls the employee (any script, one line, no `@`, ≤ 64 characters).
5832
+ * Omitted, a new Agent's `name` is used; with neither, the Agent's display name stands in.
5833
+ */
5834
+ name?: string;
4113
5835
  title: string;
4114
5836
  reportsTo: string;
4115
5837
  workspace?: string;
@@ -4121,6 +5843,8 @@ interface OrgHireRequest {
4121
5843
  };
4122
5844
  }
4123
5845
  interface OrgEmployeePatchRequest {
5846
+ /** null (or an empty string) clears the name: back to the Agent's display name. */
5847
+ name?: string | null;
4124
5848
  title?: string;
4125
5849
  reportsTo?: string;
4126
5850
  workspace?: string;
@@ -4321,5 +6045,890 @@ interface ContributionsResponse {
4321
6045
  agentTabs: WebContribution[];
4322
6046
  sessionTabs: WebContribution[];
4323
6047
  }
6048
+ /**
6049
+ * One field of a settings group a module declares (its `PluginConfigProvider.groups`
6050
+ * contribution's `properties.<name>`): what the Settings dialog draws for it. `secret` is
6051
+ * drawn as a password field and masked on the way out; `enum` is a choice among `options`;
6052
+ * `list` is a list of strings, drawn one per line; `table` is a fixed set of `rows`, each a
6053
+ * value per one of its `columns`, drawn as a table.
6054
+ */
6055
+ interface PluginConfigField {
6056
+ type: "string" | "secret" | "boolean" | "number" | "enum" | "list" | "table";
6057
+ title: string;
6058
+ titleZh?: string;
6059
+ /** What the field means: disclosed behind a "?" beside its title. */
6060
+ description?: string;
6061
+ descriptionZh?: string;
6062
+ /**
6063
+ * The shape a value must take ("one absolute path per line"): shown under the field at all
6064
+ * times, since it is read while typing.
6065
+ */
6066
+ hint?: string;
6067
+ hintZh?: string;
6068
+ placeholder?: string;
6069
+ /** The value a package with nothing stored reads; also what an empty field falls back to. */
6070
+ default?: string | number | boolean | string[];
6071
+ /**
6072
+ * Drawn in the card's Advanced fold, collapsed by default, rather than among the basic
6073
+ * fields. Presentation only: it is stored, validated and read like any other field.
6074
+ */
6075
+ advanced?: boolean;
6076
+ /** A save that would leave this field empty is refused. */
6077
+ required?: boolean;
6078
+ /** `enum` only: the values it may take, in display order. */
6079
+ options?: PluginConfigOption[];
6080
+ /** `list` only: the most entries a save may leave (after trimming and de-duplicating). */
6081
+ maxItems?: number;
6082
+ /** `number` only: the smallest and largest value a save may store. */
6083
+ minimum?: number;
6084
+ maximum?: number;
6085
+ /** `string` / `list` only: a regular expression every value (every line) must match. */
6086
+ pattern?: string;
6087
+ /** What a save refused by `pattern` says, after the field's name (e.g. "must be an absolute path"). */
6088
+ patternErrorMessage?: string;
6089
+ /**
6090
+ * `table` only: its columns, in display order, and its rows, in display order. The table
6091
+ * has no `default`: the rows' declared cells are its defaults, and what is stored is only
6092
+ * the cells that differ from them — `{ [row id]: { [column]: value } }`.
6093
+ */
6094
+ columns?: PluginConfigTableColumn[];
6095
+ rows?: PluginConfigTableRow[];
6096
+ /**
6097
+ * `table` only: a single choice of one row — the sandbox's default preset. The choice is
6098
+ * stored in `field`, an `enum` field of the same group whose options are the row ids (plus,
6099
+ * in an `extensible` table, the ids of the rows added to it); the page draws that field only
6100
+ * as its title in brackets after the chosen row's name ("(Default)"), and picks it from a
6101
+ * row's "…" menu.
6102
+ */
6103
+ rowChoice?: PluginConfigRowChoice;
6104
+ /**
6105
+ * `table` only: rows may be added (and only those deleted, from the row's "…" menu) and every
6106
+ * row reordered. A new row
6107
+ * starts from these values. What is stored, beside the declared rows' changed cells, is the
6108
+ * added rows under `"$added"` (`{ [row id]: { [column]: value } }`, every column present) and
6109
+ * the row order under `"$order"` (row ids); neither key can be a row id. A save sends either
6110
+ * key whole; a document without them reads as the declared rows in declared order.
6111
+ */
6112
+ extensible?: PluginConfigNewRow;
6113
+ /**
6114
+ * `table` only: a header drawn over adjacent columns that belong together (the sandbox's
6115
+ * Pin, under "Action"). `columns` names them. When the group ends the columns, the row's drag
6116
+ * handle and "…" menu join it.
6117
+ */
6118
+ columnGroup?: PluginConfigColumnGroup;
6119
+ /**
6120
+ * `table` only: a `boolean` column drawn as a pin toggle rather than a switch — a row pinned
6121
+ * to a list (the sandbox presets in the composer's menu). The texts are its tooltip in each
6122
+ * state. Declared on the table, not the column, so every column keeps one shape.
6123
+ */
6124
+ pin?: PluginConfigPinColumn;
6125
+ }
6126
+ /** A table's pin column: which boolean column, and its tooltip pinned and not. */
6127
+ interface PluginConfigPinColumn {
6128
+ column: string;
6129
+ on: string;
6130
+ onZh?: string;
6131
+ off: string;
6132
+ offZh?: string;
6133
+ }
6134
+ /**
6135
+ * A table's single choice of a row: the `enum` field it stores into, and its title, drawn in
6136
+ * brackets after the chosen row's name. The choice may name no row (nothing stored, and the
6137
+ * group's `derive` gives none): no row is marked and a save is not refused for it. A save that
6138
+ * leaves it naming a row the table no longer has is refused.
6139
+ */
6140
+ interface PluginConfigRowChoice {
6141
+ field: string;
6142
+ title: string;
6143
+ titleZh?: string;
6144
+ }
6145
+ /** One column of a `table` field: a scalar field type (`string`, `boolean`, or an `enum` with `options`). */
6146
+ interface PluginConfigTableColumn {
6147
+ name: string;
6148
+ type: "string" | "boolean" | "enum";
6149
+ title: string;
6150
+ titleZh?: string;
6151
+ /** What the column means: disclosed behind a "?" beside its header. */
6152
+ description?: string;
6153
+ descriptionZh?: string;
6154
+ options?: PluginConfigOption[];
6155
+ }
6156
+ /** One row of a `table` field: its id and its declared cells. */
6157
+ interface PluginConfigTableRow {
6158
+ id: string;
6159
+ /** Every column's declared value. */
6160
+ values: Record<string, string | boolean>;
6161
+ /** A string cell's declared value in Chinese, shown until a save changes the cell. */
6162
+ valuesZh?: Record<string, string>;
6163
+ /** Columns whose cell in this row a save may not change. */
6164
+ locked?: string[];
6165
+ /** A row added to an `extensible` table (not declared): the only kind that may be deleted. */
6166
+ added?: boolean;
6167
+ /**
6168
+ * What the row is for, disclosed behind a "?" beside its name while its `enum` cells hold
6169
+ * their declared values. A row without one, or whose choices changed, gets a "?" listing its
6170
+ * `enum` cells' values and what each does (the options' `description`s).
6171
+ */
6172
+ description?: string;
6173
+ descriptionZh?: string;
6174
+ }
6175
+ /** A header over adjacent table columns, with its own "?". */
6176
+ interface PluginConfigColumnGroup {
6177
+ title: string;
6178
+ titleZh?: string;
6179
+ description?: string;
6180
+ descriptionZh?: string;
6181
+ columns: string[];
6182
+ }
6183
+ /** The values a row added to an `extensible` table starts from. */
6184
+ interface PluginConfigNewRow {
6185
+ /** The add button's text ("Add preset"). */
6186
+ add?: string;
6187
+ addZh?: string;
6188
+ values: Record<string, string | boolean>;
6189
+ /** A string column's starting text in Chinese. */
6190
+ valuesZh?: Record<string, string>;
6191
+ }
6192
+ /** One choice of an `enum` field. */
6193
+ interface PluginConfigOption {
6194
+ value: string;
6195
+ title: string;
6196
+ titleZh?: string;
6197
+ /** What picking it does: listed, for a table column's option, in a row's "?" (see the row's `description`). */
6198
+ description?: string;
6199
+ descriptionZh?: string;
6200
+ }
6201
+ /** A declared configuration: a titled group of fields, in declaration order. */
6202
+ interface PluginConfiguration {
6203
+ title?: string;
6204
+ titleZh?: string;
6205
+ description?: string;
6206
+ descriptionZh?: string;
6207
+ /**
6208
+ * A `boolean` field that turns the group on (the sandbox's `enabled`). While it is off, as
6209
+ * drafted, the card draws that field alone: no other field, notice or action, and none of the
6210
+ * groups drawn inside it. Turning it on in a group that reports a `backend` it lacks offers
6211
+ * to install one.
6212
+ */
6213
+ switch?: string;
6214
+ properties: Record<string, PluginConfigField>;
6215
+ }
6216
+ /** A line of live status a contributed group reports beside its fields (e.g. that nothing can enforce it). */
6217
+ interface PluginConfigNotice {
6218
+ /**
6219
+ * `progress` = work the group started is still running (an install, a download): the page
6220
+ * reads the groups again every few seconds while any notice says so, and the text is the
6221
+ * step it is on.
6222
+ */
6223
+ tone: "attention" | "muted" | "progress";
6224
+ text: string;
6225
+ textZh?: string;
6226
+ /** More about the notice, disclosed under it on request; lines separated by `\n`. */
6227
+ details?: string;
6228
+ detailsZh?: string;
6229
+ }
6230
+ /** One settings group (GET /api/admin/plugin-config): its schema and its values, secrets masked. */
6231
+ interface PluginConfigEntry {
6232
+ /** The group's name — the id of the contribution that declared it; also the store key. */
6233
+ name: string;
6234
+ configuration: PluginConfiguration;
6235
+ /** Stored values merged onto the defaults; a secret arrives masked (`first4…last4` or `***`), never in the clear. */
6236
+ values: Record<string, unknown>;
6237
+ /** Drawn inside that entry's card and saved with it (a sandbox backend's options inside the sandbox's). */
6238
+ parent?: string;
6239
+ /** Live status beside the fields; absent when there is none. */
6240
+ notices?: PluginConfigNotice[];
6241
+ /** What this group can DO once, on the machine, drawn as buttons beneath its notices. */
6242
+ actions?: PluginConfigActionDecl[];
6243
+ /** Enum options this machine cannot honour now: drawn greyed out with the reason; a save choosing one is refused. */
6244
+ unavailable?: PluginConfigUnavailableDecl[];
6245
+ /**
6246
+ * For a group whose settings a backend plugin enforces (the sandbox): whether one that
6247
+ * applies to this machine's OS is installed, and which packages this OS defaults to. The card
6248
+ * offers to install `recommended` when the group's switch is turned on and none is installed.
6249
+ * Asked per read, like the notices; the client never guesses the OS.
6250
+ */
6251
+ backend?: PluginConfigBackend;
6252
+ }
6253
+ /** Whether a backend that applies to this machine is installed, and this OS's default ones. */
6254
+ interface PluginConfigBackend {
6255
+ /** A backend for this OS is installed: loaded, or installed and failing its check. */
6256
+ installed: boolean;
6257
+ /**
6258
+ * The npm packages this OS defaults to, installed together; absent on an OS with no default
6259
+ * backend. Linux names two: bubblewrap, and sandbox-dsh, which confines files through
6260
+ * Landlock where bubblewrap is refused.
6261
+ */
6262
+ recommended?: string[];
6263
+ }
6264
+ /** One enum option (or boolean position) a settings group cannot honour on this machine, and why. */
6265
+ interface PluginConfigUnavailableDecl {
6266
+ field: string;
6267
+ /** A `table` field's column: the option is unavailable in every cell of that column. */
6268
+ column?: string;
6269
+ /** An enum option, or a boolean field's position: "true" or "false". */
6270
+ value: string;
6271
+ reason: string;
6272
+ reasonZh?: string;
6273
+ }
6274
+ /** One button under a settings group: what it is called, and what pressing it will do. */
6275
+ interface PluginConfigActionDecl {
6276
+ id: string;
6277
+ title: string;
6278
+ titleZh?: string;
6279
+ description?: string;
6280
+ descriptionZh?: string;
6281
+ }
6282
+ /** POST /api/admin/plugin-config/action — what running one reported. */
6283
+ interface PluginConfigActionResponse {
6284
+ ok: boolean;
6285
+ message: string;
6286
+ messageZh?: string;
6287
+ /** The groups as they stand after it ran: a setup that worked changes what the page says. */
6288
+ plugins: PluginConfigEntry[];
6289
+ }
6290
+ interface PluginConfigResponse {
6291
+ plugins: PluginConfigEntry[];
6292
+ }
6293
+ /**
6294
+ * PUT /api/admin/plugin-config — one group's update. Every named field is validated
6295
+ * against its type; an omitted field keeps its stored value; a secret sent as the masked
6296
+ * value keeps the stored one, and `null` or `""` clears any field. 400 `plugin_config_invalid`
6297
+ * (with `field`) on a value that does not fit or a required field left empty; 404
6298
+ * `plugin_config_unknown` for a name no group answers to.
6299
+ */
6300
+ interface PluginConfigUpdateRequest {
6301
+ name: string;
6302
+ values: Record<string, unknown>;
6303
+ }
6304
+ /**
6305
+ * An installed plugin the running build cannot fully run (plugin/unsatisfied.ts): it
6306
+ * contributes to a slot, or wires to a module or interface, that this build does not have.
6307
+ * Also what a refused push names (`plugins_unsatisfied`) and a confirmed one reports.
6308
+ */
6309
+ interface UnsatisfiedPlugin {
6310
+ /** The package specifier, as the installed-plugins list spells it. */
6311
+ specifier: string;
6312
+ /** True when the whole plugin is left out of this generation; false when only the contributions this build has no slot for were dropped and the rest of it runs. */
6313
+ disabled: boolean;
6314
+ /** What this build lacks, in the platform's own words. */
6315
+ reason: string;
6316
+ }
6317
+ /** One plugin a Project lists (GET /api/projects/:projectId/plugins/installed). */
6318
+ interface InstalledPlugin {
6319
+ /** The package specifier as written in the file. */
6320
+ specifier: string;
6321
+ /** Whether the running process holds this package — its modules are in the tree. */
6322
+ active: boolean;
6323
+ /**
6324
+ * Where the package came from: shipped with the build (a hot push's assets, or the
6325
+ * installation's own `plugins/`) rather than fetched from npm. A tag on an installed
6326
+ * plugin — being shipped is not being installed.
6327
+ */
6328
+ builtin: boolean;
6329
+ /** Module names the package declares it adds. */
6330
+ modules: string[];
6331
+ /** Node names the package declares it stands in for. */
6332
+ replaces: string[];
6333
+ /**
6334
+ * Why the package is not running: unresolvable, or a load that
6335
+ * failed (an import that threw, a module name another plugin already took). Only ever
6336
+ * reported for a plugin this server is asked to run (`here`).
6337
+ */
6338
+ error?: string;
6339
+ /**
6340
+ * Present when this build cannot fully run the package (see {@link UnsatisfiedPlugin}).
6341
+ * With `disabled`, `active` is false and a restart would not load it; without, the package
6342
+ * runs minus the contributions this build has no slot for. Only reported for a plugin this
6343
+ * server is asked to run (`here`).
6344
+ */
6345
+ unsatisfied?: Pick<UnsatisfiedPlugin, "disabled" | "reason">;
6346
+ /** Listed in the shared `[plugins]` table: every machine runs it. */
6347
+ everywhere: boolean;
6348
+ /** The machines whose own `[plugins.<machineId>]` table lists it, by machine id. */
6349
+ machines: string[];
6350
+ /**
6351
+ * Whether THIS server is asked to run it — shared, or listed for this server's own id. A
6352
+ * plugin listed only for other machines is neither installed nor loaded here, so `active`
6353
+ * is false and no `error` is reported for it.
6354
+ */
6355
+ here: boolean;
6356
+ }
6357
+ /** What an install put on this server (POST …/plugins/installed and …/installed/archive). */
6358
+ interface PluginInstallOutcome {
6359
+ /** The package name npm installed — for a link, the name its package.json carries. */
6360
+ name: string;
6361
+ version: string | null;
6362
+ /** It carries skills, a hook package or MCP servers: the library lists it, and nothing loads it. */
6363
+ library: boolean;
6364
+ /** It carries server modules: the Project's list loads it. */
6365
+ modules: boolean;
6366
+ /** The zip held the version already installed: nothing was installed. */
6367
+ unchanged?: boolean;
6368
+ }
6369
+ /** POST /api/projects/:projectId/plugins/installed/archive (admin): a plugin package zip, base64. */
6370
+ interface PluginArchiveImportRequest {
6371
+ dataBase64: string;
6372
+ /** Replace another installed version of the package (the 409 `plugin_exists` question). */
6373
+ overwrite?: boolean;
6374
+ }
6375
+ interface InstalledPluginsResponse {
6376
+ plugins: InstalledPlugin[];
6377
+ /** After an install: what landed on this server. */
6378
+ installed?: PluginInstallOutcome;
6379
+ /**
6380
+ * Specifiers this build SHIPS (the hot push's assets, or the installation's own
6381
+ * `plugins/`): installable without a download, and not installed until listed.
6382
+ */
6383
+ shipped: string[];
6384
+ /** The file the list lives in, named for the page that explains where to edit it by hand. */
6385
+ file: string;
6386
+ /** This server's own machine id — the key of its `[plugins.<machineId>]` table. */
6387
+ machineId: string;
6388
+ /** A listed plugin neither runs, nor failed to load, nor is one this build cannot run: the App could not be re-assembled around it (the previous one was restored), so a restart is what applies it. */
6389
+ restartPending: boolean;
6390
+ }
6391
+ /** The browser an agent drives: the desktop's built-in one, or the user's own Chrome. */
6392
+ type BrowserBackend = "builtin" | "chrome";
6393
+ /**
6394
+ * What a backend's link can do. The shell cannot create tabs (the Web App's <webview> does) and
6395
+ * throttles and measures its guests; the extension creates, closes and focuses tabs itself and
6396
+ * has no cookie store the server may touch.
6397
+ */
6398
+ interface BrowserLinkCapabilities {
6399
+ /** `open-tab`, `close-tab` and `activate-tab` are answered (chrome). */
6400
+ createsTabs: boolean;
6401
+ /** `throttle` and the `metrics` events (builtin). */
6402
+ throttles: boolean;
6403
+ /** `set-cookies` and `clear-data` (builtin). */
6404
+ cookieStore: boolean;
6405
+ }
6406
+ /**
6407
+ * One tab of the agent browser. Built-in: a guest page, `id` its webContents id. Chrome: a tab
6408
+ * the extension drives (one it created in the Penguin tab group, or one the user added), `id`
6409
+ * Chrome's tab id; `canGoBack`/`canGoForward` are always false there (the tabs API cannot read
6410
+ * them).
6411
+ */
6412
+ interface BuiltinBrowserTab {
6413
+ id: number;
6414
+ /** The built-in tab belongs to this conversation; absent for pages opened outside a conversation. */
6415
+ sessionId?: string;
6416
+ /** Electron storage partition, shared only within this tab's Project. */
6417
+ partition?: string;
6418
+ url: string;
6419
+ title: string;
6420
+ loading: boolean;
6421
+ canGoBack: boolean;
6422
+ canGoForward: boolean;
6423
+ favicon?: string;
6424
+ /**
6425
+ * Present while the page's renderer is gone, with Electron's reason (`crashed`, `oom`,
6426
+ * `killed`, …): the panel shows the crash until the page is reloaded, and an agent action on
6427
+ * the tab answers 409 `tab_crashed`.
6428
+ */
6429
+ crashed?: string;
6430
+ }
6431
+ /**
6432
+ * Why the agent browser cannot be driven. Built-in: not under the desktop shell, a shell too old
6433
+ * to host it, or no app window to host a new tab. Chrome: this user has no paired extension, it
6434
+ * is paired but not connected now, or an admin switched Chrome connections off server-wide.
6435
+ */
6436
+ type BuiltinBrowserUnavailableReason = "not_desktop" | "shell_unsupported" | "no_window" | "extension_not_paired" | "extension_disconnected" | "extension_disabled";
6437
+ /** One backend as GET /status lists it for the caller. */
6438
+ interface BrowserBackendInfo {
6439
+ backend: BrowserBackend;
6440
+ available: boolean;
6441
+ reason?: BuiltinBrowserUnavailableReason;
6442
+ /** chrome: the caller's connected extension, else the one seen most recently; absent when none is paired. */
6443
+ extension?: {
6444
+ id: string;
6445
+ name: string;
6446
+ version: string;
6447
+ connected: boolean;
6448
+ lastSeenAt: string | null;
6449
+ };
6450
+ }
6451
+ /**
6452
+ * GET /api/builtin-browser/status: the caller's effective backend (`backend`) — whether it can
6453
+ * be driven now, its tabs — and every backend this server offers the caller (`backends`).
6454
+ */
6455
+ interface BuiltinBrowserStatus {
6456
+ /** Whether the effective backend can be driven now. */
6457
+ available: boolean;
6458
+ reason?: BuiltinBrowserUnavailableReason;
6459
+ backend: BrowserBackend;
6460
+ backends: BrowserBackendInfo[];
6461
+ tabs: BuiltinBrowserTab[];
6462
+ activeTabId: number | null;
6463
+ /** The shell's latest measurement of the built-in browser's load; absent before its first one, and on chrome. */
6464
+ metrics?: BuiltinBrowserMetrics;
6465
+ }
6466
+ /** GET / PUT /api/builtin-browser/backend: the caller's backend and the ones they may choose. */
6467
+ interface BrowserBackendResponse {
6468
+ backend: BrowserBackend;
6469
+ choices: BrowserBackend[];
6470
+ }
6471
+ /** One Chrome paired to the caller's account (a row of `browser_extensions`). */
6472
+ interface BrowserExtensionRecord {
6473
+ id: string;
6474
+ /** As the extension names itself, e.g. "Chrome 130 on macOS". */
6475
+ name: string;
6476
+ version: string;
6477
+ createdAt: string;
6478
+ lastSeenAt: string | null;
6479
+ /** Whether this one holds the caller's connection now (one at a time per user). */
6480
+ connected: boolean;
6481
+ }
6482
+ /** GET /api/builtin-browser/extension. */
6483
+ interface BrowserExtensionsResponse {
6484
+ paired: BrowserExtensionRecord[];
6485
+ /** The id of the connected one, when one is. */
6486
+ connected?: string;
6487
+ /** The admin's server-wide switch (`ServerSettings.browserExtensionsEnabled`). */
6488
+ enabled: boolean;
6489
+ }
6490
+ /**
6491
+ * POST /api/builtin-browser/extension/pairings: a one-time code for the signed-in user, valid
6492
+ * for ten minutes, used once; the dialog shows it beside the server address.
6493
+ */
6494
+ interface BrowserExtensionPairingResponse {
6495
+ /** 43 base64url characters. */
6496
+ code: string;
6497
+ /** ISO time the code stops working. */
6498
+ expiresAt: string;
6499
+ /** The request's Origin, when it carried one: the address the browser reaches this server at. */
6500
+ origin: string | null;
6501
+ }
6502
+ /** POST /api/builtin-browser/extension/pair (no cookie: the extension's own request). */
6503
+ interface BrowserExtensionPairRequest {
6504
+ code: string;
6505
+ /** How the extension names this Chrome, e.g. "Chrome 130 on macOS" (1–80 characters). */
6506
+ name: string;
6507
+ /** The extension's own version (manifest.version). */
6508
+ version: string;
6509
+ }
6510
+ /** What /extension/pair answers: the long-lived token, shown once and stored only hashed here. */
6511
+ interface BrowserExtensionPairResponse {
6512
+ extensionId: string;
6513
+ /** Rides the WebSocket's subprotocol list as `token.<token>`; never a URL. */
6514
+ token: string;
6515
+ /** This server's install id (a stable name for the data root); null when it is unknown. */
6516
+ installId: string | null;
6517
+ user: {
6518
+ userId: string;
6519
+ displayName: string | null;
6520
+ };
6521
+ serverVersion: string;
6522
+ }
6523
+ /**
6524
+ * The extension WebSocket's close codes (`/api/builtin-browser/extension/ws`). Besides these the
6525
+ * server closes with 1012 when it restarts or hot-swaps (reconnect with the usual backoff).
6526
+ *
6527
+ * - 4001 replaced: another extension of the same user connected (last connected wins); do not
6528
+ * auto-retry.
6529
+ * - 4003 revoked: the pairing was revoked (or the token was); forget this server.
6530
+ * - 4005 protocol_mismatch: no `penguin-browser.1` subprotocol, or a hello the server does not
6531
+ * speak; the extension needs updating.
6532
+ * - 4008 ping_timeout: two pings (or the hello) went unanswered.
6533
+ * - 4009 disabled: an admin switched Chrome connections off; retry hourly.
6534
+ */
6535
+ type BrowserExtensionCloseCode = 4001 | 4003 | 4005 | 4008 | 4009;
6536
+ /** One tab's share of the built-in browser's load, as the shell last measured it. */
6537
+ interface BuiltinBrowserTabMetrics {
6538
+ tabId: number;
6539
+ /** The memory held by the processes behind the page (its own and its cross-site frames'), KB. */
6540
+ memoryKB: number;
6541
+ /** Their CPU use since the previous measurement, in percent of one core. */
6542
+ cpuPercent: number;
6543
+ }
6544
+ /** Why the built-in browser should be lighter. */
6545
+ type BuiltinBrowserLoadWarning =
6546
+ /** Its pages together hold more memory than they should. */
6547
+ "memory"
6548
+ /** This computer is running out of memory. */
6549
+ | "low_system_memory"
6550
+ /** More tabs are open than the browser should hold. */
6551
+ | "many_tabs";
6552
+ /**
6553
+ * The built-in browser's load: GET /status's `metrics`, and the `builtin_browser_metrics` event
6554
+ * after each of the shell's measurements (every ~10 s while tabs are open, and soon after one
6555
+ * closes). The thresholds are the server's; the UI and the CLI only word what it concluded.
6556
+ */
6557
+ interface BuiltinBrowserMetrics {
6558
+ /** When the server received the measurement (epoch ms). */
6559
+ at: number;
6560
+ tabs: BuiltinBrowserTabMetrics[];
6561
+ /** All the pages' memory, a process two tabs share counted once, KB. */
6562
+ totalKB: number;
6563
+ /**
6564
+ * This computer's memory, KB. Absent on macOS, whose free count leaves out the memory it would
6565
+ * readily hand back, so that "free" there reads as nearly none on a healthy machine.
6566
+ */
6567
+ system?: {
6568
+ freeKB: number;
6569
+ totalKB: number;
6570
+ };
6571
+ /** What to warn about now; empty when all is well. */
6572
+ warnings: BuiltinBrowserLoadWarning[];
6573
+ /** While a memory warning stands: the tabs holding the most memory, heaviest first. */
6574
+ heavyTabIds: number[];
6575
+ }
6576
+ /** GET /api/builtin-browser/tabs. */
6577
+ interface BuiltinBrowserTabsResponse {
6578
+ tabs: BuiltinBrowserTab[];
6579
+ activeTabId: number | null;
6580
+ }
6581
+ /** POST /api/builtin-browser/tabs/:tab/scan — GenericAgent's web_scan. */
6582
+ interface BuiltinBrowserScanResult {
6583
+ tab: BuiltinBrowserTab;
6584
+ tabs: BuiltinBrowserTab[];
6585
+ activeTabId: number | null;
6586
+ /** Simplified HTML, or plain text with `textOnly`. */
6587
+ content?: string;
6588
+ truncated?: boolean;
6589
+ }
6590
+ /** POST /api/builtin-browser/tabs/:tab/exec (and click / type) — GenericAgent's web_execute_js. */
6591
+ interface BuiltinBrowserExecResult {
6592
+ status: "success" | "failed";
6593
+ tabId: number;
6594
+ /** The script's JSON-safe return value. */
6595
+ value?: unknown;
6596
+ error?: string;
6597
+ /** The page navigated or reloaded while the script ran. */
6598
+ reloaded?: boolean;
6599
+ /** Tabs opened while the call ran (popups, target=_blank). */
6600
+ newTabs?: {
6601
+ id: number;
6602
+ url: string;
6603
+ }[];
6604
+ /** Text that appeared during the call and may be gone again (toasts, flashes). */
6605
+ transients?: string[];
6606
+ diff?: {
6607
+ changed: number;
6608
+ topChange?: string;
6609
+ };
6610
+ suggestion?: string;
6611
+ /** click only: where the trusted click landed. */
6612
+ clicked?: {
6613
+ x: number;
6614
+ y: number;
6615
+ tag?: string;
6616
+ text?: string;
6617
+ };
6618
+ /** The page's dialogs the call answered, in order (see BuiltinBrowserDialog). */
6619
+ dialogs?: BuiltinBrowserDialog[];
6620
+ }
6621
+ /**
6622
+ * A dialog the page opened during an exec, click or type, which the call answered so the page
6623
+ * would not block: an alert is accepted, and a confirm, prompt or leave-page dialog dismissed
6624
+ * unless the request said `acceptDialogs`.
6625
+ */
6626
+ interface BuiltinBrowserDialog {
6627
+ type: "alert" | "confirm" | "prompt" | "beforeunload";
6628
+ message: string;
6629
+ accepted: boolean;
6630
+ }
6631
+ /** POST /api/builtin-browser/tabs/:tab/screenshot. */
6632
+ interface BuiltinBrowserScreenshot {
6633
+ mime: "image/png";
6634
+ /** Base64. */
6635
+ data: string;
6636
+ }
6637
+ type BuiltinBrowserImportBrowser = "chrome" | "edge" | "brave" | "chromium" | "vivaldi" | "opera" | "arc" | "firefox";
6638
+ /** One profile of a system browser that can be imported from. */
6639
+ interface BuiltinBrowserImportSource {
6640
+ /** `<browser>:<profile dir>`, e.g. `chrome:Default`. */
6641
+ id: string;
6642
+ browser: BuiltinBrowserImportBrowser;
6643
+ browserName: string;
6644
+ /** The profile's directory name. */
6645
+ profile: string;
6646
+ /** The profile's display name. */
6647
+ profileName: string;
6648
+ hasCookies: boolean;
6649
+ hasHistory: boolean;
6650
+ }
6651
+ interface BuiltinBrowserImportSourcesResponse {
6652
+ sources: BuiltinBrowserImportSource[];
6653
+ }
6654
+ /** POST /api/builtin-browser/import. */
6655
+ interface BuiltinBrowserImportRequest {
6656
+ sessionId?: string;
6657
+ projectId?: string;
6658
+ sourceId: string;
6659
+ cookies?: boolean;
6660
+ history?: boolean;
6661
+ /** Only cookies of these sites (a domain matches itself and its subdomains). */
6662
+ domains?: string[];
6663
+ }
6664
+ interface BuiltinBrowserImportResult {
6665
+ sourceId: string;
6666
+ cookies?: {
6667
+ found: number;
6668
+ imported: number;
6669
+ skipped: number;
6670
+ failed: number;
6671
+ };
6672
+ history?: {
6673
+ found: number;
6674
+ imported: number;
6675
+ };
6676
+ warnings: string[];
6677
+ }
6678
+ interface BuiltinBrowserHistoryEntry {
6679
+ url: string;
6680
+ title: string;
6681
+ visitCount: number;
6682
+ /** Epoch ms. */
6683
+ lastVisitAt: number;
6684
+ /** `builtin` for pages visited in the built-in browser, else the browser it was imported from. */
6685
+ source: string;
6686
+ }
6687
+ interface BuiltinBrowserHistoryResponse {
6688
+ entries: BuiltinBrowserHistoryEntry[];
6689
+ }
6690
+ /**
6691
+ * GET / PUT /api/builtin-browser/settings: the browser's own settings, a file of the server's
6692
+ * that is read and written without the desktop shell. PUT takes the whole object.
6693
+ */
6694
+ interface BuiltinBrowserSettings {
6695
+ /**
6696
+ * The page a new tab opens when it is given no address — the panel's "+", an agent's new tab
6697
+ * — and the toolbar's Home button goes to: an http(s) address, or null for none (a new tab is
6698
+ * then blank). PUT takes a bare host too and answers the address as stored.
6699
+ */
6700
+ homepage: string | null;
6701
+ }
6702
+ type BuiltinBrowserAction = "navigate" | "scan" | "exec" | "click" | "type" | "screenshot" | "cdp";
6703
+ /**
6704
+ * User-channel events of the agent browser. The built-in backend's go to every admin; a chrome
6705
+ * backend's go to the user whose Chrome it is, as do `builtin_browser_backend` and
6706
+ * `builtin_browser_extension`.
6707
+ */
6708
+ type BuiltinBrowserServerEvent =
6709
+ /**
6710
+ * A backend's whole tab list. `backend` names whose: a desktop admin hears both the built-in
6711
+ * browser's and their own Chrome's, and the two lists must not overwrite each other.
6712
+ */
6713
+ {
6714
+ type: "builtin_browser_tabs";
6715
+ tabs: BuiltinBrowserTab[];
6716
+ activeTabId: number | null;
6717
+ backend: BrowserBackend;
6718
+ }
6719
+ /** Create a guest for `url`, then POST /tabs/claim with `requestId` once it has a webContents id. */
6720
+ | {
6721
+ type: "builtin_browser_open";
6722
+ partition?: string;
6723
+ requestId: string;
6724
+ url: string;
6725
+ activate: boolean;
6726
+ openerTabId?: number;
6727
+ sessionId?: string;
6728
+ } | {
6729
+ type: "builtin_browser_close";
6730
+ tabId: number;
6731
+ }
6732
+ /** A draft or healed conversation acquired its permanent id. */
6733
+ | {
6734
+ type: "builtin_browser_session";
6735
+ fromSessionId: string;
6736
+ sessionId: string;
6737
+ } | {
6738
+ type: "builtin_browser_activity";
6739
+ tabId: number;
6740
+ busy: boolean;
6741
+ action: BuiltinBrowserAction;
6742
+ sessionId?: string;
6743
+ }
6744
+ /** The shell measured the browser's load (see BuiltinBrowserMetrics). */
6745
+ | {
6746
+ type: "builtin_browser_metrics";
6747
+ metrics: BuiltinBrowserMetrics;
6748
+ }
6749
+ /** The user switched backend (PUT /backend); every window of theirs follows. */
6750
+ | {
6751
+ type: "builtin_browser_backend";
6752
+ backend: BrowserBackend;
6753
+ }
6754
+ /** The user's Chrome connected, went away, was replaced by another Chrome of theirs, or was revoked. */
6755
+ | {
6756
+ type: "builtin_browser_extension";
6757
+ state: "connected" | "disconnected" | "replaced" | "revoked";
6758
+ extension?: BrowserExtensionRecord;
6759
+ };
6760
+ /** A cookie as the shell writes it (Electron's CookiesSetDetails). */
6761
+ interface DesktopBrowserCookie {
6762
+ url: string;
6763
+ name: string;
6764
+ value: string;
6765
+ domain?: string;
6766
+ path?: string;
6767
+ secure?: boolean;
6768
+ httpOnly?: boolean;
6769
+ /** Unix seconds; absent = session cookie. */
6770
+ expirationDate?: number;
6771
+ sameSite?: "unspecified" | "no_restriction" | "lax" | "strict";
6772
+ }
6773
+ /**
6774
+ * Server → browser link: what the shell (built-in) or the extension (chrome) does with its tabs.
6775
+ * Mechanism only — the product logic stays on the server. The names are historical: the same
6776
+ * envelopes travel the extension's WebSocket as JSON text frames.
6777
+ */
6778
+ type DesktopBrowserCommand =
6779
+ /** Reply: a BrowserHello. An older shell never answers. */
6780
+ {
6781
+ op: "hello";
6782
+ }
6783
+ /** Reply: `{ tabs: BuiltinBrowserTab[] }`. */
6784
+ | {
6785
+ op: "tabs";
6786
+ }
6787
+ /**
6788
+ * Reply: the CDP method's result object. `events` names the tab's CDP events the shell relays
6789
+ * from then on, as `cdp-event`s: it replaces the list before (empty relays none), and is set
6790
+ * before the command runs, so an event the command itself causes is not missed. Without it
6791
+ * the list stays as it is.
6792
+ */
6793
+ | {
6794
+ op: "cdp";
6795
+ tabId: number;
6796
+ method: string;
6797
+ params?: Record<string, unknown>;
6798
+ events?: string[];
6799
+ }
6800
+ /** Reply: `{ set: number; failed: number; errors: string[] }` (at most 10 errors). */
6801
+ | {
6802
+ op: "set-cookies";
6803
+ cookies: DesktopBrowserCookie[];
6804
+ partition?: string;
6805
+ }
6806
+ /** Reply: `{}`. */
6807
+ | {
6808
+ op: "clear-data";
6809
+ storages: ("cookies" | "cache" | "storage")[];
6810
+ partition?: string;
6811
+ }
6812
+ /**
6813
+ * Reply: `{}`. The tabs whose pages may be throttled while out of sight (Electron's background
6814
+ * throttling, applied at once); every other tab runs at full speed. It replaces the list
6815
+ * before. A tab is unthrottled until a command lists it, so a server that never sends one
6816
+ * changes nothing. A shell older than this command answers `unknown_op`.
6817
+ */
6818
+ | {
6819
+ op: "throttle";
6820
+ tabIds: number[];
6821
+ }
6822
+ /**
6823
+ * Chrome only (the shell answers `unknown_op`). Reply: `{ tab: BuiltinBrowserTab }`. A tab at
6824
+ * `url` (http(s) or about:blank, checked by the server) in the extension's Penguin tab group;
6825
+ * `activate` asks for it to be the selected tab of its window.
6826
+ */
6827
+ | {
6828
+ op: "open-tab";
6829
+ url: string;
6830
+ activate: boolean;
6831
+ }
6832
+ /** Chrome only. Reply: `{}`. Closes a tab the extension drives. */
6833
+ | {
6834
+ op: "close-tab";
6835
+ tabId: number;
6836
+ }
6837
+ /** Chrome only. Reply: `{}`. Shows the tab in the user's Chrome (selects it, focuses its window). */
6838
+ | {
6839
+ op: "activate-tab";
6840
+ tabId: number;
6841
+ }
6842
+ /** Chrome only. Reply: `{}`. Sent every 20 s; the traffic keeps the MV3 service worker alive. */
6843
+ | {
6844
+ op: "ping";
6845
+ };
6846
+ /**
6847
+ * The `hello` reply. Built-in: `{ version: 1, partition }` (an older shell sends no `backend`).
6848
+ * Chrome: `{ version: 1, backend: "chrome", extension }`; any other version is closed 4005.
6849
+ */
6850
+ interface BrowserHello {
6851
+ version: 1;
6852
+ backend?: BrowserBackend;
6853
+ partition?: string;
6854
+ extension?: {
6855
+ version: string;
6856
+ chrome: string;
6857
+ name: string;
6858
+ };
6859
+ }
6860
+ interface DesktopBrowserCommandMessage {
6861
+ type: "desktop-browser-command";
6862
+ id: string;
6863
+ command: DesktopBrowserCommand;
6864
+ }
6865
+ interface DesktopBrowserReplyMessage {
6866
+ type: "desktop-browser-reply";
6867
+ id: string;
6868
+ ok: boolean;
6869
+ result?: unknown;
6870
+ error?: string;
6871
+ }
6872
+ type DesktopBrowserEvent = {
6873
+ kind: "tab";
6874
+ tab: BuiltinBrowserTab;
6875
+ } | {
6876
+ kind: "tab-closed";
6877
+ tabId: number;
6878
+ }
6879
+ /**
6880
+ * window.open / target=_blank inside a guest (the shell denied it and asks for a tab instead),
6881
+ * or the context menu's "Open link in new tab". `background`: the tab should open behind the
6882
+ * current one, as a middle-click or that menu entry does in Chrome.
6883
+ */
6884
+ | {
6885
+ kind: "open-request";
6886
+ url: string;
6887
+ openerTabId: number;
6888
+ background?: boolean;
6889
+ }
6890
+ /** One of the tab's CDP events a `cdp` command's `events` asked for. */
6891
+ | {
6892
+ kind: "cdp-event";
6893
+ tabId: number;
6894
+ method: string;
6895
+ params: Record<string, unknown>;
6896
+ }
6897
+ /**
6898
+ * The tab's renderer went away (Electron's render-process-gone reason and exit code). The tab
6899
+ * stays; its `tab` events carry `crashed` until the page is reloaded, and the shell refuses
6900
+ * CDP commands for it meanwhile (`tab_crashed`).
6901
+ */
6902
+ | {
6903
+ kind: "tab-crashed";
6904
+ tabId: number;
6905
+ reason: string;
6906
+ exitCode: number;
6907
+ }
6908
+ /**
6909
+ * The shell's measurement of its guests (every ~10 s while there are any, and once more
6910
+ * shortly after one closes, empty after the last): each tab's memory and CPU, and the pages'
6911
+ * memory together, a process shared by two tabs counted once.
6912
+ */
6913
+ | {
6914
+ kind: "metrics";
6915
+ tabs: BuiltinBrowserTabMetrics[];
6916
+ totalKB: number;
6917
+ }
6918
+ /**
6919
+ * Chrome only: the tab still exists in Chrome but the agent may no longer drive it — the user
6920
+ * pressed Cancel on Chrome's debugging bar (`user`), the debugger went away otherwise
6921
+ * (`detached`), or the page is one Chrome lets no extension attach to (`restricted`). The
6922
+ * extension fails that tab's pending commands with the error `tab_released`.
6923
+ */
6924
+ | {
6925
+ kind: "tab-released";
6926
+ tabId: number;
6927
+ reason: "user" | "detached" | "restricted";
6928
+ };
6929
+ interface DesktopBrowserEventMessage {
6930
+ type: "desktop-browser-event";
6931
+ event: DesktopBrowserEvent;
6932
+ }
4324
6933
 
4325
- export { type AdminPasswordResetRequest, type AdminUserCreateRequest, type AdminUserCreateResponse, type AdminUsersResponse, type AgentCompactionConfigDto, type AgentConfigDto, type AgentConfigResponse, type AgentConfigUpdateRequest, type AgentCreateRequest, type AgentCreateResponse, type AgentHooksConfigDto, type AgentHooksResponse, type AgentImportRequest, type AgentImportResponse, type AgentKernelUpdateResponse, type AgentMemoryConfigDto, type AgentModelConfigDto, type AgentPluginsInstallResponse, type AgentSchedulesConfigDto, type AgentSkillsConfigDto, type AgentSkillsResponse, type AgentSummary, type AgentTraceDateGroup, type AgentTraceFileRef, type AgentTraceSessionEntry, type AgentTraceSessionFile, type AgentTraceSessionGroup, type AgentTracesResponse, type AgentVaultConfigDto, type AgentsResponse, type ApprovalDecisionRequest, type ApprovalMode, type AuthLoginRequest, type AuthResponse, type BenchmarkCaseScore, type BenchmarkCaseSummary, type BenchmarkCasesResponse, type BenchmarkCreateCase, type BenchmarkCreateRequest, type BenchmarkCreateResponse, type BenchmarkEvaluation, type BenchmarkRunScore, type BenchmarkStatus, type BenchmarkSummary, type BenchmarksResponse, type CaseMaterial, type ChatDefaultsDto, type CommandPolicyDto, type CommandPolicyRuleDto, type CompanyServerEvent, type ContextFileShare, type ContextToolShare, type ContributionsResponse, type CredentialInfo, type DefaultModelResponse, type DefaultModelUpdateRequest, type DesktopTrayCommandMessage, type DesktopTrayLocale, type DesktopTrayPatch, type DesktopTrayStatus, type DesktopTrayStatusMessage, type DesktopTrayStatusResponse, type DesktopUpdateStatus, type DesktopUpdateStatusResponse, type DesktopUpdaterCommandMessage, type DesktopUpdaterStatusMessage, type DirEntryInfo, type DirListResponse, type DirectorySkillItem, type DirectorySkillsResponse, type DraftShortcut, type EndpointModelListRequest, type EndpointModelListResponse, type ErrorBody, type FeishuBindingInfo, type FeishuBindingPutRequest, type FeishuBindingResponse, type FeishuTestRequest, type FeishuTestResponse, type FilesMoveRequest, type FilesStatRequest, type FilesStatResponse, type FilesWriteRequest, type GoalResponse, type GoalServerEvent, type GoalStateView, type HistoryMessage, type HookArchiveInstallRequest, type HookItem, type InstallResponse, type MachineInfo, type MachineJob, type MachineServerStatus, type MachinesResponse, type McpServerTestResponse, type MeResponse, type MemberAddRequest, type MemberAddResponse, type MemberInfo, type MembersResponse, type MemoryFileInfo, type MemoryFileResponse, type MemoryFilesResponse, type MemoryImportMode, type MemoryImportRequest, type MemoryImportResponse, type MemoryOverviewResponse, type MemoryScopeExport, type MemoryScopeInfo, type MemoryTransferFile, type MessagesLiveTail, type MessagesPageInfo, type MessagesResponse, type MessagingBindingCommon, type MessagingBindingInfo, type MessagingBindingStateRequest, type MessagingBindingsResponse, type MessagingChannel, type MessagingChannelState, type MessagingDeliveryError, type MessagingDeliveryPatch, type MessagingRuntimeState, type MessagingRuntimeStatus, type MessagingTestMessageResponse, type ModelInfo, type ModelOAuthCodeRequest, type ModelOAuthCodeResponse, type ModelOAuthErrorCode, type ModelOAuthMode, type ModelOAuthStartRequest, type ModelOAuthStartResponse, type ModelOAuthStatusResponse, type ModelPricingDto, type ModelProtocolDetectRequest, type ModelProtocolDetectResponse, type ModelProtocolProbeDto, type ModelRefDto, type ModelTestRequest, type ModelTestResponse, type ModelUpdateEntry, type ModelVisionDetectRequest, type ModelVisionDetectResponse, type ModelVisionOutcome, type ModelsResponse, type ModelsUpdateRequest, type OrgApprovalMode, type OrgBudgetAlert, type OrgCalendarItem, type OrgCalendarOutcome, type OrgCalendarResponse, type OrgCalendarUpsertRequest, type OrgCalendarWriteResponse, type OrgChannelCreateRequest, type OrgChannelDetail, type OrgChannelItem, type OrgChannelMember, type OrgChannelMemberRequest, type OrgChannelMessage, type OrgChannelMessageSendRequest, type OrgChannelMessagesResponse, type OrgChannelNotice, type OrgChannelNoticeKind, type OrgChannelPatchRequest, type OrgChannelReadRequest, type OrgChannelsResponse, type OrgChartResponse, type OrgDeskItem, type OrgDeskResponse, type OrgEmployeeItem, type OrgEmployeePatchRequest, type OrgEmployeeState, type OrgFinanceEmployee, type OrgFinanceResponse, type OrgFinanceTicket, type OrgHandbookFile, type OrgHandbookFileResponse, type OrgHandbookFileWriteRequest, type OrgHandbookFilesResponse, type OrgHandbookResponse, type OrgHireRequest, type OrgInbox, type OrgLanguage, type OrgSessionsResponse, type OrgSpendSummary, type OrgStatus, type OrgTicketAttachRequest, type OrgTicketBlockRequest, type OrgTicketChange, type OrgTicketCreateRequest, type OrgTicketDetail, type OrgTicketHistoryAction, type OrgTicketHistoryEntry, type OrgTicketItem, type OrgTicketMoveRequest, type OrgTicketPriority, type OrgTicketProgressRequest, type OrgTicketSessionItem, type OrgTicketStartRequest, type OrgTicketStartResponse, type OrgTicketStatus, type OrgTicketUpdateRequest, type OrgTicketsResponse, type OrgTriggerKind, type OrganizationCreateRequest, type OrganizationDetail, type OrganizationPatchRequest, type OrganizationSettings, type OrganizationSummary, type OrganizationsResponse, type PasswordChangeRequest, type PendingFollowUpInfo, type PendingSteeringInfo, type PluginFilesResponse, type PluginGroupItem, type PluginInstallRequest, type PluginItem, type PluginLibraryResponse, type PluginUpdateRef, type PrefsResponse, type ProjectCreateRequest, type ProjectCreateResponse, type ProjectRole, type ProjectSummary, type ProjectUpdateRequest, type ProjectUpdateResponse, type ProjectsResponse, type ProtocolProbeOutcome, type ProxyProbeDto, type ProxyProbeOutcome, type ProxyProbeProvider, type ProxyProbeResponse, type ProxyProbeTargetDto, type ProxyProbeTargetsResponse, type QQBindingInfo, type QQBindingPutRequest, type QQBindingResponse, type QQScanPollResponse, type QQScanStartResponse, type QQTestRequest, type QQTestResponse, type RecalledMessageResponse, type RendererRef, type RequestSpan, type RestartResponse, type RetryNowResponse, type ScheduleItem, type ScheduleServerEvent, type ScheduleStatus, type ScheduleUpsertRequest, type SchedulesResponse, type SemanticIdSuggestReason, type SemanticIdSuggestRequest, type SemanticIdSuggestResponse, type ServerEvent, type ServerSettings, type ServerSettingsResponse, type ServerSettingsUpdateRequest, type SessionBackgroundTasks, type SessionCategory, type SessionCategoryCounts, type SessionContextParts, type SessionContextResponse, type SessionCreateRequest, type SessionCreateResponse, type SessionForkRequest, type SessionForkResponse, type SessionInfo, type SessionPatchRequest, type SessionProcessInfo, type SessionProcessesResponse, type SessionResponse, type SessionSource, type SessionStatus, type SessionTracesResponse, type SessionsResponse, type SkillArchiveInstallRequest, type SkillMetadataItem, type SteerRequest, type SubagentMessageResponse, type SubagentRuntimeInfo, TICKET_SLUG_PATTERN, type TaskCreateRequest, type TaskCreateResponse, type TaskInputPart, type TelegramBindingInfo, type TelegramBindingPutRequest, type TelegramBindingResponse, type TelegramTestRequest, type TelegramTestResponse, type ToolCallSpan, type TraceAnalysisResponse, type TraceEventsResponse, type TraceFileInfo, type TraceImportRequest, type TraceImportResponse, type TraceModelSegment, type TraceOtherSpan, type TracePosition, type TraceTaskStats, type TraceToolSpan, type UiPrefs, type UpdateCheckResponse, type UpdateJobPhase, type UpdateJobStatus, type UpdateProfileRequest, type UpdateProfileResponse, type UpdateRunResponse, type UploadLimits, type UsageAgentSeries, type UsageBucket, type UsageEntitySeriesCounts, type UsageErrorCount, type UsageErrorItem, type UsageErrorKind, type UsageErrors, type UsageErrorsClearResponse, type UsageErrorsPage, type UsageGranularity, type UsageGroupBy, type UsageGroupRow, type UsageModelSeries, type UsageModelTotal, type UsageModelTotals, type UsageResponse, type UsageSeriesPoint, type UsageTrendPointInTrace, type UserInfo, type VaultEntryInfo, type VaultEntryUpdate, type VaultResponse, type VaultUpdateRequest, type VersionResponse, type WeChatBindingInfo, type WeChatBindingPutRequest, type WeChatBindingResponse, type WeChatScanPollResponse, type WeChatScanStartResponse, type WeChatScanStatus, type WeChatScanVerifyRequest, type WeChatTestResponse, type WebContribution, type WorkspaceFileEntry, type WorkspaceFilesResponse, type WorkspaceSearchHit, type WorkspaceSearchResponse };
6934
+ export { type AdminPasswordResetRequest, type AdminUserCreateRequest, type AdminUserCreateResponse, type AdminUsersResponse, type AgentApiKeyCreateRequest, type AgentApiKeyCreateResponse, type AgentApiKeyInfo, type AgentApiResponse, type AgentApiSettings, type AgentApiUpdateRequest, type AgentCompactionConfigDto, type AgentConfigDto, type AgentConfigResponse, type AgentConfigUpdateRequest, type AgentCreateRequest, type AgentCreateResponse, type AgentHooksConfigDto, type AgentHooksResponse, type AgentImportRequest, type AgentImportResponse, type AgentKernelUpdateResponse, type AgentMcpServerItem, type AgentMcpServersResponse, type AgentMemoryConfigDto, type AgentModelConfigDto, type AgentPluginsInstallResponse, type AgentSchedulesConfigDto, type AgentSkillsConfigDto, type AgentSkillsResponse, type AgentSummary, type AgentTraceDateGroup, type AgentTraceFileRef, type AgentTraceSessionEntry, type AgentTraceSessionFile, type AgentTraceSessionGroup, type AgentTracesResponse, type AgentVaultConfigDto, type AgentsResponse, type ApprovalDecisionRequest, type ApprovalMode, type ArchivedSessionGroup, type ArchivedSessionGroupsResponse, type ArchivedSessionSource, type ArchivedSessionsBatch, type ArchivedSessionsBatchFilter, type ArchivedSessionsBatchResponse, type ArchivedSessionsQuery, type ArchivedSessionsResponse, type ArchivedSessionsRowsQuery, type AuthLoginRequest, type AuthResponse, type BenchmarkArchiveGitOrigin, type BenchmarkArchiveImportRequest, type BenchmarkArchiveImportResponse, type BenchmarkCaseScore, type BenchmarkCaseSummary, type BenchmarkCasesResponse, type BenchmarkCreateCase, type BenchmarkCreateRequest, type BenchmarkCreateResponse, type BenchmarkEvaluation, type BenchmarkManifestProblem, type BenchmarkOrigin, type BenchmarkOriginKind, type BenchmarkRunScore, type BenchmarkStatus, type BenchmarkSummary, type BenchmarksResponse, type BrowserBackend, type BrowserBackendInfo, type BrowserBackendResponse, type BrowserExtensionCloseCode, type BrowserExtensionPairRequest, type BrowserExtensionPairResponse, type BrowserExtensionPairingResponse, type BrowserExtensionRecord, type BrowserExtensionsResponse, type BrowserHello, type BrowserLinkCapabilities, type BuiltinBrowserAction, type BuiltinBrowserDialog, type BuiltinBrowserExecResult, type BuiltinBrowserHistoryEntry, type BuiltinBrowserHistoryResponse, type BuiltinBrowserImportBrowser, type BuiltinBrowserImportRequest, type BuiltinBrowserImportResult, type BuiltinBrowserImportSource, type BuiltinBrowserImportSourcesResponse, type BuiltinBrowserLoadWarning, type BuiltinBrowserMetrics, type BuiltinBrowserScanResult, type BuiltinBrowserScreenshot, type BuiltinBrowserServerEvent, type BuiltinBrowserSettings, type BuiltinBrowserStatus, type BuiltinBrowserTab, type BuiltinBrowserTabMetrics, type BuiltinBrowserTabsResponse, type BuiltinBrowserUnavailableReason, type CaseMaterial, type ChatDefaultsDto, type CommandPolicyDto, type CommandPolicyRuleDto, type CompanyServerEvent, type ConnectionSourceDto, type ContextFileShare, type ContextToolShare, type ContributionsResponse, type CostParts, type CredentialInfo, type DefaultModelResponse, type DefaultModelUpdateRequest, type DesktopBrowserCommand, type DesktopBrowserCommandMessage, type DesktopBrowserCookie, type DesktopBrowserEvent, type DesktopBrowserEventMessage, type DesktopBrowserReplyMessage, type DesktopFolderAccessMessage, type DesktopFolderAccessResult, type DesktopFolderAccessResultMessage, type DesktopOpenPrivacySettingsMessage, type DesktopPrivacyPane, type DesktopTrayCommandMessage, type DesktopTrayLocale, type DesktopTrayPatch, type DesktopTrayStatus, type DesktopTrayStatusMessage, type DesktopTrayStatusResponse, type DesktopUpdateStatus, type DesktopUpdateStatusResponse, type DesktopUpdaterCommandMessage, type DesktopUpdaterStatusMessage, type DirAccessResponse, type DirEntryInfo, type DirListResponse, type DirLocation, type DirStandardFolders, type DirectorySkillItem, type DirectorySkillsResponse, type DraftShortcut, type Employment, type EndpointModelListRequest, type EndpointModelListResponse, type ErrorBody, type ErrorDetails, type FeishuBindingInfo, type FeishuBindingPutRequest, type FeishuBindingResponse, type FeishuTestRequest, type FeishuTestResponse, type FilesCreateRequest, type FilesMoveRequest, type FilesStatRequest, type FilesStatResponse, type FilesWriteRequest, type GoalResponse, type GoalServerEvent, type GoalStateView, type HistoryMessage, type HookArchiveInstallRequest, type HookItem, type InstallResponse, type InstalledPlugin, type InstalledPluginsResponse, type LanguageIndexResponse, type LanguageSummary, MACHINE_PHASES, type MachineCheck, type MachineConfigSync, type MachineDiagnosis, type MachineEnvOnly, type MachineInfo, type MachineJob, type MachinePhase, type MachineReconnect, type MachineServerEvent, type MachineServerStatus, type MachineSkippedModel, type MachineSourceReach, type MachineSshFailure, type MachineUninstallRequest, type MachineUseRefusal, type MachinesAddRequest, type MachinesCheckoutImage, type MachinesResponse, type MachinesStopUsingRequest, type MachinesUseRequest, type MachinesUseResponse, type McpServerTestResponse, type McpTransport, type MeResponse, type MemberAddRequest, type MemberAddResponse, type MemberInfo, type MembersResponse, type MemoryFileInfo, type MemoryFileResponse, type MemoryFilesResponse, type MemoryImportMode, type MemoryImportRequest, type MemoryImportResponse, type MemoryOverviewResponse, type MemoryScopeExport, type MemoryScopeInfo, type MemoryTransferFile, type MessagesLiveTail, type MessagesPageInfo, type MessagesResponse, type MessagingBindingCommon, type MessagingBindingInfo, type MessagingBindingStateRequest, type MessagingBindingsResponse, type MessagingChannel, type MessagingChannelState, type MessagingDeliveryError, type MessagingDeliveryPatch, type MessagingRuntimeState, type MessagingRuntimeStatus, type MessagingTestMessageResponse, type ModelBalanceAmount, type ModelBalanceErrorCode, type ModelBalanceFailure, type ModelBalanceReading, type ModelBalanceResponse, type ModelEffectiveConnection, type ModelInfo, type ModelOAuthCodeRequest, type ModelOAuthCodeResponse, type ModelOAuthErrorCode, type ModelOAuthMode, type ModelOAuthStartRequest, type ModelOAuthStartResponse, type ModelOAuthStatusResponse, type ModelPricingDto, type ModelProtocolDetectRequest, type ModelProtocolDetectResponse, type ModelProtocolProbeDto, type ModelRefDto, type ModelScopeAuthFlowErrorCode, type ModelScopeAuthFlowStatusResponse, type ModelScopeAuthStartResponse, type ModelTestRequest, type ModelTestResponse, type ModelUpdateEntry, type ModelVisionDetectRequest, type ModelVisionDetectResponse, type ModelVisionOutcome, type ModelsResponse, type ModelsUpdateRequest, type OrgApprovalMode, type OrgBudgetAlert, type OrgCalendarItem, type OrgCalendarOutcome, type OrgCalendarResponse, type OrgCalendarUpsertRequest, type OrgCalendarWriteResponse, type OrgChannelCreateRequest, type OrgChannelDetail, type OrgChannelItem, type OrgChannelMember, type OrgChannelMemberRequest, type OrgChannelMessage, type OrgChannelMessageSendRequest, type OrgChannelMessagesResponse, type OrgChannelNotice, type OrgChannelNoticeKind, type OrgChannelPatchRequest, type OrgChannelReadRequest, type OrgChannelsResponse, type OrgChartResponse, type OrgDeskItem, type OrgDeskResponse, type OrgEmployeeItem, type OrgEmployeePatchRequest, type OrgEmployeeState, type OrgFinanceEmployee, type OrgFinanceResponse, type OrgFinanceTicket, type OrgHandbookFile, type OrgHandbookFileResponse, type OrgHandbookFileWriteRequest, type OrgHandbookFilesResponse, type OrgHandbookResponse, type OrgHireRequest, type OrgInbox, type OrgLanguage, type OrgSessionsResponse, type OrgSpendSummary, type OrgStatus, type OrgTicketAttachRequest, type OrgTicketBlockRequest, type OrgTicketChange, type OrgTicketCreateRequest, type OrgTicketDetail, type OrgTicketHistoryAction, type OrgTicketHistoryEntry, type OrgTicketItem, type OrgTicketMoveRequest, type OrgTicketPriority, type OrgTicketProgressRequest, type OrgTicketSessionItem, type OrgTicketStartRequest, type OrgTicketStartResponse, type OrgTicketStatus, type OrgTicketUpdateRequest, type OrgTicketsResponse, type OrgTriggerKind, type OrganizationCreateRequest, type OrganizationDetail, type OrganizationPatchRequest, type OrganizationSettings, type OrganizationSummary, type OrganizationsResponse, type PasswordChangeRequest, type PendingFollowUpInfo, type PendingSteeringInfo, type PinnedBalance, type PlatformAuthFlowErrorCode, type PlatformAuthFlowStatusResponse, type PlatformAuthStartResponse, type PlatformModelSyncResponse, type PluginArchiveImportRequest, type PluginConfigActionDecl, type PluginConfigActionResponse, type PluginConfigBackend, type PluginConfigColumnGroup, type PluginConfigEntry, type PluginConfigField, type PluginConfigNewRow, type PluginConfigNotice, type PluginConfigOption, type PluginConfigPinColumn, type PluginConfigResponse, type PluginConfigRowChoice, type PluginConfigTableColumn, type PluginConfigTableRow, type PluginConfigUnavailableDecl, type PluginConfigUpdateRequest, type PluginConfiguration, type PluginFilesResponse, type PluginGroupItem, type PluginIndexEntry, type PluginIndexResponse, type PluginInstallOutcome, type PluginInstallRequest, type PluginItem, type PluginLibraryResponse, type PluginMcpServerItem, type PluginReadmeResponse, type PluginSetupKeyItem, type PluginUpdateRef, type PrefsResponse, type PresetSyncMode, type PresetSyncRequest, type PresetSyncResponse, type ProjectCreateRequest, type ProjectCreateResponse, type ProjectRole, type ProjectScheduleItem, type ProjectSchedulesResponse, type ProjectSummary, type ProjectUpdateRequest, type ProjectUpdateResponse, type ProjectsResponse, type ProtocolProbeOutcome, type ProviderConnectionDto, type ProviderConnectionUpdate, type ProxyProbeDto, type ProxyProbeOutcome, type ProxyProbeProvider, type ProxyProbeResponse, type ProxyProbeTargetDto, type ProxyProbeTargetsResponse, type QQBindingInfo, type QQBindingPutRequest, type QQBindingResponse, type QQScanPollResponse, type QQScanStartResponse, type QQTestRequest, type QQTestResponse, type QuickStartItem, type RecalledMessageResponse, type RendererRef, type RequestSpan, type RestartResponse, type RetryNowResponse, type ScheduleItem, type ScheduleServerEvent, type ScheduleStatus, type ScheduleUpsertRequest, type SchedulesResponse, type SemanticIdKind, type SemanticIdSuggestReason, type SemanticIdSuggestRequest, type SemanticIdSuggestResponse, type ServerEvent, type ServerSettings, type ServerSettingsResponse, type ServerSettingsUpdateRequest, type SessionBackgroundTasks, type SessionBatchPageRequest, type SessionBatchResult, type SessionCategory, type SessionCategoryCounts, type SessionContextParts, type SessionContextResponse, type SessionCreateRequest, type SessionCreateResponse, type SessionForkRequest, type SessionForkResponse, type SessionInfo, type SessionPatchRequest, type SessionProcessInfo, type SessionProcessesResponse, type SessionResponse, type SessionSandbox, type SessionSandboxMode, type SessionSandboxNetwork, type SessionSandboxPreset, type SessionStatus, type SessionSwitchModelRequest, type SessionTracesResponse, type SessionsBatchRequest, type SessionsBatchResponse, type SessionsResponse, type SkillArchiveInstallRequest, type SkillMetadataItem, type SshHostRequest, type SshHostResponse, type SshHostSummary, type SshHostsResponse, type SteerRequest, type StoredKeybindings, type SubagentMessageResponse, type SubagentRuntimeInfo, TICKET_SLUG_PATTERN, type TaskCreateRequest, type TaskCreateResponse, type TaskInputPart, type TelegramBindingInfo, type TelegramBindingPutRequest, type TelegramBindingResponse, type TelegramTestRequest, type TelegramTestResponse, type ToolCallSpan, type TraceAnalysisResponse, type TraceEventsResponse, type TraceFileInfo, type TraceImportRequest, type TraceImportResponse, type TraceModelSegment, type TraceOtherSpan, type TracePosition, type TraceTaskStats, type TraceToolSpan, type UiPrefs, type UnavailableSandboxBackend, type UnsatisfiedPlugin, type UpdateCheckResponse, type UpdateJobPhase, type UpdateJobStatus, type UpdateProfileRequest, type UpdateProfileResponse, type UpdateRunResponse, type UploadLimits, type UsageAgentSeries, type UsageBucket, type UsageEntitySeriesCounts, type UsageErrorCount, type UsageErrorItem, type UsageErrorKind, type UsageErrors, type UsageErrorsClearResponse, type UsageErrorsPage, type UsageGranularity, type UsageGroupBy, type UsageGroupRow, type UsageModelSeries, type UsageModelTotal, type UsageModelTotals, type UsageResponse, type UsageSeriesPoint, type UsageTrendPointInTrace, type UserInfo, type VaultEntryInfo, type VaultEntryUpdate, type VaultResponse, type VaultUpdateRequest, type VersionHistoryDiffResponse, type VersionHistoryResponse, type VersionResponse, type VersionRollbackResponse, type WeChatBindingInfo, type WeChatBindingPutRequest, type WeChatBindingResponse, type WeChatScanPollResponse, type WeChatScanStartResponse, type WeChatScanStatus, type WeChatScanVerifyRequest, type WeChatTestResponse, type WebContribution, type WorkbenchInfo, type WorkbenchRequest, type WorkbenchResponse, type WorkbenchVersion, type WorkspaceFileEntry, type WorkspaceFilesResponse, type WorkspaceSearchHit, type WorkspaceSearchResponse };