@hienlh/ppm 0.17.16 → 0.17.18

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 (464) hide show
  1. package/CHANGELOG.md +19 -0
  2. package/assets/skills/ppm/SKILL.md +1 -1
  3. package/assets/skills/ppm/references/cli-reference.md +4 -4
  4. package/assets/skills/ppm/references/http-api.md +5 -1
  5. package/dist/web/assets/{ai-resource-editor-CsossqDO.js → ai-resource-editor-BgWg-ty8.js} +1 -1
  6. package/dist/web/assets/{angular-html-CkxY2iBQ.js → angular-html-2g8YGiJY.js} +1 -1
  7. package/dist/web/assets/{angular-ts-BePeGkkC.js → angular-ts-BwSmoLfh.js} +1 -1
  8. package/dist/web/assets/{api-settings-DAk7D-NP.js → api-settings-BJTjIG4U.js} +1 -1
  9. package/dist/web/assets/{api-themes-84MT-XqR.js → api-themes-DuLpbQFV.js} +1 -1
  10. package/dist/web/assets/{apl-DBS7PWvB.js → apl-B44SMKHp.js} +1 -1
  11. package/dist/web/assets/architecture-PBZL5I3N-gLlhsjM9.js +1 -0
  12. package/dist/web/assets/{astro-Clbk1k4h.js → astro-E5620wek.js} +1 -1
  13. package/dist/web/assets/{audio-preview-DIkokvPM.js → audio-preview-CLn1LTxc.js} +1 -1
  14. package/dist/web/assets/{blade-BeBsBuGS.js → blade-D-VQWAHB.js} +1 -1
  15. package/dist/web/assets/{bsl-DYigT3JZ.js → bsl-Bq5qFzpU.js} +1 -1
  16. package/dist/web/assets/{cairo-BjpO78wI.js → cairo-Cn9-ZKRa.js} +1 -1
  17. package/dist/web/assets/chat-tab-tTUVo4G3.js +13 -0
  18. package/dist/web/assets/{cobol-CpuUx69s.js → cobol-IM3E3iCH.js} +1 -1
  19. package/dist/web/assets/code-editor-DMfUjdk5.js +10 -0
  20. package/dist/web/assets/{coffee-FoFd2Cxo.js → coffee-24wG7ekn.js} +1 -1
  21. package/dist/web/assets/{conflict-editor-Dqm1bkyL.js → conflict-editor-BosR4PW3.js} +3 -3
  22. package/dist/web/assets/{cpp-CO_nPfdr.js → cpp-DH9Cqt4g.js} +1 -1
  23. package/dist/web/assets/{crystal-D-UygAAF.js → crystal-4nZYHhdi.js} +1 -1
  24. package/dist/web/assets/{csv-preview-CvP4xzug.js → csv-preview-BqszurHp.js} +1 -1
  25. package/dist/web/assets/data-grid-overlay-editor-DxdOKRAY.js +1 -0
  26. package/dist/web/assets/data-grid-types-CzEQU9PJ.js +1 -0
  27. package/dist/web/assets/database-viewer-CiomoOU6.js +1 -0
  28. package/dist/web/assets/dialog-BpkAWOy8.js +1 -0
  29. package/dist/web/assets/{diff-viewer-BfWixe_z.js → diff-viewer-aOrOaGWx.js} +1 -1
  30. package/dist/web/assets/dist-Bn3E6I4R.js +11 -0
  31. package/dist/web/assets/{docx-preview-CguUoTKe.js → docx-preview-BAzpJdua.js} +1 -1
  32. package/dist/web/assets/{edge-BSzxzX11.js → edge-DTI9d2Qi.js} +1 -1
  33. package/dist/web/assets/{elixir-DH1-Mrep.js → elixir-Ys0wioho.js} +1 -1
  34. package/dist/web/assets/{elm-B0ln5J70.js → elm-Cihc9TAz.js} +1 -1
  35. package/dist/web/assets/{erb-GIz6NnlZ.js → erb-BwYBWK2-.js} +1 -1
  36. package/dist/web/assets/{erlang-B8MgYhib.js → erlang-CzlD_Joo.js} +1 -1
  37. package/dist/web/assets/esm-JLkfN0k-.js +2 -0
  38. package/dist/web/assets/{extension-webview-DTpFt1eL.js → extension-webview-CKUUvgOr.js} +1 -1
  39. package/dist/web/assets/{fortran-fixed-form-D_-TrK6U.js → fortran-fixed-form-CT90EtSp.js} +1 -1
  40. package/dist/web/assets/{fsharp-C9cLCk2b.js → fsharp-LasxezlU.js} +1 -1
  41. package/dist/web/assets/{gdresource-CsZ3lXwY.js → gdresource-XqgPdfiE.js} +1 -1
  42. package/dist/web/assets/{git-commit-xmq0KqrA.js → git-commit-CZBgPolw.js} +1 -1
  43. package/dist/web/assets/{git-log-panel-DUX7N5tN.js → git-log-panel-B6GFWhj4.js} +1 -1
  44. package/dist/web/assets/{git-rebase-D3O-jsSX.js → git-rebase-C2VzKDB1.js} +1 -1
  45. package/dist/web/assets/gitGraph-HDMCJU4V-BKGq--Jw.js +1 -0
  46. package/dist/web/assets/glide-data-grid-z9FPvRBG.js +138 -0
  47. package/dist/web/assets/{glimmer-js-g74gESrC.js → glimmer-js-DfjWcOLD.js} +1 -1
  48. package/dist/web/assets/{glimmer-ts-DF4aDWHT.js → glimmer-ts-DBldQATc.js} +1 -1
  49. package/dist/web/assets/{glsl-D5LmfW7l.js → glsl-CzNv1NPP.js} +1 -1
  50. package/dist/web/assets/{graphql-_KI4RuXL.js → graphql-DDyLtQb4.js} +1 -1
  51. package/dist/web/assets/group-chat-tab-Bu31mwmF.js +1 -0
  52. package/dist/web/assets/{hack-B1NaTC39.js → hack-CtoYNYkC.js} +1 -1
  53. package/dist/web/assets/{haml-DDFXadZ4.js → haml-CnBVap7G.js} +1 -1
  54. package/dist/web/assets/{handlebars-Bp-Jq3K9.js → handlebars-Dg51esUH.js} +1 -1
  55. package/dist/web/assets/{html-DyB9_5Tj.js → html-Ec2_evSy.js} +1 -1
  56. package/dist/web/assets/{html-derivative-CC60wi-3.js → html-derivative-Cy-F7ZqL.js} +1 -1
  57. package/dist/web/assets/{http-C-9qngH4.js → http-C236v8Tg.js} +1 -1
  58. package/dist/web/assets/{hurl-BtxgpNDs.js → hurl-0rMD2gPU.js} +1 -1
  59. package/dist/web/assets/{hxml-DOoOS9op.js → hxml-e5uS5jTD.js} +1 -1
  60. package/dist/web/assets/{image-preview-CZg6FQ3w.js → image-preview-CaX4gpN6.js} +1 -1
  61. package/dist/web/assets/index-4M24PSHW.js +50 -0
  62. package/dist/web/assets/index-BYFXiFsa.css +2 -0
  63. package/dist/web/assets/info-3K5VOQVL-BOhGriBF.js +1 -0
  64. package/dist/web/assets/{jinja-cN-150ao.js → jinja-DybFmhfq.js} +1 -1
  65. package/dist/web/assets/{jison-CqjsqPq5.js → jison-hkHbixdZ.js} +1 -1
  66. package/dist/web/assets/{julia-C-I5m8W4.js → julia-QnxY6sRE.js} +1 -1
  67. package/dist/web/assets/{just-CgsSW9zs.js → just-1Rs-nm8g.js} +1 -1
  68. package/dist/web/assets/keybindings-store-Cv-U85V6.js +1 -0
  69. package/dist/web/assets/{latex-BFzR9N2d.js → latex-jEBQqA7u.js} +1 -1
  70. package/dist/web/assets/{liquid-BWfr-_iC.js → liquid-DfJaZpxG.js} +1 -1
  71. package/dist/web/assets/{lua-DsXVun6g.js → lua-ti0Jr6-C.js} +1 -1
  72. package/dist/web/assets/{markdown-renderer-CiQkvWP_.js → markdown-renderer-BNXJygz9.js} +3 -3
  73. package/dist/web/assets/markdown-renderer-DCsEEdo7.js +1 -0
  74. package/dist/web/assets/{marko-DWBbo09n.js → marko-DNj4AhPg.js} +1 -1
  75. package/dist/web/assets/{mdc-CUMe9LOG.js → mdc-DPFwQFe4.js} +1 -1
  76. package/dist/web/assets/mobile-bottom-sheet--5_YEewV.js +1 -0
  77. package/dist/web/assets/{nextflow-CoYbADOm.js → nextflow-BUrFV5aB.js} +1 -1
  78. package/dist/web/assets/{nginx-z-JRZREw.js → nginx-BrW9NdOb.js} +1 -1
  79. package/dist/web/assets/{nim-Ci4LHyBm.js → nim-DS_6PF2p.js} +1 -1
  80. package/dist/web/assets/notification-store-DADt1gJH.js +1 -0
  81. package/dist/web/assets/{number-overlay-editor-lnnuUpPI.js → number-overlay-editor-CReXWiQ1.js} +1 -1
  82. package/dist/web/assets/packet-RMMSAZCW-CdLFnL8a.js +1 -0
  83. package/dist/web/assets/{panel-store-D6RV_ztx.js → panel-store-DnbT1c8L.js} +1 -1
  84. package/dist/web/assets/{pdf-preview-B6ZqANpw.js → pdf-preview-DZzbm9A4.js} +1 -1
  85. package/dist/web/assets/{perl-pbDimKOO.js → perl-ytm9vKO_.js} +1 -1
  86. package/dist/web/assets/{php-B003E5v7.js → php-DWTD1Usw.js} +1 -1
  87. package/dist/web/assets/pie-UPGHQEXC-COC-h0zz.js +1 -0
  88. package/dist/web/assets/{trash-2-CbbD5QF1.js → plus-DcuJuDC3.js} +1 -1
  89. package/dist/web/assets/postgres-viewer-BxpeiVkt.js +3 -0
  90. package/dist/web/assets/{project-store-Cekcjcom.js → project-store-Sbnqc41N.js} +1 -1
  91. package/dist/web/assets/{pug-AoueAWJo.js → pug-Bt7KL3_r.js} +1 -1
  92. package/dist/web/assets/{qml-CepxioQA.js → qml-Dexdon5J.js} +1 -1
  93. package/dist/web/assets/radar-KQ55EAFF-CiCfdJNL.js +1 -0
  94. package/dist/web/assets/{razor-lIvU-Gu-.js → razor-DnYCSl0G.js} +1 -1
  95. package/dist/web/assets/{rst-Dn-dwElE.js → rst-BEFYzwF8.js} +1 -1
  96. package/dist/web/assets/{ruby-DmsD1Iz5.js → ruby-BPaXFEjN.js} +1 -1
  97. package/dist/web/assets/{sas-6IM2P4YP.js → sas-CywiNBp8.js} +1 -1
  98. package/dist/web/assets/{scss-RgYEZQqh.js → scss-CGk0L6hN.js} +1 -1
  99. package/dist/web/assets/{settings-store-BkSf4SPQ.js → settings-store-DkRZhj-g.js} +2 -2
  100. package/dist/web/assets/settings-tab-DJOhpW09.js +1 -0
  101. package/dist/web/assets/{shaderlab-B0MNEwt4.js → shaderlab-Byjk0kH7.js} +1 -1
  102. package/dist/web/assets/{shellsession-F8S4LrQI.js → shellsession-DbA70QB6.js} +1 -1
  103. package/dist/web/assets/{soy-DDlMbX7u.js → soy-Bm97kWiA.js} +1 -1
  104. package/dist/web/assets/sparkles-B3DmG8PJ.js +1 -0
  105. package/dist/web/assets/{sparql-kthmGAAe.js → sparql-CawLnniT.js} +1 -1
  106. package/dist/web/assets/{sql-query-editor-BYNM0RRl.js → sql-query-editor-9TwIPvRj.js} +1 -1
  107. package/dist/web/assets/sqlite-viewer-D-3i_x28.js +1 -0
  108. package/dist/web/assets/{stata-Tfbew8La.js → stata-RifEDtNO.js} +1 -1
  109. package/dist/web/assets/{surrealql-C0LIGqRx.js → surrealql-DGRQa5Lm.js} +1 -1
  110. package/dist/web/assets/{svelte-CfTy8CGF.js → svelte-w9Hb12aM.js} +1 -1
  111. package/dist/web/assets/system-monitor-tab-CuVVp2wt.js +1 -0
  112. package/dist/web/assets/{tab-store-CcLQfu8H.js → tab-store-vn0viMOP.js} +1 -1
  113. package/dist/web/assets/{templ-V5H1Sbfy.js → templ-BpUwjJXV.js} +1 -1
  114. package/dist/web/assets/{terminal-tab-CK3lX-0Y.js → terminal-tab-DrSqSrdX.js} +2 -2
  115. package/dist/web/assets/{tex-C3H3uMZf.js → tex-DJFSsuTD.js} +1 -1
  116. package/dist/web/assets/tool-cards-CtE_JnF-.js +7 -0
  117. package/dist/web/assets/treemap-KZPCXAKY-CZwuSCtA.js +1 -0
  118. package/dist/web/assets/{ts-tags-7AXgG5YS.js → ts-tags-vp9Qvx2O.js} +1 -1
  119. package/dist/web/assets/{twig-BSyS38T3.js → twig-CoazxjRB.js} +1 -1
  120. package/dist/web/assets/{use-blob-url-CpyRr7Cx.js → use-blob-url-DdXgVqfz.js} +1 -1
  121. package/dist/web/assets/use-is-mobile-CE6AamG6.js +1 -0
  122. package/dist/web/assets/{use-monaco-theme-Jj2OxkAE.js → use-monaco-theme-BvzqffqR.js} +1 -1
  123. package/dist/web/assets/{vendor-mermaid-DGOXKrJC.js → vendor-mermaid-DPpYkrMf.js} +193 -193
  124. package/dist/web/assets/{video-preview-BOHxkwKG.js → video-preview-DxdyVATR.js} +1 -1
  125. package/dist/web/assets/{vue-C4d_br9u.js → vue-CnshsiXd.js} +1 -1
  126. package/dist/web/assets/{vue-html-CGdoz4vW.js → vue-html-CZshynFp.js} +1 -1
  127. package/dist/web/assets/{vue-vine-CqNolQlN.js → vue-vine-C5gtp_T1.js} +1 -1
  128. package/dist/web/assets/{xml-UmCmu-Od.js → xml-BEGJasgj.js} +1 -1
  129. package/dist/web/assets/{xsl-DJG6USGh.js → xsl-DXpRLjYk.js} +1 -1
  130. package/dist/web/index.html +22 -20
  131. package/dist/web/monacoeditorwork/css.worker.bundle.js +122 -122
  132. package/dist/web/monacoeditorwork/editor.worker.bundle.js +78 -78
  133. package/dist/web/monacoeditorwork/html.worker.bundle.js +110 -110
  134. package/dist/web/monacoeditorwork/json.worker.bundle.js +108 -108
  135. package/dist/web/monacoeditorwork/ts.worker.bundle.js +81 -81
  136. package/dist/web/sw.js +1 -1
  137. package/docs/journal/binary-self-upgrade-260724.md +66 -0
  138. package/nul/docs/claude-agent-sdk-reference.md +780 -0
  139. package/nul/docs/code-standards.md +1080 -0
  140. package/nul/docs/codebase-summary.md +916 -0
  141. package/nul/docs/deployment-guide.md +702 -0
  142. package/nul/docs/design-guidelines.md +767 -0
  143. package/nul/docs/extension-development-guide.md +629 -0
  144. package/nul/docs/journals/260414-1400-ext-git-graph-port-complete.md +147 -0
  145. package/nul/docs/journals/260414-1452-git-graph-faithful-port.md +144 -0
  146. package/nul/docs/journals/260414-1810-git-graph-ui-improvements-complete.md +261 -0
  147. package/nul/docs/journals/260414-2001-bundled-extensions.md +219 -0
  148. package/nul/docs/journals/260415-frontend-memory-optimization.md +73 -0
  149. package/nul/docs/lessons-learned.md +70 -0
  150. package/nul/docs/project-changelog.md +574 -0
  151. package/nul/docs/project-overview-pdr.md +205 -0
  152. package/nul/docs/project-roadmap.md +222 -0
  153. package/nul/docs/system-architecture.md +1961 -0
  154. package/package.json +1 -1
  155. package/packages/vscode-compat/node_modules/.bin/tsc.bunx +0 -0
  156. package/packages/vscode-compat/node_modules/.bin/tsc.exe +0 -0
  157. package/packages/vscode-compat/node_modules/.bin/tsserver.bunx +0 -0
  158. package/packages/vscode-compat/node_modules/.bin/tsserver.exe +0 -0
  159. package/spike-group-channel.mjs +156 -0
  160. package/spike-group-chat.mjs +181 -0
  161. package/src/server/routes/group-chat.ts +118 -9
  162. package/src/services/group-chat/agent-runner.ts +15 -2
  163. package/src/services/group-chat/context-window.ts +15 -0
  164. package/src/services/group-chat/group-chat.service.ts +178 -79
  165. package/src/services/group-chat/group-chat.store.ts +30 -0
  166. package/src/services/group-chat/responder-router.ts +110 -0
  167. package/src/services/group-chat/transcript-archive.ts +18 -0
  168. package/src/services/group-chat/turn-engine.ts +183 -105
  169. package/src/services/jsonl-transcript-parser.ts +36 -0
  170. package/src/types/group-chat-ws.ts +2 -2
  171. package/src/types/group-chat.ts +33 -4
  172. package/src/web/components/group-chat/group-chat-tab.tsx +109 -14
  173. package/src/web/components/group-chat/group-create-dialog.tsx +10 -3
  174. package/src/web/components/group-chat/group-full-transcript-view.tsx +9 -8
  175. package/src/web/components/group-chat/group-member-edit-dialog.tsx +108 -0
  176. package/src/web/components/group-chat/group-member-roster.tsx +51 -23
  177. package/src/web/components/group-chat/group-message-item.tsx +45 -26
  178. package/src/web/components/group-chat/group-transcript-messages.tsx +126 -0
  179. package/src/web/components/layout/cloud-share-popover.tsx +10 -2
  180. package/src/web/components/layout/mobile-drawer-tab-bar.tsx +10 -0
  181. package/src/web/components/layout/mobile-drawer.tsx +26 -9
  182. package/src/web/components/layout/nav-section-rail.tsx +8 -2
  183. package/src/web/components/layout/upgrade-button.tsx +33 -11
  184. package/src/web/hooks/use-group-chat.ts +8 -1
  185. package/src/web/lib/api-group-chat.ts +47 -4
  186. package/src/web/lib/sidebar-tabs/tab-registry.ts +3 -1
  187. package/dist/web/assets/architecture-PBZL5I3N-NAS99xy0.js +0 -1
  188. package/dist/web/assets/chat-tab-Bh6l253v.js +0 -18
  189. package/dist/web/assets/click-outside-container-DXZVmhaX.js +0 -1
  190. package/dist/web/assets/code-editor-B7F-d4DK.js +0 -10
  191. package/dist/web/assets/data-grid-overlay-editor-CUlFsD-D.js +0 -1
  192. package/dist/web/assets/database-viewer-5oNTPgjF.js +0 -1
  193. package/dist/web/assets/dist-BsziT-tR.js +0 -11
  194. package/dist/web/assets/esm-CjVeOTNQ.js +0 -2
  195. package/dist/web/assets/gitGraph-HDMCJU4V-B4I3L4Sf.js +0 -1
  196. package/dist/web/assets/glide-data-grid-7vB1SNne.js +0 -138
  197. package/dist/web/assets/group-chat-tab-CNECY-zg.js +0 -1
  198. package/dist/web/assets/index-D2G6kmTs.js +0 -50
  199. package/dist/web/assets/index-DCmrZnq0.css +0 -2
  200. package/dist/web/assets/info-3K5VOQVL-CBbbOe3C.js +0 -1
  201. package/dist/web/assets/input-24Nf_edC.js +0 -1
  202. package/dist/web/assets/keybindings-store-D8Cppv4w.js +0 -1
  203. package/dist/web/assets/notification-store-DOGxkWih.js +0 -1
  204. package/dist/web/assets/packet-RMMSAZCW-fr8-QSTF.js +0 -1
  205. package/dist/web/assets/pie-UPGHQEXC-Biu7_Pbb.js +0 -1
  206. package/dist/web/assets/postgres-viewer-B23xTCSs.js +0 -3
  207. package/dist/web/assets/radar-KQ55EAFF-CMtNRpK6.js +0 -1
  208. package/dist/web/assets/settings-tab-D5hAA1bY.js +0 -1
  209. package/dist/web/assets/sparkles-2ii4lR7b.js +0 -1
  210. package/dist/web/assets/sqlite-viewer-DW9f2Hqj.js +0 -1
  211. package/dist/web/assets/system-monitor-tab-B5f4pgdX.js +0 -1
  212. package/dist/web/assets/treemap-KZPCXAKY-ZYVFjoaN.js +0 -1
  213. package/dist/web/assets/use-websocket-DrDBgIWU.js +0 -1
  214. /package/dist/web/assets/{abap-BZ3J5hF3.js → abap-wT6fgqwv.js} +0 -0
  215. /package/dist/web/assets/{actionscript-3-CkyGfT-8.js → actionscript-3-CqcHpPYj.js} +0 -0
  216. /package/dist/web/assets/{ada-CxNLBoe7.js → ada-DWE3Hn4E.js} +0 -0
  217. /package/dist/web/assets/{andromeeda-BPN5rsDz.js → andromeeda-DpQ-y-Jt.js} +0 -0
  218. /package/dist/web/assets/{apache-C1F5wHL6.js → apache-LRTUP4r3.js} +0 -0
  219. /package/dist/web/assets/{apex-BaNixdO-.js → apex-DwVeH5_h.js} +0 -0
  220. /package/dist/web/assets/{api-client-Dvzcc_EO.js → api-client-BK4NPNoY.js} +0 -0
  221. /package/dist/web/assets/{applescript-Cl_pdcch.js → applescript-DyQiO9JU.js} +0 -0
  222. /package/dist/web/assets/{apply-theme-ORrEdHKT.js → apply-theme-DxKsrDLA.js} +0 -0
  223. /package/dist/web/assets/{ara-B6UAE7M2.js → ara-Cf-OWd2S.js} +0 -0
  224. /package/dist/web/assets/{asciidoc-D9c9Twki.js → asciidoc-gocbKcVK.js} +0 -0
  225. /package/dist/web/assets/{asm-CNmtv7Y9.js → asm-C3meb5HY.js} +0 -0
  226. /package/dist/web/assets/{aurora-x-C9z-NyzR.js → aurora-x-BYcXaCIg.js} +0 -0
  227. /package/dist/web/assets/{awk-BfLHEnKa.js → awk-BcT6nIdN.js} +0 -0
  228. /package/dist/web/assets/{ayu-dark-Bn7trkt_.js → ayu-dark-CLZPoccg.js} +0 -0
  229. /package/dist/web/assets/{ayu-light-Dpya9hPf.js → ayu-light-CTyRQdS2.js} +0 -0
  230. /package/dist/web/assets/{ayu-mirage-Cc0jfABD.js → ayu-mirage-DjqfqZdo.js} +0 -0
  231. /package/dist/web/assets/{ballerina-BtiCmN7T.js → ballerina-BCARR-zK.js} +0 -0
  232. /package/dist/web/assets/{bat-tmFM76zR.js → bat-_yaRjWOb.js} +0 -0
  233. /package/dist/web/assets/{beancount-BjPqRrcH.js → beancount-BPESBMS9.js} +0 -0
  234. /package/dist/web/assets/{berry-D0SQBxN2.js → berry-BSLE-Sf8.js} +0 -0
  235. /package/dist/web/assets/{bibtex-CkjRWQKr.js → bibtex-CvUWWCVx.js} +0 -0
  236. /package/dist/web/assets/{bicep-Bhq28Cxc.js → bicep--vcqqALZ.js} +0 -0
  237. /package/dist/web/assets/{bird2-DFGInYb3.js → bird2-Clclqgh6.js} +0 -0
  238. /package/dist/web/assets/{c-CP8cB4k_.js → c-CSC2LwTp.js} +0 -0
  239. /package/dist/web/assets/{c3-oRytyeWo.js → c3-Cl1_jUnm.js} +0 -0
  240. /package/dist/web/assets/{cadence-Inf1rGb7.js → cadence-BwAD7vSC.js} +0 -0
  241. /package/dist/web/assets/{catppuccin-frappe-DAzribQo.js → catppuccin-frappe-hr0HKoFX.js} +0 -0
  242. /package/dist/web/assets/{catppuccin-latte-B6Y_mCCQ.js → catppuccin-latte-DXEIYlFO.js} +0 -0
  243. /package/dist/web/assets/{catppuccin-macchiato-COLbYqVL.js → catppuccin-macchiato-B2zST7sA.js} +0 -0
  244. /package/dist/web/assets/{catppuccin-mocha-DKWaML1B.js → catppuccin-mocha-CvrdnYv9.js} +0 -0
  245. /package/dist/web/assets/{chevron-right-BBc6ATtm.js → chevron-right-DeUue2aK.js} +0 -0
  246. /package/dist/web/assets/{circle-question-mark-CveWaCux.js → circle-question-mark-PaJgx-3d.js} +0 -0
  247. /package/dist/web/assets/{clarity-_ZLLrrsX.js → clarity-BeQd5TQD.js} +0 -0
  248. /package/dist/web/assets/{clojure-DWGVCg_P.js → clojure-Csdo0vb_.js} +0 -0
  249. /package/dist/web/assets/{cmake-Cwh3mug7.js → cmake-Cf0q7ddg.js} +0 -0
  250. /package/dist/web/assets/{code-7ZAlTvsm.js → code-TekEnDFM.js} +0 -0
  251. /package/dist/web/assets/{codeowners-BdV6CFYi.js → codeowners-CUEzlC7R.js} +0 -0
  252. /package/dist/web/assets/{codeql-NvupDIDf.js → codeql-DxHyogQt.js} +0 -0
  253. /package/dist/web/assets/{common-lisp-B-vtBh5k.js → common-lisp-WLb-tAda.js} +0 -0
  254. /package/dist/web/assets/{coq-M8-FTfiZ.js → coq-DFtMD6Wn.js} +0 -0
  255. /package/dist/web/assets/{csharp-B7Ugdtd-.js → csharp-DEnvXVjs.js} +0 -0
  256. /package/dist/web/assets/{css-86k3Z-YA.js → css-DMPpCfkZ.js} +0 -0
  257. /package/dist/web/assets/{csv-BX5XYhfH.js → csv-DTSgNSaP.js} +0 -0
  258. /package/dist/web/assets/{csv-parser-d56Fzpwc.js → csv-parser-B_xNn07y.js} +0 -0
  259. /package/dist/web/assets/{cue-B7099gQa.js → cue-BEmVIZmd.js} +0 -0
  260. /package/dist/web/assets/{cypher-D99mWVIu.js → cypher-CncQVhvU.js} +0 -0
  261. /package/dist/web/assets/{d-CFE8oQwh.js → d-BzUSBVex.js} +0 -0
  262. /package/dist/web/assets/{dark-plus-BEeeDwdg.js → dark-plus-mysFNRq2.js} +0 -0
  263. /package/dist/web/assets/{dart-HVSWGnI5.js → dart-BBu3nkcE.js} +0 -0
  264. /package/dist/web/assets/{database-BLht7BVM.js → database-COYAMZaw.js} +0 -0
  265. /package/dist/web/assets/{dax-DjDgSJkh.js → dax-DcLVz9_9.js} +0 -0
  266. /package/dist/web/assets/{desktop-B5A3i7J1.js → desktop-DS2PdvoU.js} +0 -0
  267. /package/dist/web/assets/{diff-CpTrlaE9.js → diff-aUYk5Qys.js} +0 -0
  268. /package/dist/web/assets/{dist-BSCgJs-V.js → dist-Dxl3dqhC.js} +0 -0
  269. /package/dist/web/assets/{docker-xBhmJeHj.js → docker-12Lj1QUD.js} +0 -0
  270. /package/dist/web/assets/{dotenv-DKKhLEv5.js → dotenv-D-P4bzvl.js} +0 -0
  271. /package/dist/web/assets/{dracula-CVwQuzra.js → dracula-C-eRNn4o.js} +0 -0
  272. /package/dist/web/assets/{dracula-soft-CG6mB-lf.js → dracula-soft-CBB1BI7O.js} +0 -0
  273. /package/dist/web/assets/{dream-maker-SC-IdYY3.js → dream-maker-C_j5A0r7.js} +0 -0
  274. /package/dist/web/assets/{edit-diff-preview-C50i1H63.js → edit-diff-preview-CRMu-6r1.js} +0 -0
  275. /package/dist/web/assets/{emacs-lisp-CIBFq6W8.js → emacs-lisp-W54LT7rh.js} +0 -0
  276. /package/dist/web/assets/{everforest-dark-odRT_lec.js → everforest-dark-B_ISUDeh.js} +0 -0
  277. /package/dist/web/assets/{everforest-light-or1Voqjl.js → everforest-light-CNUYa354.js} +0 -0
  278. /package/dist/web/assets/{fennel-tQd1z3iu.js → fennel-DK82-oso.js} +0 -0
  279. /package/dist/web/assets/{file-exclamation-point-C-lL0g_E.js → file-exclamation-point-DBEC-Kst.js} +0 -0
  280. /package/dist/web/assets/{fish-DSuZQG0k.js → fish-CbJVJzQj.js} +0 -0
  281. /package/dist/web/assets/{fluent-D7xI8a8e.js → fluent-Cug5cOL1.js} +0 -0
  282. /package/dist/web/assets/{fortran-free-form-BlAtcPd3.js → fortran-free-form-amHz32Zw.js} +0 -0
  283. /package/dist/web/assets/{gdscript-VsEQXBPm.js → gdscript-t_G6VYpv.js} +0 -0
  284. /package/dist/web/assets/{gdshader-iqHMOdwW.js → gdshader-DqZaHh4E.js} +0 -0
  285. /package/dist/web/assets/{genie-C4XU4g1a.js → genie-CIJtFlZK.js} +0 -0
  286. /package/dist/web/assets/{gherkin-C4nfQzAB.js → gherkin-CalidqLY.js} +0 -0
  287. /package/dist/web/assets/{github-dark-B_pbG3CC.js → github-dark-BG2xX0wa.js} +0 -0
  288. /package/dist/web/assets/{github-dark-default-kFbx7C95.js → github-dark-default-tDp_EOBs.js} +0 -0
  289. /package/dist/web/assets/{github-dark-dimmed-CpVfQEqs.js → github-dark-dimmed-DlSfPwW2.js} +0 -0
  290. /package/dist/web/assets/{github-dark-high-contrast-DYKqQwZh.js → github-dark-high-contrast-qZM7j5KB.js} +0 -0
  291. /package/dist/web/assets/{github-light-gEYBmKaj.js → github-light-CnvWTiyb.js} +0 -0
  292. /package/dist/web/assets/{github-light-default-DVHfMPQ2.js → github-light-default-Dy6OAt2Q.js} +0 -0
  293. /package/dist/web/assets/{github-light-high-contrast-ClVJDu-q.js → github-light-high-contrast-1BHQCOPl.js} +0 -0
  294. /package/dist/web/assets/{gleam-BLaPu7ft.js → gleam-D-2cpXVX.js} +0 -0
  295. /package/dist/web/assets/{gn-BKgerUyI.js → gn-C1cC4xLc.js} +0 -0
  296. /package/dist/web/assets/{gnuplot-cqJuEFb-.js → gnuplot-2Gp459mV.js} +0 -0
  297. /package/dist/web/assets/{go-B2EHdyD8.js → go-BA1q0q8h.js} +0 -0
  298. /package/dist/web/assets/{groovy-DdImoj8s.js → groovy-BlIa-VfI.js} +0 -0
  299. /package/dist/web/assets/{gruvbox-dark-hard-DlwstAds.js → gruvbox-dark-hard-BMIwVwaJ.js} +0 -0
  300. /package/dist/web/assets/{gruvbox-dark-medium-CiYEd1vd.js → gruvbox-dark-medium-DxfuBW3n.js} +0 -0
  301. /package/dist/web/assets/{gruvbox-dark-soft-C-wEa4h4.js → gruvbox-dark-soft-_NSslDDi.js} +0 -0
  302. /package/dist/web/assets/{gruvbox-light-hard-BqYmfna-.js → gruvbox-light-hard-CP5MIsL9.js} +0 -0
  303. /package/dist/web/assets/{gruvbox-light-medium-CxmfalT2.js → gruvbox-light-medium-BTMursH0.js} +0 -0
  304. /package/dist/web/assets/{gruvbox-light-soft-DGWXXMCB.js → gruvbox-light-soft-Bv6zFoVX.js} +0 -0
  305. /package/dist/web/assets/{haskell-H_C_IuVb.js → haskell-Da8UeSv-.js} +0 -0
  306. /package/dist/web/assets/{haxe-CGNSMpeZ.js → haxe-mcKlcxBa.js} +0 -0
  307. /package/dist/web/assets/{hcl-B7bSTSQf.js → hcl-BdZcx5JX.js} +0 -0
  308. /package/dist/web/assets/{hjson-DlJ-glQZ.js → hjson-pFXYbN4t.js} +0 -0
  309. /package/dist/web/assets/{hlsl-CehW5SJN.js → hlsl-Z0II0KR8.js} +0 -0
  310. /package/dist/web/assets/{horizon-Beabcz2d.js → horizon-CufN8DyP.js} +0 -0
  311. /package/dist/web/assets/{horizon-bright-BC87RXPa.js → horizon-bright-5qoQVyIY.js} +0 -0
  312. /package/dist/web/assets/{houston-Da7RFwUy.js → houston-DOFkQwep.js} +0 -0
  313. /package/dist/web/assets/{hy-m1H70w19.js → hy-BVbDMDcG.js} +0 -0
  314. /package/dist/web/assets/{imba-MExFsdAA.js → imba-DRM6LLS5.js} +0 -0
  315. /package/dist/web/assets/{ini-CJy_zmwy.js → ini-2KCnNvmz.js} +0 -0
  316. /package/dist/web/assets/{java-DqDFvJBH.js → java-BmvH-Ou1.js} +0 -0
  317. /package/dist/web/assets/{javascript-C3Wyv22X.js → javascript-B7VhU1Yc.js} +0 -0
  318. /package/dist/web/assets/{json-DnGBw_DZ.js → json-X1pNk0n7.js} +0 -0
  319. /package/dist/web/assets/{json5-CRN-zoN4.js → json5-CJeEJPTg.js} +0 -0
  320. /package/dist/web/assets/{jsonc-CKRUZNRo.js → jsonc-C1haTBsf.js} +0 -0
  321. /package/dist/web/assets/{jsonl-CGfD6FrH.js → jsonl-wQ8-AJgs.js} +0 -0
  322. /package/dist/web/assets/{jsonnet-nbFuDj0o.js → jsonnet-BrEjY3Xh.js} +0 -0
  323. /package/dist/web/assets/{jssm-D0ShzIqD.js → jssm-C6LgYbOs.js} +0 -0
  324. /package/dist/web/assets/{jsx-D27hbRbs.js → jsx-B2cfD_Fb.js} +0 -0
  325. /package/dist/web/assets/{kanagawa-dragon-Ov3f8K0T.js → kanagawa-dragon-6UCcJpLw.js} +0 -0
  326. /package/dist/web/assets/{kanagawa-lotus-CLyOqh-g.js → kanagawa-lotus-DOmfpsRg.js} +0 -0
  327. /package/dist/web/assets/{kanagawa-wave-Kj3dzabH.js → kanagawa-wave-CpxmPE7N.js} +0 -0
  328. /package/dist/web/assets/{kdl-DFF58Gu4.js → kdl-BrLEivmb.js} +0 -0
  329. /package/dist/web/assets/{kotlin-BM4PKaB4.js → kotlin-BO2FUnDB.js} +0 -0
  330. /package/dist/web/assets/{kusto-DrvHLCv5.js → kusto-Cev1yiq9.js} +0 -0
  331. /package/dist/web/assets/{laserwave-Db6i6ZkO.js → laserwave-BlEkf-eH.js} +0 -0
  332. /package/dist/web/assets/{lean-D0hEjzwk.js → lean-BLS3Xk2w.js} +0 -0
  333. /package/dist/web/assets/{less-BEj4npGG.js → less-BXmcsqcq.js} +0 -0
  334. /package/dist/web/assets/{lib-CBdlQ75W.js → lib-CxSBdrd7.js} +0 -0
  335. /package/dist/web/assets/{light-plus-DLIIbDea.js → light-plus-DS6zriYl.js} +0 -0
  336. /package/dist/web/assets/{llvm-DMt6umsO.js → llvm-C_wLnJgX.js} +0 -0
  337. /package/dist/web/assets/{log-BopvdtPW.js → log-Cg8pvmWi.js} +0 -0
  338. /package/dist/web/assets/{logo-CYSUFPFR.js → logo-C9-kEvPW.js} +0 -0
  339. /package/dist/web/assets/{luau-XC611HNO.js → luau-NwaGugfZ.js} +0 -0
  340. /package/dist/web/assets/{make-BMYZIfBj.js → make-DBpJE-Z8.js} +0 -0
  341. /package/dist/web/assets/{markdown-BLza5nvF.js → markdown-B7ZTAu7B.js} +0 -0
  342. /package/dist/web/assets/{material-theme-Uw27UfJW.js → material-theme-BsXH-Mss.js} +0 -0
  343. /package/dist/web/assets/{material-theme-darker-dfqt8XBY.js → material-theme-darker-CW9OlIeY.js} +0 -0
  344. /package/dist/web/assets/{material-theme-lighter-CPtBTblV.js → material-theme-lighter-D2PAh0Kw.js} +0 -0
  345. /package/dist/web/assets/{material-theme-ocean-BfZgVqC-.js → material-theme-ocean-Vz6DBVi3.js} +0 -0
  346. /package/dist/web/assets/{material-theme-palenight-DAZoVYg8.js → material-theme-palenight-D-GleP3G.js} +0 -0
  347. /package/dist/web/assets/{matlab-cSzmquiW.js → matlab-C7MYlMSn.js} +0 -0
  348. /package/dist/web/assets/{mdx-Dq0kW5VM.js → mdx-FkOOX3Dq.js} +0 -0
  349. /package/dist/web/assets/{mermaid-DoIKF0yS.js → mermaid-C_9qw5it.js} +0 -0
  350. /package/dist/web/assets/{min-dark-DyraimDU.js → min-dark-BowNvxk2.js} +0 -0
  351. /package/dist/web/assets/{min-light-Co9cbcUv.js → min-light-x8oWUmbL.js} +0 -0
  352. /package/dist/web/assets/{mipsasm-ph6cKgHn.js → mipsasm-Dmi1AZOT.js} +0 -0
  353. /package/dist/web/assets/{mojo-BBT0XwmY.js → mojo-C6z7mry2.js} +0 -0
  354. /package/dist/web/assets/{monokai-IP0T5Rtg.js → monokai-BQ4e_Hn0.js} +0 -0
  355. /package/dist/web/assets/{moonbit-B8cogNXl.js → moonbit-CEeNIoB4.js} +0 -0
  356. /package/dist/web/assets/{move-DkY26NtM.js → move-bALfKUa7.js} +0 -0
  357. /package/dist/web/assets/{narrat-rBs3byG0.js → narrat-DX4JlAWT.js} +0 -0
  358. /package/dist/web/assets/{nextflow-groovy-DkO9wMLP.js → nextflow-groovy-CKQWtRkO.js} +0 -0
  359. /package/dist/web/assets/{night-owl-jVR4lFji.js → night-owl-D4PQd726.js} +0 -0
  360. /package/dist/web/assets/{night-owl-light-fBYJ7s3U.js → night-owl-light-BQo_YHll.js} +0 -0
  361. /package/dist/web/assets/{nix-WX9oavu6.js → nix-B1jThkGj.js} +0 -0
  362. /package/dist/web/assets/{nord-Cf7G21Pu.js → nord-35quHUKo.js} +0 -0
  363. /package/dist/web/assets/{nushell-DQdzcJ1H.js → nushell-L6DbJTuL.js} +0 -0
  364. /package/dist/web/assets/{objective-c-JSO2uvZ-.js → objective-c-CTZ4bR2U.js} +0 -0
  365. /package/dist/web/assets/{objective-cpp-CZMGJ9Fb.js → objective-cpp-Dm1YDIdA.js} +0 -0
  366. /package/dist/web/assets/{ocaml-C4S4NSDo.js → ocaml-B3n__LGy.js} +0 -0
  367. /package/dist/web/assets/{odin-Y2jXCgVq.js → odin-DmqZ8n3i.js} +0 -0
  368. /package/dist/web/assets/{one-dark-pro-dVIxuoJY.js → one-dark-pro-16_UaUig.js} +0 -0
  369. /package/dist/web/assets/{one-light-BQ1IKfD1.js → one-light-B-GGjWn5.js} +0 -0
  370. /package/dist/web/assets/{openscad-Bddw4I3z.js → openscad-DTFB9Sj6.js} +0 -0
  371. /package/dist/web/assets/{pascal-BKVXrzC1.js → pascal-RcT1dQYN.js} +0 -0
  372. /package/dist/web/assets/{pkl-BTd-YRsO.js → pkl-Dr4yLQ_o.js} +0 -0
  373. /package/dist/web/assets/{plastic-eiGvBIxV.js → plastic-hp2Nry0R.js} +0 -0
  374. /package/dist/web/assets/{plsql-C290tieP.js → plsql-pFcO4WZ0.js} +0 -0
  375. /package/dist/web/assets/{po-DyDwh0tx.js → po-BkZ0_HvY.js} +0 -0
  376. /package/dist/web/assets/{poimandres-B87zc_g-.js → poimandres-BcTrxj7f.js} +0 -0
  377. /package/dist/web/assets/{polar-Cdub1YMT.js → polar-DHK3q8FF.js} +0 -0
  378. /package/dist/web/assets/{postcss-BcI_3K0v.js → postcss-Bv7WzijA.js} +0 -0
  379. /package/dist/web/assets/{powerquery-OdMrkJSe.js → powerquery-DpaU_hW9.js} +0 -0
  380. /package/dist/web/assets/{powershell-C8auT6xg.js → powershell-_Rqc0MGP.js} +0 -0
  381. /package/dist/web/assets/{prisma-DIbVv8PA.js → prisma-wyegE8us.js} +0 -0
  382. /package/dist/web/assets/{prolog-DbTOH3Ld.js → prolog-DspHCURv.js} +0 -0
  383. /package/dist/web/assets/{proto-C4Cq2dCD.js → proto-BOBiNGf5.js} +0 -0
  384. /package/dist/web/assets/{puppet-DJp3uqum.js → puppet-DfLvV8h2.js} +0 -0
  385. /package/dist/web/assets/{purescript-CLsV4rPU.js → purescript-BY5ZSOZy.js} +0 -0
  386. /package/dist/web/assets/{python-2gBtEza2.js → python-B758mMeg.js} +0 -0
  387. /package/dist/web/assets/{qmldir-DfhULcua.js → qmldir-C75bpHvy.js} +0 -0
  388. /package/dist/web/assets/{qss-DK--VTTV.js → qss-DcLTlbLd.js} +0 -0
  389. /package/dist/web/assets/{r-BArBO2Sa.js → r-D0G_Y1P5.js} +0 -0
  390. /package/dist/web/assets/{racket-5mijHsVy.js → racket-DTye_5qn.js} +0 -0
  391. /package/dist/web/assets/{raku-ir0suElk.js → raku-uJXBXkV0.js} +0 -0
  392. /package/dist/web/assets/{react-J9g6e3Rq.js → react-CorNPdie.js} +0 -0
  393. /package/dist/web/assets/{red-DSecpIOT.js → red-rs3HE3re.js} +0 -0
  394. /package/dist/web/assets/{reg-Dp30FusS.js → reg-DkTIaX7a.js} +0 -0
  395. /package/dist/web/assets/{regexp-DXJnEM_6.js → regexp-4TLSs4_m.js} +0 -0
  396. /package/dist/web/assets/{rel-Dr483ozj.js → rel-B0jumCjt.js} +0 -0
  397. /package/dist/web/assets/{resolve-theme-DJORYzKu.js → resolve-theme-CCoakF2E.js} +0 -0
  398. /package/dist/web/assets/{riscv-ZfBy5IA1.js → riscv-DtaWMlo0.js} +0 -0
  399. /package/dist/web/assets/{ron-ZJ_CljGP.js → ron-BCPxwJD6.js} +0 -0
  400. /package/dist/web/assets/{rose-pine-CcW_eJ6G.js → rose-pine-CNryoqGi.js} +0 -0
  401. /package/dist/web/assets/{rose-pine-dawn-v2l_UF-j.js → rose-pine-dawn-C-gJOQVP.js} +0 -0
  402. /package/dist/web/assets/{rose-pine-moon-CNXkwf3D.js → rose-pine-moon-BdXSXpZh.js} +0 -0
  403. /package/dist/web/assets/{rosmsg-BMYMVW84.js → rosmsg-CPG8X-D5.js} +0 -0
  404. /package/dist/web/assets/{rust-BR-zJh5q.js → rust-CxfW3ePx.js} +0 -0
  405. /package/dist/web/assets/{sass-BukPOBFz.js → sass-D-7bxo_L.js} +0 -0
  406. /package/dist/web/assets/{save-DO5xjW95.js → save-BM4p9_9n.js} +0 -0
  407. /package/dist/web/assets/{scala-DUFY7D6D.js → scala-DyUsfHNc.js} +0 -0
  408. /package/dist/web/assets/{scheme-Dl-dNNxQ.js → scheme-ROgrwiO-.js} +0 -0
  409. /package/dist/web/assets/{sdbl-DdmlQhia.js → sdbl-B1EAQWpp.js} +0 -0
  410. /package/dist/web/assets/{shellscript-UyhrvGcd.js → shellscript-HhmB_Ufy.js} +0 -0
  411. /package/dist/web/assets/{shield-check-m2QzZTuZ.js → shield-check-C3qYSU8a.js} +0 -0
  412. /package/dist/web/assets/{shield-off-DNn7VzJe.js → shield-off-D5tU7gQT.js} +0 -0
  413. /package/dist/web/assets/{slack-dark-Cv6Szr-q.js → slack-dark-DnmSY72G.js} +0 -0
  414. /package/dist/web/assets/{slack-ochin-V6Ii3lAm.js → slack-ochin-q97gHjcd.js} +0 -0
  415. /package/dist/web/assets/{smalltalk-BUCv5G91.js → smalltalk-B-1waXvA.js} +0 -0
  416. /package/dist/web/assets/{snazzy-light-B67a78qi.js → snazzy-light-C8yWDN2g.js} +0 -0
  417. /package/dist/web/assets/{solarized-dark-ccWnfYyA.js → solarized-dark-ClVDEMJZ.js} +0 -0
  418. /package/dist/web/assets/{solarized-light-C3WyRO5I.js → solarized-light-Bts6CUIP.js} +0 -0
  419. /package/dist/web/assets/{solidity-BFbRPNi_.js → solidity-DYxwp0om.js} +0 -0
  420. /package/dist/web/assets/{splunk-zdpwCJLE.js → splunk-jK6-Sr06.js} +0 -0
  421. /package/dist/web/assets/{sql-B2IPUsfz.js → sql-DPpd8aUS.js} +0 -0
  422. /package/dist/web/assets/{ssh-config-Co4cXifq.js → ssh-config-Bimb9LVr.js} +0 -0
  423. /package/dist/web/assets/{stylus-CmI75XY8.js → stylus-B2DgJRJw.js} +0 -0
  424. /package/dist/web/assets/{swift-Dfks9YQI.js → swift-ChEH2nFL.js} +0 -0
  425. /package/dist/web/assets/{synthwave-84-B3LERzNS.js → synthwave-84-DwSLbgAm.js} +0 -0
  426. /package/dist/web/assets/{system-verilog-BFvldiwl.js → system-verilog-DoVBMhJ8.js} +0 -0
  427. /package/dist/web/assets/{systemd-Cu4Vvgbu.js → systemd--Ux4VAYD.js} +0 -0
  428. /package/dist/web/assets/{table-CAVmgQMT.js → table-Di8pv2Y_.js} +0 -0
  429. /package/dist/web/assets/{talonscript-Bnq32pUB.js → talonscript-DQNx9nak.js} +0 -0
  430. /package/dist/web/assets/{tasl-Nwe2K3J1.js → tasl-Bwy_Y9uD.js} +0 -0
  431. /package/dist/web/assets/{tcl-zOnpywrl.js → tcl-Drea-2jA.js} +0 -0
  432. /package/dist/web/assets/{terraform-Bc9Vj-n3.js → terraform-4rS5HoFk.js} +0 -0
  433. /package/dist/web/assets/{tokyo-night-9_KyM8M2.js → tokyo-night-C9MqaxIn.js} +0 -0
  434. /package/dist/web/assets/{toml-ByH-fxOY.js → toml-tSgOSv42.js} +0 -0
  435. /package/dist/web/assets/{tsv-DrCnDClP.js → tsv-BmL4O1Zx.js} +0 -0
  436. /package/dist/web/assets/{tsx-B8LafSYr.js → tsx-BwzeA880.js} +0 -0
  437. /package/dist/web/assets/{turtle-DuyP_QCJ.js → turtle-2DYvssFL.js} +0 -0
  438. /package/dist/web/assets/{typescript-BTBJb5RD.js → typescript-ZC-vonSh.js} +0 -0
  439. /package/dist/web/assets/{typespec-DdOFo7M5.js → typespec-DkCEO45F.js} +0 -0
  440. /package/dist/web/assets/{typst-LYvnBWQu.js → typst-DAZdb2n6.js} +0 -0
  441. /package/dist/web/assets/{utils-CTg5uAYR.js → utils-CSCvNZxE.js} +0 -0
  442. /package/dist/web/assets/{v-BuBYA1_x.js → v-B_wsb4C1.js} +0 -0
  443. /package/dist/web/assets/{vala-vRIBYYe3.js → vala-BDBNS784.js} +0 -0
  444. /package/dist/web/assets/{vb-DwGi-IYM.js → vb-CuOoJSSa.js} +0 -0
  445. /package/dist/web/assets/{vendor-xterm-i6SHFTfU.js → vendor-xterm-CImj6i6Z.js} +0 -0
  446. /package/dist/web/assets/{verilog-6B1X4G2-.js → verilog-BCcOvOcc.js} +0 -0
  447. /package/dist/web/assets/{vesper-Dw1y_Ae7.js → vesper-vN8CREK5.js} +0 -0
  448. /package/dist/web/assets/{vhdl-DK8JwBzJ.js → vhdl-CI8PoqeM.js} +0 -0
  449. /package/dist/web/assets/{viml-BTKIEIqm.js → viml-B66dNP1B.js} +0 -0
  450. /package/dist/web/assets/{vitesse-black-BqqolhEg.js → vitesse-black-0bGIaCfD.js} +0 -0
  451. /package/dist/web/assets/{vitesse-dark-B9JL5WQA.js → vitesse-dark-Dk-RSXq-.js} +0 -0
  452. /package/dist/web/assets/{vitesse-light-DYJKj2se.js → vitesse-light-BUsA5RhG.js} +0 -0
  453. /package/dist/web/assets/{vyper-D-jDI3WH.js → vyper-dNodzEa8.js} +0 -0
  454. /package/dist/web/assets/{wasm-1-GlNdpk.js → wasm-BDxaNPCj.js} +0 -0
  455. /package/dist/web/assets/{wasm-BBW4WmT2.js → wasm-d6SE2_qm.js} +0 -0
  456. /package/dist/web/assets/{wenyan-B6cM7sDO.js → wenyan-D0h8Uz55.js} +0 -0
  457. /package/dist/web/assets/{wgsl-FaHGQgko.js → wgsl-vKs_vsvQ.js} +0 -0
  458. /package/dist/web/assets/{wikitext-DtIyPUrk.js → wikitext-BIWsPJit.js} +0 -0
  459. /package/dist/web/assets/{wit-BMVRif5Y.js → wit-6vmKi2H3.js} +0 -0
  460. /package/dist/web/assets/{wolfram-DQsCGohn.js → wolfram-zoNg6F1a.js} +0 -0
  461. /package/dist/web/assets/{x-C2VSAp7O.js → x-BsFLg-T8.js} +0 -0
  462. /package/dist/web/assets/{yaml-C-Va56Gt.js → yaml-W95qP6-B.js} +0 -0
  463. /package/dist/web/assets/{zenscript-DdobKGI-.js → zenscript-BxircxDx.js} +0 -0
  464. /package/dist/web/assets/{zig-vqDFwj6q.js → zig-DD79WQtz.js} +0 -0
@@ -7,16 +7,18 @@ import {
7
7
  setMemberSession,
8
8
  setGroupLeaderSession,
9
9
  } from "./group-chat.store.ts";
10
- import { runGroupTurnLoop } from "./turn-engine.ts";
10
+ import { runReplyBurst } from "./turn-engine.ts";
11
11
  import { makeEngineRunAgent } from "./agent-runner.ts";
12
12
  import type { ChatBackend } from "./agent-runner.ts";
13
+ import { makeResponderRouter } from "./responder-router.ts";
13
14
  import { archiveAndDelete } from "./transcript-archive.ts";
14
15
  import type {
15
16
  Group,
16
17
  GroupMember,
17
18
  GroupMessage,
18
19
  AgentTurnResult,
19
- TurnLoopResult,
20
+ BurstResult,
21
+ TurnEngineDeps,
20
22
  } from "../../types/group-chat.ts";
21
23
  import type { GroupChatServerMessage } from "../../types/group-chat-ws.ts";
22
24
 
@@ -24,10 +26,9 @@ type WsClient = { send: (data: string) => void };
24
26
 
25
27
  interface GroupRuntime {
26
28
  abort: AbortController;
27
- clients: Set<WsClient>;
28
- /** Bounded reconnect buffer of emitted server events. */
29
+ /** Bounded reconnect buffer of the CURRENT burst's events (replayed on reconnect). */
29
30
  buffer: GroupChatServerMessage[];
30
- loop: Promise<TurnLoopResult>;
31
+ loop: Promise<BurstResult>;
31
32
  }
32
33
 
33
34
  const MAX_BUFFER = 2000;
@@ -37,31 +38,54 @@ const MAX_BUFFER = 2000;
37
38
  * model — FE disconnect does NOT abort the loop. */
38
39
  class GroupChatService {
39
40
  private runtimes = new Map<string, GroupRuntime>();
40
- /** WS clients connected before a runtime exists migrated into the runtime on start/resume. */
41
- private pendingClients = new Map<string, Set<WsClient>>();
41
+ /** Connected WS clients per group PERSISTENT across bursts. Must NOT be tied to a
42
+ * runtime's lifetime: with re-seed-per-message the runtime is created/destroyed for
43
+ * every message, and a client stored on the runtime would be orphaned when the burst
44
+ * ends (missing its own next message until it reconnects). */
45
+ private clients = new Map<string, Set<WsClient>>();
46
+ /** Latest user message received while a burst was running — drained into the next
47
+ * burst when the current one finishes (single-slot: newer supersedes older). */
48
+ private pendingMessage = new Map<string, GroupMessage>();
49
+ /** Member session ids known created + live THIS server run. Keep-alive: a member's
50
+ * session is reused (resumed → native memory) across bursts while its id is here.
51
+ * Cleared on server restart → sessions are recreated fresh (memory reset, accepted). */
52
+ private aliveSessions = new Set<string>();
42
53
 
43
54
  // --- test seams (production uses the real provider-backed runner) --------
44
55
  private stubbedRunAgent: (() => (m: GroupMember, prompt: string) => Promise<AgentTurnResult>) | null = null;
56
+ private stubbedResponderRouter: (() => NonNullable<TurnEngineDeps["routeNextSpeakers"]>) | null = null;
45
57
  private spawnStub = false;
46
58
 
47
59
  _setRunAgentFactory(f: () => (m: GroupMember, prompt: string) => Promise<AgentTurnResult>): void {
48
60
  this.stubbedRunAgent = f;
49
61
  }
62
+ _setResponderRouterFactory(f: (() => NonNullable<TurnEngineDeps["routeNextSpeakers"]>) | null): void {
63
+ this.stubbedResponderRouter = f;
64
+ }
50
65
  _setSpawnStub(v: boolean): void { this.spawnStub = v; }
66
+ /** Inject a fake ChatBackend (session/create/send) for lifecycle tests. */
67
+ _setBackend(b: ChatBackend | null): void { this.backendCache = b; this.aliveSessions.clear(); }
51
68
  isRunning(groupId: string): boolean { return this.runtimes.has(groupId); }
52
69
 
53
70
  // --- lifecycle -----------------------------------------------------------
54
71
 
55
- /** Start (or ignore if already running) the turn loop for a group. Detached.
56
- * Persists the user's task as the first bus message, then runs the loop. */
72
+ /** Handle a user message: persist it as a chat message, then run a bounded reply
73
+ * burst. If a burst is already running, queue the message (newer supersedes older)
74
+ * and run it once the current burst finishes. */
57
75
  async start(groupId: string, userMessage: string, providerId = "claude"): Promise<void> {
58
- if (this.runtimes.has(groupId)) return;
59
76
  const { group, members } = this.loadGroup(groupId);
60
-
61
- const taskMsg = appendMessage({
62
- groupId, fromMember: "user", kind: "task", summary: userMessage, turnIndex: -1,
77
+ const msg = appendMessage({
78
+ groupId, fromMember: "user", kind: "chat", summary: userMessage,
79
+ turnIndex: this.nextTurnIndex(groupId),
63
80
  });
64
- await this.runLoop(group, members, userMessage, providerId, taskMsg);
81
+
82
+ if (this.runtimes.has(groupId)) {
83
+ // A burst is in flight — queue this message and surface it in the feed now.
84
+ this.pendingMessage.set(groupId, msg);
85
+ this.emit(groupId, { type: "group_message", message: msg });
86
+ return;
87
+ }
88
+ await this.runBurst(group, members, providerId, msg);
65
89
  }
66
90
 
67
91
  /** Stop a running group: abort the in-flight turn + mark paused. Cooperative
@@ -72,16 +96,17 @@ class GroupChatService {
72
96
  setGroupStatus(groupId, "paused");
73
97
  }
74
98
 
75
- /** Resume: re-spawn fresh member sessions and re-enter the loop, seeding
76
- * context from the durable bus (windowed + rolling summary). The bus is the
77
- * source of truth works even after transcripts were archived/deleted.
78
- * Resuming a converged group starts a new round appended to the same bus. */
99
+ /** Resume after a Stop: re-run a reply burst ONLY when the latest message is an
100
+ * unanswered user message (i.e. a burst was stopped/errored before replying). If the
101
+ * last message is an assistant reply, the conversation is already answered → no-op,
102
+ * so Resume never double-replies to a question that was already handled. */
79
103
  async resume(groupId: string, providerId = "claude"): Promise<void> {
80
104
  if (this.runtimes.has(groupId)) return;
81
105
  const { group, members } = this.loadGroup(groupId);
82
- // The original task is the durable anchor for every resumed round.
83
- const task = this.originalTask(groupId) ?? group.name;
84
- await this.runLoop(group, members, task, providerId);
106
+ const msgs = readMessages(groupId);
107
+ const last = msgs[msgs.length - 1];
108
+ if (!last || last.fromMember !== "user") { setGroupStatus(groupId, "idle"); return; }
109
+ await this.runBurst(group, members, providerId);
85
110
  }
86
111
 
87
112
  // --- shared loop core ----------------------------------------------------
@@ -94,71 +119,104 @@ class GroupChatService {
94
119
  return { group, members };
95
120
  }
96
121
 
97
- /** Original user task from the durable bus (turn_index = -1). */
98
- private originalTask(groupId: string): string | null {
99
- const first = readMessages(groupId).find((m) => m.kind === "task" && m.fromMember === "user");
100
- return first?.summary ?? null;
122
+ /** Next sequential turn index for the group's durable bus. */
123
+ private nextTurnIndex(groupId: string): number {
124
+ const prior = readMessages(groupId);
125
+ return (prior[prior.length - 1]?.turnIndex ?? -1) + 1;
101
126
  }
102
127
 
103
- /** Spawn fresh sessions (unless stubbed), run the detached turn loop, and
104
- * register the runtime. Shared by both start() and resume(). */
105
- private async runLoop(
106
- group: Group, members: GroupMember[], task: string, providerId: string,
128
+ /** Register the runtime, then (async) spawn sessions + run one detached reply burst,
129
+ * draining any user message queued while the burst ran. Re-seed per message.
130
+ * The runtime is registered SYNCHRONOUSLY (before the first await) so a message that
131
+ * arrives during session spawn is queued via start()'s runtimes.has() check — never
132
+ * run as a second concurrent burst. */
133
+ private async runBurst(
134
+ group: Group, members: GroupMember[], providerId: string,
107
135
  initialMessage?: GroupMessage,
108
136
  ): Promise<void> {
109
- const backend = this.spawnStub ? null : await this.getBackend();
110
- if (backend) await this.spawnSessions(group, members, backend);
111
-
112
- setGroupStatus(group.id, "active");
113
137
  const abort = new AbortController();
138
+ // Synchronous registration — no await may precede this (so a message arriving during
139
+ // session spawn is queued, not run as a second concurrent burst).
140
+ const runtime: GroupRuntime = {
141
+ abort, buffer: [],
142
+ loop: Promise.resolve({ reason: "stopped", turns: 0, costUsd: 0 } as BurstResult),
143
+ };
144
+ this.runtimes.set(group.id, runtime);
145
+ setGroupStatus(group.id, "active");
146
+ // Emit the user's message now that the runtime exists (so it lands in the reconnect
147
+ // buffer); persistent clients receive it live regardless of runtime lifetime.
148
+ if (initialMessage) {
149
+ this.emit(group.id, { type: "group_message", message: initialMessage });
150
+ }
114
151
 
115
- const runAgent = this.stubbedRunAgent
116
- ? this.stubbedRunAgent()
117
- : makeEngineRunAgent(backend!, providerId, abort.signal);
152
+ // Dedicated ephemeral router session (cheap-model next-speaker classification) —
153
+ // kept separate from member sessions so router prompts never pollute member history.
154
+ let routerSessionId: string | null = null;
118
155
 
119
- const deps = {
120
- runAgent,
121
- appendMessage,
122
- readMessages,
123
- onMessage: (message: GroupMessage) => this.emit(group.id, { type: "group_message", message }),
124
- shouldStop: () => abort.signal.aborted,
125
- };
156
+ const loop = (async () => {
157
+ const backend = this.spawnStub ? null : await this.getBackend();
158
+ if (backend) await this.ensureSessions(group, members, backend);
159
+ const runAgent = this.stubbedRunAgent
160
+ ? this.stubbedRunAgent()
161
+ : makeEngineRunAgent(backend!, providerId, abort.signal);
126
162
 
127
- const loop = runGroupTurnLoop(group, members, deps, task)
163
+ // Smart responder routing: prefer a stubbed router in tests; otherwise build one over
164
+ // a fresh ephemeral session. Absent → engine falls back to mention-following.
165
+ let routeNextSpeakers: TurnEngineDeps["routeNextSpeakers"];
166
+ if (this.stubbedResponderRouter) {
167
+ routeNextSpeakers = this.stubbedResponderRouter();
168
+ } else if (backend) {
169
+ const rs = await backend.createSession({
170
+ projectPath: group.projectPath, projectName: group.projectName,
171
+ title: `[group:${group.name}] router`,
172
+ });
173
+ routerSessionId = rs.id;
174
+ routeNextSpeakers = makeResponderRouter(backend, providerId, rs.id);
175
+ }
176
+
177
+ const deps = {
178
+ runAgent,
179
+ appendMessage,
180
+ readMessages,
181
+ onMessage: (message: GroupMessage) => this.emit(group.id, { type: "group_message", message }),
182
+ onTyping: (member: string) => this.emit(group.id, { type: "typing", member }),
183
+ routeNextSpeakers,
184
+ shouldStop: () => abort.signal.aborted,
185
+ };
186
+ return runReplyBurst(group, members, deps, { cap: group.maxTurns });
187
+ })()
128
188
  .then((res) => {
129
189
  this.emit(group.id, { type: "group_done", reason: res.reason, turns: res.turns, costUsd: res.costUsd });
130
190
  // A user-requested stop keeps the group paused for a later resume;
131
- // any natural termination returns the group to idle.
191
+ // a natural burst end returns the group to idle to wait for the next message.
132
192
  setGroupStatus(group.id, res.reason === "stopped" ? "paused" : "idle");
133
193
  return res;
134
194
  })
135
195
  .catch((e) => {
136
196
  this.emit(group.id, { type: "error", message: (e as Error).message });
137
197
  setGroupStatus(group.id, "idle");
138
- return { reason: "stopped", turns: 0, costUsd: 0 } as TurnLoopResult;
198
+ return { reason: "stopped", turns: 0, costUsd: 0 } as BurstResult;
139
199
  })
140
200
  .finally(() => {
141
- if (!this.spawnStub) void this.archiveMembers(group, members);
201
+ // Keep-alive: member sessions are NOT archived per burst — they persist so the
202
+ // next burst resumes them (native memory). Archival happens on group/member delete.
203
+ if (routerSessionId) void this.deleteRouterSession(providerId, routerSessionId);
142
204
  this.runtimes.delete(group.id);
205
+ // Drain a message queued mid-burst → run the next burst for it.
206
+ const queued = this.pendingMessage.get(group.id);
207
+ if (queued) {
208
+ this.pendingMessage.delete(group.id);
209
+ void this.runBurst(group, members, providerId);
210
+ }
143
211
  });
144
-
145
- // Migrate any WS clients that connected before this runtime was created.
146
- const inherited = this.pendingClients.get(group.id) ?? new Set<WsClient>();
147
- this.pendingClients.delete(group.id);
148
- this.runtimes.set(group.id, { abort, clients: inherited, buffer: [], loop });
149
- // Emit the task message now that the runtime + client set exists, so WS
150
- // clients that connected before start() was called receive it immediately.
151
- if (initialMessage) {
152
- this.emit(group.id, { type: "group_message", message: initialMessage });
153
- }
154
- // Surface spawn/immediate errors to the caller; loop errors are emitted.
212
+ runtime.loop = loop;
213
+ // Surface spawn/immediate errors to the caller; burst errors are emitted.
155
214
  await Promise.resolve();
156
215
  }
157
216
 
158
217
  // --- WS wiring -----------------------------------------------------------
159
218
 
160
219
  addClient(groupId: string, ws: WsClient): void {
161
- const rt = this.runtimes.get(groupId);
162
220
  const group = getGroup(groupId);
163
221
  if (group) {
164
222
  const members = listMembers(groupId).map((m) => ({
@@ -166,39 +224,50 @@ class GroupChatService {
166
224
  }));
167
225
  ws.send(JSON.stringify({ type: "group_state", groupId, status: group.status, members } satisfies GroupChatServerMessage));
168
226
  }
169
- if (rt) {
170
- rt.clients.add(ws);
171
- for (const ev of rt.buffer) ws.send(JSON.stringify(ev));
172
- } else {
173
- // No runtime yet park the client so it is migrated into the runtime when the
174
- // engine starts. Without this the WS receives group_state but misses all
175
- // group_message broadcasts because emit() only iterates rt.clients.
176
- let pending = this.pendingClients.get(groupId);
177
- if (!pending) { pending = new Set(); this.pendingClients.set(groupId, pending); }
178
- pending.add(ws);
179
- }
227
+ // Register in the persistent per-group client set (survives burst churn).
228
+ let set = this.clients.get(groupId);
229
+ if (!set) { set = new Set(); this.clients.set(groupId, set); }
230
+ set.add(ws);
231
+ // Replay the current burst's buffer so a mid-burst reconnect catches up.
232
+ const rt = this.runtimes.get(groupId);
233
+ if (rt) for (const ev of rt.buffer) ws.send(JSON.stringify(ev));
180
234
  }
181
235
 
182
236
  removeClient(groupId: string, ws: WsClient): void {
183
- this.runtimes.get(groupId)?.clients.delete(ws);
184
- this.pendingClients.get(groupId)?.delete(ws);
237
+ const set = this.clients.get(groupId);
238
+ set?.delete(ws);
239
+ if (set && set.size === 0) this.clients.delete(groupId);
185
240
  }
186
241
 
187
242
  private emit(groupId: string, ev: GroupChatServerMessage): void {
243
+ // Buffer into the current burst (for reconnect replay) when one is running.
188
244
  const rt = this.runtimes.get(groupId);
189
- if (!rt) return;
190
- rt.buffer.push(ev);
191
- if (rt.buffer.length > MAX_BUFFER) rt.buffer.shift();
245
+ if (rt) {
246
+ rt.buffer.push(ev);
247
+ if (rt.buffer.length > MAX_BUFFER) rt.buffer.shift();
248
+ }
249
+ // Broadcast to all persistent clients — independent of runtime lifetime.
250
+ const set = this.clients.get(groupId);
251
+ if (!set) return;
192
252
  const payload = JSON.stringify(ev);
193
- for (const c of rt.clients) {
253
+ for (const c of set) {
194
254
  try { c.send(payload); } catch { /* dropped client */ }
195
255
  }
196
256
  }
197
257
 
198
258
  // --- provider-backed session lifecycle -----------------------------------
199
259
 
200
- private async spawnSessions(group: Group, members: GroupMember[], backend: ChatBackend): Promise<void> {
260
+ /** Keep-alive: reuse a member's session while it's live this run (resume → native
261
+ * memory); otherwise create a fresh one (archiving any stale prior file first). */
262
+ private async ensureSessions(group: Group, members: GroupMember[], backend: ChatBackend): Promise<void> {
201
263
  for (const m of members) {
264
+ if (m.sessionId && this.aliveSessions.has(m.sessionId)) {
265
+ if (m.role === "leader") setGroupLeaderSession(group.id, m.sessionId);
266
+ continue; // reuse — the provider resumes it (in-process message count > 0)
267
+ }
268
+ // Not alive this run (new member / post-restart): archive any orphaned old file,
269
+ // then create a fresh session so we never create-with-an-existing-id.
270
+ if (m.sessionId) await archiveAndDelete(m.sessionId, group.name).catch(() => {});
202
271
  const session = await backend.createSession({
203
272
  projectPath: group.projectPath,
204
273
  projectName: group.projectName,
@@ -206,16 +275,46 @@ class GroupChatService {
206
275
  });
207
276
  setMemberSession(m.id, session.id);
208
277
  m.sessionId = session.id;
278
+ this.aliveSessions.add(session.id);
209
279
  if (m.role === "leader") setGroupLeaderSession(group.id, session.id);
210
280
  }
211
281
  }
212
282
 
283
+ /** Archive + delete every member's live session and forget it (on group/member delete). */
213
284
  private async archiveMembers(group: Group, members: GroupMember[]): Promise<void> {
214
285
  for (const m of members) {
215
- if (m.sessionId) await archiveAndDelete(m.sessionId, group.name).catch(() => {});
286
+ if (m.sessionId) {
287
+ await archiveAndDelete(m.sessionId, group.name).catch(() => {});
288
+ this.aliveSessions.delete(m.sessionId);
289
+ }
216
290
  }
217
291
  }
218
292
 
293
+ /** Teardown for group delete: stop any burst, archive all member sessions, drop state. */
294
+ async archiveAndForget(groupId: string): Promise<void> {
295
+ const group = getGroup(groupId);
296
+ if (!group) return;
297
+ this.stop(groupId);
298
+ if (!this.spawnStub) await this.archiveMembers(group, listMembers(groupId));
299
+ this.clients.delete(groupId);
300
+ this.pendingMessage.delete(groupId);
301
+ }
302
+
303
+ /** Archive + forget a single member's session (on member removal). */
304
+ async archiveMemberSession(groupName: string, sessionId: string | null): Promise<void> {
305
+ if (!sessionId || this.spawnStub) return;
306
+ await archiveAndDelete(sessionId, groupName).catch(() => {});
307
+ this.aliveSessions.delete(sessionId);
308
+ }
309
+
310
+ /** Delete the ephemeral router session (no archive — its transcript is throwaway). */
311
+ private async deleteRouterSession(providerId: string, sessionId: string): Promise<void> {
312
+ try {
313
+ const { chatService } = await import("../chat.service.ts");
314
+ await chatService.deleteSession(providerId, sessionId);
315
+ } catch { /* best-effort cleanup */ }
316
+ }
317
+
219
318
  private backendCache: ChatBackend | null = null;
220
319
 
221
320
  private async getBackend(): Promise<ChatBackend> {
@@ -228,7 +327,7 @@ class GroupChatService {
228
327
  // consumer-side signal guard halts the turn, so only permissionMode is
229
328
  // forwarded here.
230
329
  sendMessage: (pid, sid, prompt, opts) =>
231
- chatService.sendMessage(pid, sid, prompt, { permissionMode: opts?.permissionMode }),
330
+ chatService.sendMessage(pid, sid, prompt, { permissionMode: opts?.permissionMode, model: opts?.model, oneMContext: opts?.oneMContext }),
232
331
  };
233
332
  return this.backendCache;
234
333
  }
@@ -10,6 +10,7 @@ import {
10
10
  type MemberStatus,
11
11
  type CreateGroupInput,
12
12
  type AddMemberInput,
13
+ type UpdateMemberInput,
13
14
  type AppendMessageInput,
14
15
  type ReadMessagesOptions,
15
16
  } from "../../types/group-chat.ts";
@@ -142,6 +143,11 @@ export function setGroupLeaderSession(id: string, sessionId: string): void {
142
143
  getDb().query("UPDATE chat_groups SET leader_session_id = ? WHERE id = ?").run(sessionId, id);
143
144
  }
144
145
 
146
+ /** Update the per-group reply-burst cap (max AI turns per user message). */
147
+ export function setGroupMaxTurns(id: string, maxTurns: number): void {
148
+ getDb().query("UPDATE chat_groups SET max_turns = ? WHERE id = ?").run(maxTurns, id);
149
+ }
150
+
145
151
  // ---------------------------------------------------------------------------
146
152
  // Members
147
153
  // ---------------------------------------------------------------------------
@@ -159,6 +165,30 @@ export function addMember(input: AddMemberInput): GroupMember {
159
165
  return toMember(row);
160
166
  }
161
167
 
168
+ export function getMember(memberId: string): GroupMember | null {
169
+ const row = getDb().query("SELECT * FROM chat_group_members WHERE id = ?").get(memberId) as MemberRow | null;
170
+ return row ? toMember(row) : null;
171
+ }
172
+
173
+ /** Update the provided fields of a member; unspecified fields are left unchanged. */
174
+ export function updateMember(memberId: string, patch: UpdateMemberInput): GroupMember | null {
175
+ const cols: Array<[string, string | null]> = [];
176
+ if (patch.role !== undefined) cols.push(["role", patch.role]);
177
+ if (patch.name !== undefined) cols.push(["name", patch.name]);
178
+ if (patch.persona !== undefined) cols.push(["persona", patch.persona]);
179
+ if (patch.agentType !== undefined) cols.push(["agent_type", patch.agentType]);
180
+ if (patch.model !== undefined) cols.push(["model", patch.model]);
181
+ if (patch.color !== undefined) cols.push(["color", patch.color]);
182
+ if (cols.length === 0) return getMember(memberId);
183
+ const set = cols.map(([c]) => `${c} = ?`).join(", ");
184
+ getDb().query(`UPDATE chat_group_members SET ${set} WHERE id = ?`).run(...cols.map(([, v]) => v), memberId);
185
+ return getMember(memberId);
186
+ }
187
+
188
+ export function removeMember(memberId: string): void {
189
+ getDb().query("DELETE FROM chat_group_members WHERE id = ?").run(memberId);
190
+ }
191
+
162
192
  export function listMembers(groupId: string): GroupMember[] {
163
193
  const rows = getDb().query(
164
194
  "SELECT * FROM chat_group_members WHERE group_id = ? ORDER BY joined_at, id",
@@ -0,0 +1,110 @@
1
+ import type { GroupMember, GroupMessage } from "../../types/group-chat.ts";
2
+ import { buildContextWindow, renderWindow, DEFAULT_WINDOW } from "./context-window.ts";
3
+
4
+ /** Cheapest/fastest model for the lightweight next-speaker classification. */
5
+ export const ROUTER_MODEL = "claude-haiku-4-5";
6
+ const NONE = "NONE";
7
+
8
+ /** Minimal backend the router needs (structural subset of agent-runner's ChatBackend). */
9
+ export interface RouterBackend {
10
+ sendMessage(
11
+ providerId: string,
12
+ sessionId: string,
13
+ prompt: string,
14
+ opts?: { model?: string; permissionMode?: string; oneMContext?: boolean },
15
+ ): AsyncIterable<{ type: string; content?: string; message?: string }>;
16
+ }
17
+
18
+ function escapeRe(s: string): string {
19
+ return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
20
+ }
21
+
22
+ /** Parse the router's reply into known member names (0-N), in order of appearance.
23
+ * Never invents a name — only exact member-name matches (case-insensitive, word-bounded).
24
+ * Longest names first + claimed spans so a short name can't match inside a longer one.
25
+ * NONE / unknown / garbage → []. */
26
+ export function parseResponders(text: string, members: GroupMember[]): string[] {
27
+ const t = (text ?? "").trim();
28
+ if (!t) return [];
29
+ const byLen = [...members].sort((a, b) => b.name.length - a.name.length);
30
+ const claimed: Array<[number, number]> = [];
31
+ const found: Array<{ name: string; at: number }> = [];
32
+ for (const m of byLen) {
33
+ if (!m.name) continue;
34
+ const re = new RegExp(`(^|[^\\p{L}\\p{N}_])(${escapeRe(m.name)})([^\\p{L}\\p{N}_]|$)`, "giu");
35
+ let match: RegExpExecArray | null;
36
+ while ((match = re.exec(t)) !== null) {
37
+ const start = match.index + match[1]!.length;
38
+ if (claimed.some(([s, e]) => start >= s && start < e)) continue; // inside a longer name
39
+ found.push({ name: m.name, at: start });
40
+ claimed.push([start, start + m.name.length]);
41
+ break; // one occurrence per member is enough
42
+ }
43
+ }
44
+ found.sort((a, b) => a.at - b.at);
45
+ const out: string[] = [];
46
+ const seen = new Set<string>();
47
+ for (const f of found) if (!seen.has(f.name)) { out.push(f.name); seen.add(f.name); }
48
+ return out;
49
+ }
50
+
51
+ /** Build the classification prompt. Priority: recent history > member name > persona;
52
+ * leader only as fallback / to curb off-topic drift; user turns must pick someone. */
53
+ export function buildRouterPrompt(
54
+ history: GroupMessage[], members: GroupMember[], isUserTurn: boolean,
55
+ ): string {
56
+ const { window, rollingSummary } = buildContextWindow(history, DEFAULT_WINDOW);
57
+ const leader = members.find((m) => m.role === "leader");
58
+ const roster = members
59
+ .map((m) => `- ${m.name}${m.role === "leader" ? " (leader)" : ""}${m.persona ? `: ${m.persona}` : ""}`)
60
+ .join("\n");
61
+ const names = members.map((m) => m.name).join(", ");
62
+ // The AI the user is currently conversing with (spoke most recently).
63
+ const lastSpeaker = [...history].reverse().find((m) => m.fromMember !== "user")?.fromMember;
64
+
65
+ const parts = [
66
+ `You route a group chat: decide WHO should speak next. Output member names (or ${NONE}).`,
67
+ `Members:\n${roster}`,
68
+ rollingSummary ? `\nEARLIER (summary):\n${rollingSummary}` : "",
69
+ `\nRECENT MESSAGES:\n${renderWindow(window)}`,
70
+ `\nRules (priority order):`,
71
+ lastSpeaker
72
+ ? `1. CONTINUITY: the user is currently talking with "${lastSpeaker}" (spoke most recently). If this new user message is a reply or instruction directed at them (incl. "assign X", "tell the others", "you do it"), pick "${lastSpeaker}" — even if that's the leader. Only pick someone else if the message clearly targets a different member.`
73
+ : `1. Prefer whoever the RECENT conversation makes most relevant — continue an ongoing thread.`,
74
+ `2. Otherwise, the member NAME being addressed.`,
75
+ `3. Use PERSONA/expertise only for generic questions with no clear thread.`,
76
+ `4. Pick the leader (${leader?.name ?? "leader"}) ONLY if no member clearly fits, or to steer the team back when the discussion drifts off-topic.`,
77
+ `5. PARALLELISM: if several members should respond and can do so INDEPENDENTLY (their replies don't depend on each other), list them ALL — they run in parallel. If a reply must build on another's, list only the ONE who goes next.`,
78
+ `6. WRAP-UP: after the leader (${leader?.name ?? "leader"}) delegated a task and the assigned members have reported back, pick the leader ONCE to summarize the results for the user — then, on the following turn, output ${NONE} to end.`,
79
+ isUserTurn
80
+ ? `This is a reply to the USER — you MUST pick at least one member (never ${NONE}).`
81
+ : `If no one has anything useful to add, output ${NONE} to end the exchange.`,
82
+ `\nAnswer with member name(s) from [${names}]${isUserTurn ? "" : ` or ${NONE}`}, separated by spaces — no other words.`,
83
+ ];
84
+ return parts.filter(Boolean).join("\n");
85
+ }
86
+
87
+ /** Bind a `routeNextSpeaker` fn to a backend + a dedicated router session. Best-effort:
88
+ * any error / empty / unknown reply → null (the engine forces the leader on user turns
89
+ * and ends the burst on AI turns). Uses the cheapest model via a per-call override. */
90
+ export function makeResponderRouter(
91
+ backend: RouterBackend, providerId: string, sessionId: string,
92
+ opts: { model?: string } = {},
93
+ ): (ctx: { history: GroupMessage[]; members: GroupMember[]; isUserTurn: boolean }) => Promise<string[]> {
94
+ const model = opts.model ?? ROUTER_MODEL;
95
+ return async ({ history, members, isUserTurn }) => {
96
+ try {
97
+ const prompt = buildRouterPrompt(history, members, isUserTurn);
98
+ let text = "";
99
+ // oneMContext:false — the cheap router model needs no 1M window and may not support it.
100
+ for await (const ev of backend.sendMessage(providerId, sessionId, prompt, { model, permissionMode: "bypassPermissions", oneMContext: false })) {
101
+ if (ev.type === "text") text += ev.content ?? "";
102
+ else if (ev.type === "error") return [];
103
+ else if (ev.type === "done") break;
104
+ }
105
+ return parseResponders(text, members);
106
+ } catch {
107
+ return [];
108
+ }
109
+ };
110
+ }
@@ -74,3 +74,21 @@ export function readArchivedTranscript(sessionId: string, groupName: string): st
74
74
  if (!existsSync(dest)) return null;
75
75
  try { return readFileSync(dest, "utf8"); } catch { return null; }
76
76
  }
77
+
78
+ /** Absolute path of an archived transcript JSONL if it exists (else null). */
79
+ export function archivedTranscriptPath(sessionId: string, groupName: string): string | null {
80
+ const dest = join(archiveDir(groupName), `${sessionId}.jsonl`);
81
+ return existsSync(dest) ? dest : null;
82
+ }
83
+
84
+ /** Absolute path of a LIVE (not-yet-archived) session JSONL under the Claude projects
85
+ * root, else null. Keep-alive sessions stay here until the group is deleted. */
86
+ export async function liveTranscriptPath(sessionId: string): Promise<string | null> {
87
+ return findJsonl(sessionId);
88
+ }
89
+
90
+ /** Resolve a member transcript: prefer the LIVE session file (keep-alive), fall back to
91
+ * the archived copy (post group/member delete). */
92
+ export async function resolveTranscriptPath(sessionId: string, groupName: string): Promise<string | null> {
93
+ return (await liveTranscriptPath(sessionId)) ?? archivedTranscriptPath(sessionId, groupName);
94
+ }