@hienlh/ppm 0.18.13 → 0.18.15

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 (542) hide show
  1. package/.rdtest2-cf.out +0 -0
  2. package/CHANGELOG.md +23 -0
  3. package/CLAUDE.md +3 -0
  4. package/assets/skills/ppm/SKILL.md +1 -1
  5. package/assets/skills/ppm/references/cli-reference.md +18 -0
  6. package/assets/skills/ppm/references/http-api.md +8 -1
  7. package/dist/web/assets/{adaptive-context-menu-CbgPYQmj.js → adaptive-context-menu-ChBZWLLq.js} +1 -1
  8. package/dist/web/assets/{ai-resource-editor-unM2HNmg.js → ai-resource-editor-C8aAY8Po.js} +1 -1
  9. package/dist/web/assets/{ai-settings-section-CdktdrwF.js → ai-settings-section-ERu1WiZc.js} +1 -1
  10. package/dist/web/assets/{angular-html-DcpbdcNG.js → angular-html-CXgHIQ2U.js} +1 -1
  11. package/dist/web/assets/{angular-ts-Bv_WIk34.js → angular-ts-C8renMV9.js} +1 -1
  12. package/dist/web/assets/api-client-WpZ2FDHg.js +1 -0
  13. package/dist/web/assets/api-settings-BgSEE6et.js +1 -0
  14. package/dist/web/assets/{api-settings-BdCtex9i.js → api-settings-R59c66DE.js} +1 -1
  15. package/dist/web/assets/{api-themes-pRRoLvgD.js → api-themes-DKxrwJda.js} +1 -1
  16. package/dist/web/assets/{apl-D5_lLszu.js → apl-DkGH8laI.js} +1 -1
  17. package/dist/web/assets/architecture-PBZL5I3N-CCalSKAt.js +1 -0
  18. package/dist/web/assets/{astro-CBHwddYL.js → astro-tHUd8Zlg.js} +1 -1
  19. package/dist/web/assets/{audio-preview-BD1iDovf.js → audio-preview-B_C6Dpvz.js} +1 -1
  20. package/dist/web/assets/{blade-DGe1d2Pq.js → blade-BbKpjIYK.js} +1 -1
  21. package/dist/web/assets/{bsl-2lyAzRJA.js → bsl-DA0UYto0.js} +1 -1
  22. package/dist/web/assets/{button-2WCAS8dI.js → button-DBSpDPS0.js} +1 -1
  23. package/dist/web/assets/{cairo-DjzZeJrq.js → cairo-79ayucbB.js} +1 -1
  24. package/dist/web/assets/chat-tab-BraiJsYG.js +15 -0
  25. package/dist/web/assets/{cobol-cC3XIuAJ.js → cobol-DSS2EOYo.js} +1 -1
  26. package/dist/web/assets/code-editor-lhmLNhXu.js +10 -0
  27. package/dist/web/assets/{coffee-4eGboVoU.js → coffee-DvSxq2iD.js} +1 -1
  28. package/dist/web/assets/{conflict-editor-DdJag4QK.js → conflict-editor-C4VCv51R.js} +1 -1
  29. package/dist/web/assets/{context-menu-BO1c9jKy.js → context-menu-BpwUny-o.js} +1 -1
  30. package/dist/web/assets/{cpp-DF9_G-OU.js → cpp-BhWZ-FSn.js} +1 -1
  31. package/dist/web/assets/{crystal-OavZlzcm.js → crystal-yP2OeF-f.js} +1 -1
  32. package/dist/web/assets/{csv-preview-DnXwouF3.js → csv-preview-D46W_icb.js} +1 -1
  33. package/dist/web/assets/{data-grid-overlay-editor-B9Gpjm3l.js → data-grid-overlay-editor-DeGkPYtA.js} +1 -1
  34. package/dist/web/assets/{database-viewer-Dk8oGyZx.js → database-viewer-DFI4YtrH.js} +1 -1
  35. package/dist/web/assets/{dialog-B-l2ZxVm.js → dialog-RIpeb87v.js} +1 -1
  36. package/dist/web/assets/diff-viewer-eaNOLcBL.js +4 -0
  37. package/dist/web/assets/{docx-preview-DAdHPlhZ.js → docx-preview-Cbj-pOK3.js} +1 -1
  38. package/dist/web/assets/{edge-TzYh8OlN.js → edge-D3K9cd-p.js} +1 -1
  39. package/dist/web/assets/{edit-diff-preview-DlwP3UIU.js → edit-diff-preview-DDqy3KLA.js} +1 -1
  40. package/dist/web/assets/{elixir-Cy3RhKKH.js → elixir-D4PBmsLp.js} +1 -1
  41. package/dist/web/assets/{elm-BTBZdweK.js → elm-DSpXjuaH.js} +1 -1
  42. package/dist/web/assets/{erb-dv1mI0y4.js → erb-CaYZMTJW.js} +1 -1
  43. package/dist/web/assets/{erlang-DjeiDrkl.js → erlang-DX5BFqJj.js} +1 -1
  44. package/dist/web/assets/{esm-FRwvcesu.js → esm-Cv7HsFnk.js} +1 -1
  45. package/dist/web/assets/explorer-body-BxbSttfl.js +2 -0
  46. package/dist/web/assets/explorer-window-content-DUqQCC7A.js +1 -0
  47. package/dist/web/assets/{extension-store-DUaZINyC.js → extension-store-B1EClUAr.js} +1 -1
  48. package/dist/web/assets/{extension-webview-W0qRfaAU.js → extension-webview-BaGvDk8v.js} +1 -1
  49. package/dist/web/assets/{file-store-yReVbR8q.js → file-store-m9crh6uq.js} +1 -1
  50. package/dist/web/assets/{fortran-fixed-form-BCH9DAIJ.js → fortran-fixed-form-B0bAufUe.js} +1 -1
  51. package/dist/web/assets/{fsharp-C93NYKYD.js → fsharp-C2AL2b1M.js} +1 -1
  52. package/dist/web/assets/{gdresource-BcIEE2eX.js → gdresource-DRQsDMNI.js} +1 -1
  53. package/dist/web/assets/{git-commit-BVjlXEy8.js → git-commit-GtBdnrzk.js} +1 -1
  54. package/dist/web/assets/{git-log-panel-2_4HuP47.js → git-log-panel-hwV1flSO.js} +1 -1
  55. package/dist/web/assets/{git-rebase-Bj_21B1V.js → git-rebase-h_0CGA6F.js} +1 -1
  56. package/dist/web/assets/gitGraph-HDMCJU4V-DAUrJCqi.js +1 -0
  57. package/dist/web/assets/glide-data-grid-YsN-xm6x.js +138 -0
  58. package/dist/web/assets/{glimmer-js-C20yj4Zv.js → glimmer-js-Bxwu93bS.js} +1 -1
  59. package/dist/web/assets/{glimmer-ts-ipbYC7kh.js → glimmer-ts-CIUYdR4E.js} +1 -1
  60. package/dist/web/assets/{glsl-Dovazu3t.js → glsl-CfxLI5jn.js} +1 -1
  61. package/dist/web/assets/{graphql-DpMlM7hC.js → graphql-CstqDPde.js} +1 -1
  62. package/dist/web/assets/group-chat-tab-B7mJO1gG.js +1 -0
  63. package/dist/web/assets/{hack-gc7LCBuV.js → hack-Bjqnssb5.js} +1 -1
  64. package/dist/web/assets/{haml-vtlFm9gm.js → haml-lVc-DfbM.js} +1 -1
  65. package/dist/web/assets/hand-x0ejq3fS.js +1 -0
  66. package/dist/web/assets/{handlebars-DGKhpCji.js → handlebars-Yy6fPQI_.js} +1 -1
  67. package/dist/web/assets/{html-derivative-C3oTSSCN.js → html-derivative-CchzCY1z.js} +1 -1
  68. package/dist/web/assets/{html-DvfhQbXb.js → html-lqtcy6-2.js} +1 -1
  69. package/dist/web/assets/{http-SG6zLTo7.js → http-ChclRrko.js} +1 -1
  70. package/dist/web/assets/{hurl-BWXsYlqW.js → hurl-CoOK7sXa.js} +1 -1
  71. package/dist/web/assets/{hxml-DUdMzaoh.js → hxml-fZSZ7Iyt.js} +1 -1
  72. package/dist/web/assets/{image-preview-DxgFOV1T.js → image-preview-ClAcqDlL.js} +1 -1
  73. package/dist/web/assets/index-CtJbMNNI.css +2 -0
  74. package/dist/web/assets/index-H_gScEOk.js +52 -0
  75. package/dist/web/assets/info-3K5VOQVL-Cx1ZfJbp.js +1 -0
  76. package/dist/web/assets/{input-DuAF-Stj.js → input-DXPgUtxi.js} +1 -1
  77. package/dist/web/assets/{jinja-955w9RKM.js → jinja-Y0w_KBdE.js} +1 -1
  78. package/dist/web/assets/{jison-WWfqPo50.js → jison-BILj3F2e.js} +1 -1
  79. package/dist/web/assets/{julia-BFrfnaNl.js → julia-DRfOPDts.js} +1 -1
  80. package/dist/web/assets/{just-BrZdyR_I.js → just-n3U_oODO.js} +1 -1
  81. package/dist/web/assets/keybindings-store-BqctreaI.js +1 -0
  82. package/dist/web/assets/{keybindings-store-0Wqfa7Qp.js → keybindings-store-D-CHdq-G.js} +1 -1
  83. package/dist/web/assets/keyboard-DBBaoQSu.js +1 -0
  84. package/dist/web/assets/{latex-CwtPPASD.js → latex-D6gztEOL.js} +1 -1
  85. package/dist/web/assets/{liquid-CYZuvDDd.js → liquid-DPqCEVb9.js} +1 -1
  86. package/dist/web/assets/{lua-BV095ZyY.js → lua-I40KPUm-.js} +1 -1
  87. package/dist/web/assets/markdown-renderer-BwOkiZC1.js +1 -0
  88. package/dist/web/assets/{markdown-renderer-CnRcKK3d.js → markdown-renderer-oJ9jPdQ2.js} +3 -3
  89. package/dist/web/assets/{marko-CNeljZ1C.js → marko-DMjsThSW.js} +1 -1
  90. package/dist/web/assets/{mdc-JfGapShy.js → mdc-CN2alsbo.js} +1 -1
  91. package/dist/web/assets/{media-url-Bm4PsduE.js → media-url-COUvasjn.js} +1 -1
  92. package/dist/web/assets/mobile-bottom-sheet-Cj1YBq-c.js +1 -0
  93. package/dist/web/assets/mobile-explorer-sheet-D1nM0Ik1.js +1 -0
  94. package/dist/web/assets/{nextflow-DD5C8nqX.js → nextflow-CGa4JeZ6.js} +1 -1
  95. package/dist/web/assets/{nginx-Ca7B__x_.js → nginx-CmqrIj84.js} +1 -1
  96. package/dist/web/assets/{nim-B8FogsDw.js → nim-CqhmTeMa.js} +1 -1
  97. package/dist/web/assets/notification-store-n-XEQ5pH.js +1 -0
  98. package/dist/web/assets/{number-overlay-editor-DG9xJwEG.js → number-overlay-editor-BEzJ1YeD.js} +1 -1
  99. package/dist/web/assets/{overview-panel-s53bnl0q.js → overview-panel-Bf9Gn8qV.js} +1 -1
  100. package/dist/web/assets/packet-RMMSAZCW-Bqwiy7dz.js +1 -0
  101. package/dist/web/assets/{panel-store-B4rijFKN.js → panel-store-UWCH6B-_.js} +1 -1
  102. package/dist/web/assets/{pdf-preview-DYt3aBzM.js → pdf-preview-BMHM5cR2.js} +1 -1
  103. package/dist/web/assets/{perl-BR78hS8R.js → perl-BOwpb6Tb.js} +1 -1
  104. package/dist/web/assets/{php-DXBOMC9z.js → php-wT9fVxxu.js} +1 -1
  105. package/dist/web/assets/pie-UPGHQEXC-BXSJ2God.js +1 -0
  106. package/dist/web/assets/{postgres-viewer-BvoWSuoZ.js → postgres-viewer-D8rfEY5b.js} +3 -3
  107. package/dist/web/assets/process-table-CsQ-hDJr.js +1 -0
  108. package/dist/web/assets/{project-store-BuY7rxn_.js → project-store-CKAOr23O.js} +1 -1
  109. package/dist/web/assets/{pug-CvTwVVKV.js → pug-DdlNnj-p.js} +1 -1
  110. package/dist/web/assets/{qml-CPSoZ1-B.js → qml-C5zBdydf.js} +1 -1
  111. package/dist/web/assets/radar-KQ55EAFF-D6XrM7HH.js +1 -0
  112. package/dist/web/assets/{razor-BUA1YKtF.js → razor-DqcJX4iq.js} +1 -1
  113. package/dist/web/assets/remote-desktop-mobile-sheet-CTiipn4k.js +2 -0
  114. package/dist/web/assets/remote-desktop-mobile-view-Cf5B0dKo.js +1 -0
  115. package/dist/web/assets/remote-desktop-stats-overlay-vhJBIW7s.js +1 -0
  116. package/dist/web/assets/remote-desktop-window-content-wCJUfI6G.js +1 -0
  117. package/dist/web/assets/{rst-BJsq7ISz.js → rst-DiMiMYUO.js} +1 -1
  118. package/dist/web/assets/{ruby-DSDMeYf5.js → ruby-DK62iflA.js} +1 -1
  119. package/dist/web/assets/{run-in-terminal-C9xpskFn.js → run-in-terminal-DJSL6nwZ.js} +1 -1
  120. package/dist/web/assets/{sas-B1nJOjbN.js → sas-6QPGJRa5.js} +1 -1
  121. package/dist/web/assets/{scroll-area-DBcG1fQo.js → scroll-area-DXGXeiRz.js} +1 -1
  122. package/dist/web/assets/{scss-BZFyRiQn.js → scss-DmUqh48_.js} +1 -1
  123. package/dist/web/assets/{send-to-chat-BIg70MUZ.js → send-to-chat-BOony0sO.js} +1 -1
  124. package/dist/web/assets/settings-store-BpcY5aYE.js +2 -0
  125. package/dist/web/assets/settings-tab-BVqRO01j.js +1 -0
  126. package/dist/web/assets/settings-tab-FH581A_i.js +3 -0
  127. package/dist/web/assets/{shaderlab-laINRXBo.js → shaderlab-DopFR1mi.js} +1 -1
  128. package/dist/web/assets/{shellsession-BpOOvWVR.js → shellsession-Bp_TGuXb.js} +1 -1
  129. package/dist/web/assets/{soy-BW4d91Sa.js → soy-DEB9KEFb.js} +1 -1
  130. package/dist/web/assets/{sparql-BsvvyoHn.js → sparql-C8lEwO1p.js} +1 -1
  131. package/dist/web/assets/{sql-query-editor-qQce5QJ6.js → sql-query-editor-MMfk0Kbz.js} +1 -1
  132. package/dist/web/assets/{sqlite-viewer-yhc0YNvu.js → sqlite-viewer-vglzrgnZ.js} +1 -1
  133. package/dist/web/assets/{stata-K-dZInUt.js → stata-DVep4apN.js} +1 -1
  134. package/dist/web/assets/{surrealql-CpNp2sgf.js → surrealql-fSlJApOA.js} +1 -1
  135. package/dist/web/assets/{svelte-BG6iBA8R.js → svelte-DezWBEDi.js} +1 -1
  136. package/dist/web/assets/{switch-_NcrktLe.js → switch-DqL_ZjB_.js} +1 -1
  137. package/dist/web/assets/system-monitor-body-BYUfyF8f.js +2 -0
  138. package/dist/web/assets/system-monitor-tab-CKGXB8lH.js +1 -0
  139. package/dist/web/assets/{system-monitor-window-content-CaOhD0AU.js → system-monitor-window-content-CiJ2yIsr.js} +1 -1
  140. package/dist/web/assets/tab-host-window-content-DYlXR0cg.js +1 -0
  141. package/dist/web/assets/{tab-store-tFnyPmm4.js → tab-store-D2thn4Vv.js} +1 -1
  142. package/dist/web/assets/team-member-sheet-Bnygu7Xw.js +2 -0
  143. package/dist/web/assets/{team-member-window-content-B1jkKkDi.js → team-member-window-content-qrgFTC96.js} +1 -1
  144. package/dist/web/assets/{templ-gxNKXmt3.js → templ-D0zB42a9.js} +1 -1
  145. package/dist/web/assets/{terminal-tab-UvwM0sty.js → terminal-tab-D_-R5rGm.js} +3 -3
  146. package/dist/web/assets/{tex-C0ON27g5.js → tex-BJiPijcH.js} +1 -1
  147. package/dist/web/assets/{tool-cards-jB7GT-wj.js → tool-cards-BW7Xc20J.js} +4 -4
  148. package/dist/web/assets/treemap-KZPCXAKY-BobWhcHN.js +1 -0
  149. package/dist/web/assets/{ts-tags-BX_CTVgQ.js → ts-tags-Dcr4u6y5.js} +1 -1
  150. package/dist/web/assets/{twig-CwL7XnU0.js → twig-B-SQU0bg.js} +1 -1
  151. package/dist/web/assets/{use-blob-url-DcRcjNTE.js → use-blob-url-BtPlqwLr.js} +1 -1
  152. package/dist/web/assets/use-monaco-theme-DQY1ZSaf.js +1 -0
  153. package/dist/web/assets/{use-open-team-member-1hRsHLKg.js → use-open-team-member-C5LQ_TCM.js} +1 -1
  154. package/dist/web/assets/use-visual-viewport-pVxLfE5B.js +1 -0
  155. package/dist/web/assets/{use-websocket-rxJLbFqy.js → use-websocket-l39Odhlp.js} +1 -1
  156. package/dist/web/assets/{vendor-mermaid-CHTa6D69.js → vendor-mermaid-CtDsSxbM.js} +1 -1
  157. package/dist/web/assets/{video-preview-DMhnf2Gi.js → video-preview-C5AhkIDl.js} +1 -1
  158. package/dist/web/assets/{vue-Bykm6wSS.js → vue-Cw10Sfbh.js} +1 -1
  159. package/dist/web/assets/{vue-html-CETdK_iV.js → vue-html-Cc5C_Z_w.js} +1 -1
  160. package/dist/web/assets/{vue-vine-DiJb6fO5.js → vue-vine-BuNMlnvu.js} +1 -1
  161. package/dist/web/assets/window-pip-registry-C7oz3Dfy.js +1 -0
  162. package/dist/web/assets/window-store-B5ZswdYg.js +1 -0
  163. package/dist/web/assets/{xml-BNw8J5YW.js → xml-DqT1wkky.js} +1 -1
  164. package/dist/web/assets/{xsl-CsG9L31b.js → xsl-DLkge3A2.js} +1 -1
  165. package/dist/web/index.html +45 -42
  166. package/dist/web/sw.js +1 -1
  167. package/docs/architecture/ai-chat-and-providers.md +3 -0
  168. package/docs/architecture/workspace-and-ui.md +8 -0
  169. package/docs/design-guidelines.md +3 -2
  170. package/package.json +1 -1
  171. package/src/cli/commands/backup-cmd.ts +119 -0
  172. package/src/index.ts +3 -0
  173. package/src/providers/claude-agent-sdk.ts +29 -1
  174. package/src/server/index.ts +42 -15
  175. package/src/server/routes/accounts.ts +30 -2
  176. package/src/server/routes/chat.ts +8 -1
  177. package/src/server/routes/named-tunnel.ts +5 -2
  178. package/src/server/routes/remote-desktop.ts +64 -0
  179. package/src/server/routes/settings.ts +1 -0
  180. package/src/server/ws/chat.ts +126 -7
  181. package/src/server/ws/remote-desktop.ts +86 -0
  182. package/src/services/account.service.ts +106 -23
  183. package/src/services/db-backup/db-backup-paths.ts +66 -0
  184. package/src/services/db-backup/db-backup-restore.ts +105 -0
  185. package/src/services/db-backup/db-backup-retention.ts +96 -0
  186. package/src/services/db-backup/db-backup-sync.ts +113 -0
  187. package/src/services/db-backup/db-backup.service.ts +61 -0
  188. package/src/services/db.service.ts +29 -1
  189. package/src/services/fs-credential-path-guard.ts +19 -1
  190. package/src/services/named-tunnel/cloudflared-login.service.ts +2 -2
  191. package/src/services/nested-subagent-spy.ts +206 -0
  192. package/src/services/remote-desktop/access-unit-assembler.ts +90 -0
  193. package/src/services/remote-desktop/avc1-codec-string.ts +18 -0
  194. package/src/services/remote-desktop/nal-splitter.ts +100 -0
  195. package/src/services/remote-desktop/remote-desktop-capture.ts +120 -0
  196. package/src/services/remote-desktop/remote-desktop-encoder-args.ts +42 -0
  197. package/src/services/remote-desktop/remote-desktop-flag.ts +10 -0
  198. package/src/services/remote-desktop/remote-desktop-input.ts +217 -0
  199. package/src/services/remote-desktop/remote-desktop-nonce.ts +38 -0
  200. package/src/services/remote-desktop/remote-desktop-session.ts +189 -0
  201. package/src/services/remote-desktop/remote-desktop-vk-map.ts +41 -0
  202. package/src/services/subagent-transcript-merger.ts +92 -75
  203. package/src/services/subprocess-retention.ts +29 -6
  204. package/src/services/supervisor.ts +58 -0
  205. package/src/services/team-member-activity/subagent-transcript-index.ts +56 -0
  206. package/src/types/chat.ts +2 -0
  207. package/src/web/app.tsx +8 -0
  208. package/src/web/components/chat/account-dialogs.tsx +11 -4
  209. package/src/web/components/chat/question-card.tsx +9 -2
  210. package/src/web/components/chat/usage-badge.tsx +17 -3
  211. package/src/web/components/floating-window/floating-window.tsx +8 -1
  212. package/src/web/components/floating-window/open-tab-in-pip.ts +78 -0
  213. package/src/web/components/floating-window/window-content-registry.ts +2 -0
  214. package/src/web/components/floating-window/window-pip-registry.ts +46 -0
  215. package/src/web/components/floating-window/window-store-types.ts +1 -1
  216. package/src/web/components/layout/mobile-drawer.tsx +38 -27
  217. package/src/web/components/layout/nav-section-rail.tsx +10 -2
  218. package/src/web/components/layout/tab-pop-out-menu-item.tsx +25 -2
  219. package/src/web/components/remote-desktop/open-remote-desktop.ts +26 -0
  220. package/src/web/components/remote-desktop/remote-desktop-coords.ts +98 -0
  221. package/src/web/components/remote-desktop/remote-desktop-frame-timestamp.ts +21 -0
  222. package/src/web/components/remote-desktop/remote-desktop-gesture-classifiers.ts +56 -0
  223. package/src/web/components/remote-desktop/remote-desktop-mobile-key-bar.tsx +131 -0
  224. package/src/web/components/remote-desktop/remote-desktop-mobile-sheet.tsx +43 -0
  225. package/src/web/components/remote-desktop/remote-desktop-mobile-toolbar.tsx +79 -0
  226. package/src/web/components/remote-desktop/remote-desktop-mobile-view.tsx +190 -0
  227. package/src/web/components/remote-desktop/remote-desktop-stats-overlay.tsx +54 -0
  228. package/src/web/components/remote-desktop/remote-desktop-stats.ts +37 -0
  229. package/src/web/components/remote-desktop/remote-desktop-touch-gesture-tracker.ts +195 -0
  230. package/src/web/components/remote-desktop/remote-desktop-window-content.tsx +72 -0
  231. package/src/web/components/remote-desktop/remote-desktop-ws-url.ts +22 -0
  232. package/src/web/components/remote-desktop/use-h264-canvas-decoder.ts +175 -0
  233. package/src/web/components/remote-desktop/use-remote-desktop-available.ts +29 -0
  234. package/src/web/components/remote-desktop/use-remote-desktop-connection.ts +147 -0
  235. package/src/web/components/remote-desktop/use-remote-desktop-mobile-open-state.ts +18 -0
  236. package/src/web/components/remote-desktop/use-remote-desktop-touch.ts +224 -0
  237. package/src/web/components/remote-desktop/use-remote-desktop-virtual-keyboard.ts +50 -0
  238. package/src/web/components/remote-desktop/use-remote-input-capture.ts +92 -0
  239. package/src/web/components/tunnels/named-tunnel/named-tunnel-copy.ts +59 -59
  240. package/src/web/components/tunnels/named-tunnel/named-tunnel-section.tsx +2 -2
  241. package/src/web/hooks/use-notification-badge.ts +19 -11
  242. package/src/web/lib/feature-badges.ts +2 -0
  243. package/src/web/stores/settings-store.ts +13 -0
  244. package/src/web/stores/streaming-store.ts +21 -2
  245. package/dist/web/assets/api-client-BogqsFna.js +0 -1
  246. package/dist/web/assets/api-settings-DfXViPKz.js +0 -1
  247. package/dist/web/assets/architecture-PBZL5I3N-BzFm9ELu.js +0 -1
  248. package/dist/web/assets/chat-tab-DzK8LGti.js +0 -15
  249. package/dist/web/assets/code-editor-CuMfPN9X.js +0 -10
  250. package/dist/web/assets/diff-viewer-eaUBjLCH.js +0 -4
  251. package/dist/web/assets/explorer-body-DkCkSVrN.js +0 -2
  252. package/dist/web/assets/explorer-window-content-MsnTVngu.js +0 -1
  253. package/dist/web/assets/gitGraph-HDMCJU4V-B72vcb2r.js +0 -1
  254. package/dist/web/assets/glide-data-grid-B0IY6n_Q.js +0 -138
  255. package/dist/web/assets/group-chat-tab-DRLDHOp6.js +0 -1
  256. package/dist/web/assets/index-CBn4SOoT.js +0 -52
  257. package/dist/web/assets/index-DWpNR3nB.css +0 -2
  258. package/dist/web/assets/info-3K5VOQVL-3irPxRK_.js +0 -1
  259. package/dist/web/assets/keybindings-store-d54pv2NK.js +0 -1
  260. package/dist/web/assets/markdown-renderer-BTS8ifdu.js +0 -1
  261. package/dist/web/assets/mobile-bottom-sheet-M5iZDX7y.js +0 -1
  262. package/dist/web/assets/mobile-explorer-sheet-BqK2-WuN.js +0 -1
  263. package/dist/web/assets/notification-store-Yvy1I8gd.js +0 -1
  264. package/dist/web/assets/packet-RMMSAZCW-Z5jWjFCV.js +0 -1
  265. package/dist/web/assets/pie-UPGHQEXC-Bq7UMi8P.js +0 -1
  266. package/dist/web/assets/process-table-BYwKigiD.js +0 -1
  267. package/dist/web/assets/radar-KQ55EAFF-BLo4BOTD.js +0 -1
  268. package/dist/web/assets/settings-store-GV5pkkke.js +0 -2
  269. package/dist/web/assets/settings-tab-BePvIcjd.js +0 -3
  270. package/dist/web/assets/settings-tab-Br-lJSPa.js +0 -1
  271. package/dist/web/assets/system-monitor-body-D8Z5QXBB.js +0 -2
  272. package/dist/web/assets/system-monitor-tab-BclYXFTe.js +0 -1
  273. package/dist/web/assets/tab-host-window-content-f_Rc5rJK.js +0 -1
  274. package/dist/web/assets/team-member-sheet-QDHoYCs0.js +0 -2
  275. package/dist/web/assets/treemap-KZPCXAKY-dyrE6SW2.js +0 -1
  276. package/dist/web/assets/use-monaco-theme--9tOOkDA.js +0 -1
  277. package/dist/web/assets/window-store-CQ1lghvv.js +0 -1
  278. /package/dist/web/assets/{abap-DGlrX_8G.js → abap-CKgBNk5K.js} +0 -0
  279. /package/dist/web/assets/{actionscript-3-CUqRtUJE.js → actionscript-3-oARhAY6c.js} +0 -0
  280. /package/dist/web/assets/{ada-CY3Spa33.js → ada-2vi8ASy3.js} +0 -0
  281. /package/dist/web/assets/{andromeeda-Lxs2TJn7.js → andromeeda-B3vMjPKd.js} +0 -0
  282. /package/dist/web/assets/{apache-DfxgGdgM.js → apache-DnIEr338.js} +0 -0
  283. /package/dist/web/assets/{apex-QVDF1dnI.js → apex-B7Qs5AY5.js} +0 -0
  284. /package/dist/web/assets/{api-client-oY1tC3tM.js → api-client-BkGKTjsq.js} +0 -0
  285. /package/dist/web/assets/{applescript-pwJ8QXh-.js → applescript-EbQNUCWb.js} +0 -0
  286. /package/dist/web/assets/{apply-theme-B_bMVL6a.js → apply-theme-Dd_cqGa-.js} +0 -0
  287. /package/dist/web/assets/{ara-mPjeX5YX.js → ara-CtHdhFRw.js} +0 -0
  288. /package/dist/web/assets/{asciidoc-Bf7LsDkI.js → asciidoc-BH6qin_O.js} +0 -0
  289. /package/dist/web/assets/{asm-rRKC1OUx.js → asm-D1XU1ACk.js} +0 -0
  290. /package/dist/web/assets/{aurora-x-DwQr0DkG.js → aurora-x-BkhcxkuS.js} +0 -0
  291. /package/dist/web/assets/{awk-Bpt768fN.js → awk-DijzB6o7.js} +0 -0
  292. /package/dist/web/assets/{ayu-dark-D9lwQHMs.js → ayu-dark-tysG77WC.js} +0 -0
  293. /package/dist/web/assets/{ayu-light-B4-ptrU5.js → ayu-light-Dg0WMETC.js} +0 -0
  294. /package/dist/web/assets/{ayu-mirage-DAyRzH5Q.js → ayu-mirage--RsJCjC4.js} +0 -0
  295. /package/dist/web/assets/{ballerina-Cgdpj1HC.js → ballerina-DT_5-l82.js} +0 -0
  296. /package/dist/web/assets/{bat-4FrN36S2.js → bat-BRpDlkv0.js} +0 -0
  297. /package/dist/web/assets/{beancount-DkcxQER3.js → beancount--8Df-WGG.js} +0 -0
  298. /package/dist/web/assets/{berry-Dnuut-Ml.js → berry-Bc9fo35J.js} +0 -0
  299. /package/dist/web/assets/{bibtex-CCDwmbuA.js → bibtex-CwyBpp9X.js} +0 -0
  300. /package/dist/web/assets/{bicep-BC0ryoGM.js → bicep-DiyH2Bs7.js} +0 -0
  301. /package/dist/web/assets/{bird2-C50V_A-0.js → bird2-CHgK4a2A.js} +0 -0
  302. /package/dist/web/assets/{c-Ddorhq0U.js → c-BM1qL6-f.js} +0 -0
  303. /package/dist/web/assets/{c3-DdIf5r9E.js → c3-D2oOjrP7.js} +0 -0
  304. /package/dist/web/assets/{cadence-s9pd1Wng.js → cadence-nS45wqF6.js} +0 -0
  305. /package/dist/web/assets/{can-open-in-ppm-C6hZV1OP.js → can-open-in-ppm-DwP9KU8o.js} +0 -0
  306. /package/dist/web/assets/{catppuccin-frappe-CzJNTyu-.js → catppuccin-frappe-BpnJHiCj.js} +0 -0
  307. /package/dist/web/assets/{catppuccin-latte-BNpt-m7s.js → catppuccin-latte-Bb95Wj3h.js} +0 -0
  308. /package/dist/web/assets/{catppuccin-macchiato-FYGkfsJ5.js → catppuccin-macchiato-Btx8c17W.js} +0 -0
  309. /package/dist/web/assets/{catppuccin-mocha-it2g5D1q.js → catppuccin-mocha-B4DZOUGI.js} +0 -0
  310. /package/dist/web/assets/{clarity-BVFBTjMe.js → clarity-1ps69jU6.js} +0 -0
  311. /package/dist/web/assets/{clipboard-BWznymz7.js → clipboard-CrHX8AYw.js} +0 -0
  312. /package/dist/web/assets/{clojure-BSYvXMI8.js → clojure-BklC0hKV.js} +0 -0
  313. /package/dist/web/assets/{cmake-DGEqH-Np.js → cmake-B573LYAQ.js} +0 -0
  314. /package/dist/web/assets/{codeowners-D8FCOxWg.js → codeowners-BoYeX89M.js} +0 -0
  315. /package/dist/web/assets/{codeql-COV0eluG.js → codeql-7Dhf5Udk.js} +0 -0
  316. /package/dist/web/assets/{common-lisp-xHnZUlgt.js → common-lisp-m1nxlthv.js} +0 -0
  317. /package/dist/web/assets/{coq-Cs_2i3Xg.js → coq-DNhGmd4a.js} +0 -0
  318. /package/dist/web/assets/{csharp-B1ONB4PT.js → csharp-P-nAurtS.js} +0 -0
  319. /package/dist/web/assets/{css-BgGQbvrZ.js → css-LYbSeh3-.js} +0 -0
  320. /package/dist/web/assets/{csv-CNHmQBzs.js → csv-DHZZZm7X.js} +0 -0
  321. /package/dist/web/assets/{csv-parser-DWR24J17.js → csv-parser-CY9Ggtg5.js} +0 -0
  322. /package/dist/web/assets/{cue-ZXAKzwIy.js → cue-D8yLUOle.js} +0 -0
  323. /package/dist/web/assets/{cypher-4PL5bmT5.js → cypher-9cbW8RYA.js} +0 -0
  324. /package/dist/web/assets/{d-CEsV3RT2.js → d-ZihzhxqI.js} +0 -0
  325. /package/dist/web/assets/{dark-plus-NtFnYSDz.js → dark-plus-C8xIA1u3.js} +0 -0
  326. /package/dist/web/assets/{dart-CKHn4mDf.js → dart-CBm6jKc7.js} +0 -0
  327. /package/dist/web/assets/{data-grid-types-CxU8oo-j.js → data-grid-types-DkEQ-Na9.js} +0 -0
  328. /package/dist/web/assets/{dax-Cmkj1I_M.js → dax-B3cx6YeV.js} +0 -0
  329. /package/dist/web/assets/{desktop-DJLalvPQ.js → desktop-C03SpXth.js} +0 -0
  330. /package/dist/web/assets/{diff-BnS2wpj6.js → diff-D8LnnvFY.js} +0 -0
  331. /package/dist/web/assets/{dist-uYjs3Wv6.js → dist-C4XoS_zo.js} +0 -0
  332. /package/dist/web/assets/{dist-CuIdRyUB.js → dist-Do3fljss.js} +0 -0
  333. /package/dist/web/assets/{docker-CovNy7ml.js → docker-DwFiOQWW.js} +0 -0
  334. /package/dist/web/assets/{dotenv-Gh4w3I6B.js → dotenv-CVJ_vFSQ.js} +0 -0
  335. /package/dist/web/assets/{dracula-NF0tAHXl.js → dracula-DMw8UACY.js} +0 -0
  336. /package/dist/web/assets/{dracula-soft-Cj_qBurK.js → dracula-soft-BnB3nDrA.js} +0 -0
  337. /package/dist/web/assets/{dream-maker-CRuZPcl7.js → dream-maker-2riXUQXo.js} +0 -0
  338. /package/dist/web/assets/{emacs-lisp-BACmC9qj.js → emacs-lisp-BQdYaiB5.js} +0 -0
  339. /package/dist/web/assets/{everforest-dark-Dd9av4QG.js → everforest-dark-C2FPIWdj.js} +0 -0
  340. /package/dist/web/assets/{everforest-light-C-jsss-B.js → everforest-light-DMOWf9sD.js} +0 -0
  341. /package/dist/web/assets/{fennel-BJA9XnL8.js → fennel-JtSHp0vW.js} +0 -0
  342. /package/dist/web/assets/{fish-CKlhNSc2.js → fish-CflDcRfe.js} +0 -0
  343. /package/dist/web/assets/{fluent-wfglRk5D.js → fluent-D4eu6Vac.js} +0 -0
  344. /package/dist/web/assets/{format-bytes-utwHMTry.js → format-bytes-DR3228bu.js} +0 -0
  345. /package/dist/web/assets/{fortran-free-form-BlXCdid3.js → fortran-free-form-hO4RuC9Z.js} +0 -0
  346. /package/dist/web/assets/{gdscript-BwAVG6Et.js → gdscript-D7n3a8VM.js} +0 -0
  347. /package/dist/web/assets/{gdshader-DPc_T7ib.js → gdshader-BhUmrlzG.js} +0 -0
  348. /package/dist/web/assets/{genie-B-zZSLCX.js → genie-VbyftqBp.js} +0 -0
  349. /package/dist/web/assets/{gherkin-CJ0ajpGs.js → gherkin-BZzhvUUb.js} +0 -0
  350. /package/dist/web/assets/{github-dark-Dz8ZkzSP.js → github-dark-C4q41woW.js} +0 -0
  351. /package/dist/web/assets/{github-dark-default-CNUYo_kp.js → github-dark-default-CMNi7dQX.js} +0 -0
  352. /package/dist/web/assets/{github-dark-dimmed-241z_Ycx.js → github-dark-dimmed-BRf_-LlY.js} +0 -0
  353. /package/dist/web/assets/{github-dark-high-contrast-7r6yZZYH.js → github-dark-high-contrast-xanKBsRI.js} +0 -0
  354. /package/dist/web/assets/{github-light-C8gmZn-D.js → github-light-BEQeNQCs.js} +0 -0
  355. /package/dist/web/assets/{github-light-default-GAEgq1eA.js → github-light-default-ceQrtdXg.js} +0 -0
  356. /package/dist/web/assets/{github-light-high-contrast-3z91352E.js → github-light-high-contrast-doKbXzgW.js} +0 -0
  357. /package/dist/web/assets/{gleam-C7vxW5zd.js → gleam-Chzqf4fT.js} +0 -0
  358. /package/dist/web/assets/{gn-CyJIXwHP.js → gn-CCN_-6fp.js} +0 -0
  359. /package/dist/web/assets/{gnuplot-CqLbGgPd.js → gnuplot-CIplgwwP.js} +0 -0
  360. /package/dist/web/assets/{go-DdDcK9aX.js → go-Cjj-9dRs.js} +0 -0
  361. /package/dist/web/assets/{groovy-UYHggEqH.js → groovy-PqD4K7X1.js} +0 -0
  362. /package/dist/web/assets/{gruvbox-dark-hard-B4ierkJn.js → gruvbox-dark-hard-BAbpUSF-.js} +0 -0
  363. /package/dist/web/assets/{gruvbox-dark-medium-DYwkDSZ6.js → gruvbox-dark-medium-BWcWXDBB.js} +0 -0
  364. /package/dist/web/assets/{gruvbox-dark-soft-CVL4P-Lz.js → gruvbox-dark-soft-Dkx1okkk.js} +0 -0
  365. /package/dist/web/assets/{gruvbox-light-hard-BJ0LewUZ.js → gruvbox-light-hard-WVsDK0MM.js} +0 -0
  366. /package/dist/web/assets/{gruvbox-light-medium-CaqqjeGJ.js → gruvbox-light-medium-tiR9t7gr.js} +0 -0
  367. /package/dist/web/assets/{gruvbox-light-soft-ByKMoVNr.js → gruvbox-light-soft-HzHohaX6.js} +0 -0
  368. /package/dist/web/assets/{haskell-BilYRVPp.js → haskell-B1eIZfr6.js} +0 -0
  369. /package/dist/web/assets/{haxe-Brs9Bj1s.js → haxe-BcDdi5xr.js} +0 -0
  370. /package/dist/web/assets/{hcl-ClrZpol-.js → hcl-Dq-HKVni.js} +0 -0
  371. /package/dist/web/assets/{hjson-9PvHnYMX.js → hjson-Dr9D9WVU.js} +0 -0
  372. /package/dist/web/assets/{hlsl-BbBk4vqV.js → hlsl-Bo8t2FuZ.js} +0 -0
  373. /package/dist/web/assets/{horizon-E5Py0cny.js → horizon-DWG5IXc-.js} +0 -0
  374. /package/dist/web/assets/{horizon-bright-DVCsNGEk.js → horizon-bright-DFr9wTTx.js} +0 -0
  375. /package/dist/web/assets/{houston-JdFEax9Y.js → houston-NAVNhkup.js} +0 -0
  376. /package/dist/web/assets/{hy-W_W3tLKT.js → hy-DnyvUk3B.js} +0 -0
  377. /package/dist/web/assets/{image-gallery-5IURd6db.js → image-gallery-DoFdNF9i.js} +0 -0
  378. /package/dist/web/assets/{imba-CfAqJz7T.js → imba-CPbR5W_A.js} +0 -0
  379. /package/dist/web/assets/{ini-C_3gvKWs.js → ini-lc8_Lt0y.js} +0 -0
  380. /package/dist/web/assets/{java-CKqtXZ-j.js → java-B0KCo2vK.js} +0 -0
  381. /package/dist/web/assets/{javascript-C7gSEfae.js → javascript-YqEbNRpd.js} +0 -0
  382. /package/dist/web/assets/{json-BoN6e11T.js → json-Co0WtOlh.js} +0 -0
  383. /package/dist/web/assets/{json5-CgE7C8HW.js → json5-2Jhb0QUN.js} +0 -0
  384. /package/dist/web/assets/{jsonc-DjyoattY.js → jsonc-e72F11nU.js} +0 -0
  385. /package/dist/web/assets/{jsonl-lI82wGSw.js → jsonl-Bff8IPye.js} +0 -0
  386. /package/dist/web/assets/{jsonnet-CszEI8va.js → jsonnet-DRCkb-_H.js} +0 -0
  387. /package/dist/web/assets/{jssm-CZXDlszb.js → jssm-C6UiwVEm.js} +0 -0
  388. /package/dist/web/assets/{jsx-BeRz0eEz.js → jsx-u1aZlnfA.js} +0 -0
  389. /package/dist/web/assets/{kanagawa-dragon-BOmnUiyd.js → kanagawa-dragon-De-RQFV3.js} +0 -0
  390. /package/dist/web/assets/{kanagawa-lotus-ffU_P5w-.js → kanagawa-lotus-K8elE4QU.js} +0 -0
  391. /package/dist/web/assets/{kanagawa-wave-DKTJZrMu.js → kanagawa-wave-BrStl_zx.js} +0 -0
  392. /package/dist/web/assets/{kdl-BWECJveH.js → kdl-CLCfMeZy.js} +0 -0
  393. /package/dist/web/assets/{kotlin-DpVwBCSt.js → kotlin-DEEC3pNh.js} +0 -0
  394. /package/dist/web/assets/{kusto-BgVqjUZA.js → kusto-DTqRhCup.js} +0 -0
  395. /package/dist/web/assets/{laserwave-CEVCi8em.js → laserwave-DsgpUSbA.js} +0 -0
  396. /package/dist/web/assets/{lean-CGyfRhLJ.js → lean-Dl9MI0Y3.js} +0 -0
  397. /package/dist/web/assets/{less-B0_H10Ku.js → less-Bg21rao6.js} +0 -0
  398. /package/dist/web/assets/{lib-hrC3bruJ.js → lib-CXUwxw1M.js} +0 -0
  399. /package/dist/web/assets/{libesm-DVpFmwJu.js → libesm-CVkncAQz.js} +0 -0
  400. /package/dist/web/assets/{light-plus-DqzMqPID.js → light-plus-DGNbbK1V.js} +0 -0
  401. /package/dist/web/assets/{llvm-9ucnfNiF.js → llvm-91_ZX3p-.js} +0 -0
  402. /package/dist/web/assets/{loader-circle-Dpqcv0eS.js → loader-circle-DouKgIXy.js} +0 -0
  403. /package/dist/web/assets/{lock-DldbT31x.js → lock-CNxUGpTk.js} +0 -0
  404. /package/dist/web/assets/{log-m6A2dOSj.js → log-DSXzbq8u.js} +0 -0
  405. /package/dist/web/assets/{logo-ByPcsC9P.js → logo-BQRNd4tc.js} +0 -0
  406. /package/dist/web/assets/{luau-DFCo8nBn.js → luau-BE-TMpiZ.js} +0 -0
  407. /package/dist/web/assets/{make-C90w3MeA.js → make-ePYGKTpc.js} +0 -0
  408. /package/dist/web/assets/{markdown-DT0uywZG.js → markdown-Cst3wgMW.js} +0 -0
  409. /package/dist/web/assets/{material-theme-BVUO93tJ.js → material-theme-9XyvnNLb.js} +0 -0
  410. /package/dist/web/assets/{material-theme-darker-xXgGDwnF.js → material-theme-darker-H1VN4MTS.js} +0 -0
  411. /package/dist/web/assets/{material-theme-lighter-BjStPzWB.js → material-theme-lighter-DAreBbuF.js} +0 -0
  412. /package/dist/web/assets/{material-theme-ocean-wFmSiKjy.js → material-theme-ocean-BpTR-3II.js} +0 -0
  413. /package/dist/web/assets/{material-theme-palenight-e-0CtRoe.js → material-theme-palenight-DUNzX711.js} +0 -0
  414. /package/dist/web/assets/{matlab-BNaNQAUH.js → matlab-BpYNufBU.js} +0 -0
  415. /package/dist/web/assets/{mdx-DfpVWAqO.js → mdx-BYjfe1DK.js} +0 -0
  416. /package/dist/web/assets/{mermaid-Bpq7wFCP.js → mermaid-DPCcIW9B.js} +0 -0
  417. /package/dist/web/assets/{min-dark-CL2Y0WH6.js → min-dark-DnGOAGCo.js} +0 -0
  418. /package/dist/web/assets/{min-light-DOgr_IdP.js → min-light-CwV-o31y.js} +0 -0
  419. /package/dist/web/assets/{mipsasm-C9ie2zlB.js → mipsasm-DdeCPC97.js} +0 -0
  420. /package/dist/web/assets/{mojo-Md2A2sJy.js → mojo-BmxxZ3iM.js} +0 -0
  421. /package/dist/web/assets/{monitor-4vWfqzuN.js → monitor-C835ucWH.js} +0 -0
  422. /package/dist/web/assets/{monokai-nbXIEySB.js → monokai-Cx6cQ-MA.js} +0 -0
  423. /package/dist/web/assets/{moonbit-CJ-T-s4A.js → moonbit-D2-Gj5vj.js} +0 -0
  424. /package/dist/web/assets/{move-DwEehLIK.js → move-DSWGoQa8.js} +0 -0
  425. /package/dist/web/assets/{narrat-CuF4sIKr.js → narrat-D9TnZSSu.js} +0 -0
  426. /package/dist/web/assets/{nextflow-groovy-1dYhfPfq.js → nextflow-groovy-BbL-8PwD.js} +0 -0
  427. /package/dist/web/assets/{night-owl-gZS5mfzY.js → night-owl-CZitKm5d.js} +0 -0
  428. /package/dist/web/assets/{night-owl-light-DDAster5.js → night-owl-light-DJVX1Pno.js} +0 -0
  429. /package/dist/web/assets/{nix-BlbEYs2c.js → nix-R6hr3oln.js} +0 -0
  430. /package/dist/web/assets/{nord-BRiPb8Vy.js → nord-ChGHya3l.js} +0 -0
  431. /package/dist/web/assets/{nushell-DgWicB1i.js → nushell-QQWB-GWx.js} +0 -0
  432. /package/dist/web/assets/{objective-c-D8mPZ42Y.js → objective-c-C0pZ6y73.js} +0 -0
  433. /package/dist/web/assets/{objective-cpp-B0pNXzuj.js → objective-cpp-BdzVM31a.js} +0 -0
  434. /package/dist/web/assets/{ocaml-lQd7aK5d.js → ocaml-D4KktNlS.js} +0 -0
  435. /package/dist/web/assets/{odin-CxtIOBjm.js → odin-Y-fypmGT.js} +0 -0
  436. /package/dist/web/assets/{one-dark-pro-T_UFnWfe.js → one-dark-pro-BwecIxQL.js} +0 -0
  437. /package/dist/web/assets/{one-light-LBVqwyV-.js → one-light-Cz7UVzLU.js} +0 -0
  438. /package/dist/web/assets/{openscad-C5dnS6J7.js → openscad-DtRe3qYD.js} +0 -0
  439. /package/dist/web/assets/{pascal-CmWCWQML.js → pascal-Ck6I1fGG.js} +0 -0
  440. /package/dist/web/assets/{pkl-BWWOtDQk.js → pkl-DdFK94VW.js} +0 -0
  441. /package/dist/web/assets/{plastic-ekhupckF.js → plastic-BQSAzZwR.js} +0 -0
  442. /package/dist/web/assets/{play-CpCAoQRZ.js → play-CJlWpVMw.js} +0 -0
  443. /package/dist/web/assets/{plsql-BPsDHXx4.js → plsql-DjXg_CP0.js} +0 -0
  444. /package/dist/web/assets/{po-BX2FiuW2.js → po-Bw2SuM4r.js} +0 -0
  445. /package/dist/web/assets/{poimandres-DzXOdZcN.js → poimandres-YMK6c5VN.js} +0 -0
  446. /package/dist/web/assets/{polar-DDRXgJzG.js → polar-BMu17-O0.js} +0 -0
  447. /package/dist/web/assets/{portal-container-context-a399Hd9F.js → portal-container-context-DSGdlXrJ.js} +0 -0
  448. /package/dist/web/assets/{postcss-BHW6XBhx.js → postcss-DWib10RZ.js} +0 -0
  449. /package/dist/web/assets/{powerquery-B89c2NBb.js → powerquery-Dht4CK0b.js} +0 -0
  450. /package/dist/web/assets/{powershell-Dvxrd3Tu.js → powershell-DvrCg8ji.js} +0 -0
  451. /package/dist/web/assets/{prisma-lMK8EFLU.js → prisma-DbodYvCM.js} +0 -0
  452. /package/dist/web/assets/{prolog-D0YrtMG4.js → prolog-DZ-m6ysB.js} +0 -0
  453. /package/dist/web/assets/{proto-J0mM25aw.js → proto-HHpG3tGO.js} +0 -0
  454. /package/dist/web/assets/{puppet-CUT0xkhh.js → puppet-BljHe_75.js} +0 -0
  455. /package/dist/web/assets/{purescript-CoJWSGRp.js → purescript-DzCiFR0B.js} +0 -0
  456. /package/dist/web/assets/{python-BBrmuz6P.js → python-B5BQ27cC.js} +0 -0
  457. /package/dist/web/assets/{qmldir-CUtzs4Q7.js → qmldir-CYlcXrnn.js} +0 -0
  458. /package/dist/web/assets/{qss-Dm1RZ8aL.js → qss-Ce7T_TEg.js} +0 -0
  459. /package/dist/web/assets/{r-BGP7qZTe.js → r-Bku-2Jp_.js} +0 -0
  460. /package/dist/web/assets/{racket-K_6U1lqJ.js → racket-DtfSL3WR.js} +0 -0
  461. /package/dist/web/assets/{raku-bNSiZeW_.js → raku-CnDLsBJr.js} +0 -0
  462. /package/dist/web/assets/{react-X4jUntEf.js → react-Dfio3PfK.js} +0 -0
  463. /package/dist/web/assets/{red-uVeIEF0f.js → red-vbqcE9b-.js} +0 -0
  464. /package/dist/web/assets/{refresh-cw-B9iv7csb.js → refresh-cw-84mz8aOE.js} +0 -0
  465. /package/dist/web/assets/{reg-DSm9GWjh.js → reg-DIDLT8-A.js} +0 -0
  466. /package/dist/web/assets/{regexp-CljRm_j_.js → regexp-CB4h-VPu.js} +0 -0
  467. /package/dist/web/assets/{rel-d3JcopZg.js → rel-CiQEnWXa.js} +0 -0
  468. /package/dist/web/assets/{resolve-theme-DLKmm-lE.js → resolve-theme-DPTQHAy1.js} +0 -0
  469. /package/dist/web/assets/{riscv-CJmaELjT.js → riscv-R78iq5Rf.js} +0 -0
  470. /package/dist/web/assets/{ron-D0g9B1rW.js → ron-DhYENxc4.js} +0 -0
  471. /package/dist/web/assets/{rose-pine-DOrD2BSH.js → rose-pine-5wBeyjfJ.js} +0 -0
  472. /package/dist/web/assets/{rose-pine-dawn-CzZQ-LCF.js → rose-pine-dawn-FpKtPCuH.js} +0 -0
  473. /package/dist/web/assets/{rose-pine-moon-CdmsYp7C.js → rose-pine-moon-BwcIYc7C.js} +0 -0
  474. /package/dist/web/assets/{rosmsg-CmDth6pO.js → rosmsg-DWIGAYMl.js} +0 -0
  475. /package/dist/web/assets/{rotate-ccw-DnuPzJRE.js → rotate-ccw-CNTk2J7l.js} +0 -0
  476. /package/dist/web/assets/{rust-DGdP6jiR.js → rust-DU_ofKgt.js} +0 -0
  477. /package/dist/web/assets/{sass-Dkgt2FKZ.js → sass-U9SUuYEy.js} +0 -0
  478. /package/dist/web/assets/{save-DxonD4y1.js → save-CtW9ojKk.js} +0 -0
  479. /package/dist/web/assets/{scala-BQLTON6C.js → scala-op4gjA6j.js} +0 -0
  480. /package/dist/web/assets/{scheme-DKlxUGCi.js → scheme-tEjkR8rL.js} +0 -0
  481. /package/dist/web/assets/{sdbl-C3iZJQeZ.js → sdbl-C6SSRmZg.js} +0 -0
  482. /package/dist/web/assets/{send-B-t5Mz26.js → send-C_sLFt56.js} +0 -0
  483. /package/dist/web/assets/{shallow-jwPzEunk.js → shallow-C1YoJQA-.js} +0 -0
  484. /package/dist/web/assets/{shellscript-BRu-ZQAx.js → shellscript-Bf2KiwQj.js} +0 -0
  485. /package/dist/web/assets/{shield-check-CdTiK1j2.js → shield-check-Du6HKYn9.js} +0 -0
  486. /package/dist/web/assets/{shield-off-DuetkXKM.js → shield-off-CWKb1MQL.js} +0 -0
  487. /package/dist/web/assets/{slack-dark-qkm_u2zz.js → slack-dark-oGgCey8r.js} +0 -0
  488. /package/dist/web/assets/{slack-ochin-BP8_8e4m.js → slack-ochin-CemlB8cE.js} +0 -0
  489. /package/dist/web/assets/{smalltalk-Bnz4gt07.js → smalltalk-CTE-6jrZ.js} +0 -0
  490. /package/dist/web/assets/{snazzy-light-fbPZg5io.js → snazzy-light-DoEIGnmz.js} +0 -0
  491. /package/dist/web/assets/{solarized-dark-CM0vTRoi.js → solarized-dark-CStiJyBg.js} +0 -0
  492. /package/dist/web/assets/{solarized-light-C4_W9mvs.js → solarized-light-CfuNycS9.js} +0 -0
  493. /package/dist/web/assets/{solidity-Bz6v1PoD.js → solidity-DL6sO5rd.js} +0 -0
  494. /package/dist/web/assets/{splunk-DyNxu-0i.js → splunk-yK4ZUFBB.js} +0 -0
  495. /package/dist/web/assets/{sql-DXRL1gw1.js → sql-Lw03eoN8.js} +0 -0
  496. /package/dist/web/assets/{ssh-config-2z1QHuOG.js → ssh-config-DeRp25TR.js} +0 -0
  497. /package/dist/web/assets/{stylus-D4I-VaeY.js → stylus-D8OMsPh-.js} +0 -0
  498. /package/dist/web/assets/{swift-C_2G-tIc.js → swift-nsl9stPV.js} +0 -0
  499. /package/dist/web/assets/{synthwave-84-osBjChRP.js → synthwave-84-exAgsgpf.js} +0 -0
  500. /package/dist/web/assets/{system-verilog-zQP2qPNU.js → system-verilog-DBWf00T6.js} +0 -0
  501. /package/dist/web/assets/{systemd-UGfjRSBM.js → systemd-nhu27Dw5.js} +0 -0
  502. /package/dist/web/assets/{table-FLU8ei8b.js → table-GAR4upb6.js} +0 -0
  503. /package/dist/web/assets/{talonscript-C1XrFPwo.js → talonscript-CNOEcGDc.js} +0 -0
  504. /package/dist/web/assets/{tasl-B5raVn43.js → tasl-CjYZenSH.js} +0 -0
  505. /package/dist/web/assets/{tcl-3-vbeMIc.js → tcl-BVA09RDz.js} +0 -0
  506. /package/dist/web/assets/{terraform-a8VXZDm9.js → terraform-C5EloH1q.js} +0 -0
  507. /package/dist/web/assets/{text-wrap-B05VGRa-.js → text-wrap-D71Jqz5U.js} +0 -0
  508. /package/dist/web/assets/{tokyo-night-4SRhf-Hz.js → tokyo-night-D764DdhO.js} +0 -0
  509. /package/dist/web/assets/{toml-B4N0i8wT.js → toml-CCDYblSB.js} +0 -0
  510. /package/dist/web/assets/{trash-2-CTXlTflF.js → trash-2-C08fhIg1.js} +0 -0
  511. /package/dist/web/assets/{triangle-alert-BRq0VAfd.js → triangle-alert-85BQgs3T.js} +0 -0
  512. /package/dist/web/assets/{tsv-DA5k9Lvw.js → tsv-BZ4sA05e.js} +0 -0
  513. /package/dist/web/assets/{tsx-D9SZ8owC.js → tsx-DPLBzAt5.js} +0 -0
  514. /package/dist/web/assets/{turtle-BTHTvyY_.js → turtle-CwGHCq40.js} +0 -0
  515. /package/dist/web/assets/{typescript-DYzV9p5L.js → typescript-CUH0vBAl.js} +0 -0
  516. /package/dist/web/assets/{typespec-BC15eh7F.js → typespec-Bq31Vq4u.js} +0 -0
  517. /package/dist/web/assets/{typst-BJUqzA6D.js → typst-MD9BR9W5.js} +0 -0
  518. /package/dist/web/assets/{use-is-mobile-DFWViA4S.js → use-is-mobile-DUuki97q.js} +0 -0
  519. /package/dist/web/assets/{utils-C9OjCrou.js → utils-u2fbYPrY.js} +0 -0
  520. /package/dist/web/assets/{v-50Lps6oA.js → v-DZV7IxIT.js} +0 -0
  521. /package/dist/web/assets/{vala-8wiUvvrD.js → vala-urd1zi9S.js} +0 -0
  522. /package/dist/web/assets/{vb-BlgPLt6B.js → vb-DIZU3zbJ.js} +0 -0
  523. /package/dist/web/assets/{vendor-xterm-BZOrwF_F.js → vendor-xterm-DCj0zgSC.js} +0 -0
  524. /package/dist/web/assets/{verilog-CD9pVW5p.js → verilog-Ndz_ciTA.js} +0 -0
  525. /package/dist/web/assets/{vesper-BrivW4PT.js → vesper-C86M7LJp.js} +0 -0
  526. /package/dist/web/assets/{vhdl-Bunxfa7N.js → vhdl-_klwDUjd.js} +0 -0
  527. /package/dist/web/assets/{viml-DndUb2qM.js → viml-DzRXWoQh.js} +0 -0
  528. /package/dist/web/assets/{vitesse-black-BurZSkxW.js → vitesse-black-TWZ20_3U.js} +0 -0
  529. /package/dist/web/assets/{vitesse-dark-8UShspzC.js → vitesse-dark-Dl7R6fy9.js} +0 -0
  530. /package/dist/web/assets/{vitesse-light-DghCAZ90.js → vitesse-light-Cs0Dt2_n.js} +0 -0
  531. /package/dist/web/assets/{vyper-b-Clr-8u.js → vyper-qnCVj8ia.js} +0 -0
  532. /package/dist/web/assets/{wasm-CpcKStkw.js → wasm-Ckx9jBPe.js} +0 -0
  533. /package/dist/web/assets/{wasm-C5v2T06Y.js → wasm-Dy-SJ3W9.js} +0 -0
  534. /package/dist/web/assets/{wenyan-Dz2srLZR.js → wenyan-DTEy2Lx-.js} +0 -0
  535. /package/dist/web/assets/{wgsl-CeD1-dRb.js → wgsl-DC7n63Sy.js} +0 -0
  536. /package/dist/web/assets/{wikitext-CyeS-9FH.js → wikitext-1zFoZiW6.js} +0 -0
  537. /package/dist/web/assets/{wit-Kr-q-qBQ.js → wit-DtTfCRph.js} +0 -0
  538. /package/dist/web/assets/{wolfram-BKm8bmln.js → wolfram-B2Bgu1KL.js} +0 -0
  539. /package/dist/web/assets/{x-YDu4m2Iq.js → x-CeN0Qybc.js} +0 -0
  540. /package/dist/web/assets/{yaml-2XSxomjM.js → yaml-B-Jxk0IQ.js} +0 -0
  541. /package/dist/web/assets/{zenscript-CKIS34vf.js → zenscript-BWRfvNER.js} +0 -0
  542. /package/dist/web/assets/{zig-DdHGNllX.js → zig-CkwuvETm.js} +0 -0
@@ -5,11 +5,13 @@
5
5
  * session JSONL (no isSidechain lines, no parent_tool_use_id). Instead each
6
6
  * agent gets its own transcript at:
7
7
  * ~/.claude/projects/<slug>/<sessionId>/subagents/agent-<id>.jsonl
8
- * with a sibling agent-<id>.meta.json carrying the spawning Agent tool_use id.
8
+ * with a sibling agent-<id>.meta.json carrying the spawning Agent tool_use id
9
+ * (session-spawned agents) or the spawning agent's id (nested agents).
9
10
  *
10
11
  * Without merging these, a reloaded session shows bare Agent cards and the
11
- * live-streamed children are lost. This module reads the meta files, parses
12
- * each agent transcript, and attaches its events as the card's children.
12
+ * live-streamed children are lost. This module groups the transcripts under
13
+ * their session-level card, parses each one, and attaches the events every
14
+ * nesting level flattened into one time-ordered list — as the card's children.
13
15
  */
14
16
 
15
17
  import { existsSync, readdirSync, readFileSync } from "node:fs";
@@ -17,18 +19,20 @@ import { homedir } from "node:os";
17
19
  import { join } from "node:path";
18
20
  import type { ChatEvent } from "../types/chat.ts";
19
21
  import { parseSessionMessage } from "./jsonl-transcript-parser.ts";
22
+ import { groupSubagentsByCard } from "./team-member-activity/subagent-transcript-index.ts";
20
23
 
21
24
  /**
22
25
  * Locate the per-session directory (…/projects/<slug>/<sessionId>) that holds
23
- * subagents/. The SDK encodes the cwd by replacing separators + drive colon
24
- * with "-"; when that drifts (drive-letter case), fall back to scanning
25
- * project dirs for the session's main JSONL.
26
+ * subagents/. The SDK encodes the cwd by replacing separators, drive colon
27
+ * and dots with "-" (`…\ppm\.claude\worktrees\x` `…-ppm--claude-worktrees-x`);
28
+ * when that drifts (drive-letter case), fall back to scanning project dirs for
29
+ * the session's main JSONL.
26
30
  */
27
31
  export function resolveSessionDir(sessionId: string, projectPath: string | null | undefined): string | null {
28
32
  const home = homedir();
29
33
  const projectsRoot = join(home, ".claude", "projects");
30
34
  if (projectPath) {
31
- const encoded = projectPath.replace(/[/\\:]/g, "-");
35
+ const encoded = projectPath.replace(/[/\\:.]/g, "-");
32
36
  const dir = join(projectsRoot, encoded);
33
37
  if (existsSync(join(dir, `${sessionId}.jsonl`))) return join(dir, sessionId);
34
38
  }
@@ -53,92 +57,105 @@ interface MessageLike {
53
57
  const MAX_CHILDREN_PER_AGENT = 2000;
54
58
  const MAX_CHILD_OUTPUT_CHARS = 50_000;
55
59
 
56
- /** Map of Agent-card toolUseId agent transcript path, from subagents/*.meta.json */
57
- function indexAgentTranscripts(subagentsDir: string): Map<string, string> {
58
- const map = new Map<string, string>();
59
- let entries: string[];
60
- try {
61
- entries = readdirSync(subagentsDir);
62
- } catch {
63
- return map;
64
- }
65
- for (const name of entries) {
66
- if (!name.endsWith(".meta.json")) continue;
67
- try {
68
- const meta = JSON.parse(readFileSync(join(subagentsDir, name), "utf8"));
69
- const toolUseId = meta?.toolUseId as string | undefined;
70
- if (!toolUseId) continue;
71
- const transcript = join(subagentsDir, name.replace(/\.meta\.json$/, ".jsonl"));
72
- if (existsSync(transcript)) map.set(toolUseId, transcript);
73
- } catch {
74
- /* unreadable meta — skip this agent */
75
- }
76
- }
77
- return map;
60
+ /** One child event plus the transcript record time it came from (epoch ms). */
61
+ export interface TimedChatEvent {
62
+ ts: number;
63
+ ev: ChatEvent;
78
64
  }
79
65
 
80
- /** Parse one agent transcript into a flat children event list (stream order). */
81
- export function parseAgentTranscript(filePath: string): ChatEvent[] {
82
- const children: ChatEvent[] = [];
66
+ /**
67
+ * Incremental parser for one agent transcript. Feed it JSONL lines in file
68
+ * order (whole file at once, or a live tail's new lines) and it returns the
69
+ * child events those lines carry, keeping the skip/limit state across calls.
70
+ */
71
+ export function createAgentTranscriptLineParser() {
72
+ let isFirstUser = true;
73
+ let emitted = 0;
74
+ let lastTs = 0;
75
+ return {
76
+ feed(line: string): TimedChatEvent[] {
77
+ const out: TimedChatEvent[] = [];
78
+ const trimmed = line.trim();
79
+ if (!trimmed || emitted >= MAX_CHILDREN_PER_AGENT) return out;
80
+ let entry: any;
81
+ try {
82
+ entry = JSON.parse(trimmed);
83
+ } catch {
84
+ return out;
85
+ }
86
+ if (entry.type !== "user" && entry.type !== "assistant") return out;
87
+ if (!entry.message) return out;
88
+ // The first user record is the agent's spawn prompt — the live stream
89
+ // never showed it as a child, so skip it to match the streaming view.
90
+ if (entry.type === "user" && isFirstUser) {
91
+ isFirstUser = false;
92
+ const content = entry.message?.content;
93
+ const hasToolResult = Array.isArray(content) && content.some((b: any) => b?.type === "tool_result");
94
+ if (!hasToolResult) return out;
95
+ }
96
+ // Records without a timestamp inherit the previous one so file order holds.
97
+ const parsedTs = typeof entry.timestamp === "string" ? Date.parse(entry.timestamp) : NaN;
98
+ if (!Number.isNaN(parsedTs)) lastTs = parsedTs;
99
+ const parsed = parseSessionMessage(entry);
100
+ for (const ev of parsed.events ?? []) {
101
+ if (emitted >= MAX_CHILDREN_PER_AGENT) break;
102
+ // Keep single events from ballooning the history payload (agent files
103
+ // can carry multi-MB tool outputs the live stream also showed in full,
104
+ // but 24 agents × full outputs breaks mobile reloads).
105
+ if (ev.type === "tool_result" && typeof ev.output === "string" && ev.output.length > MAX_CHILD_OUTPUT_CHARS) {
106
+ ev.output = ev.output.slice(0, MAX_CHILD_OUTPUT_CHARS) + "\n… [truncated]";
107
+ }
108
+ out.push({ ts: lastTs, ev });
109
+ emitted++;
110
+ }
111
+ return out;
112
+ },
113
+ };
114
+ }
115
+
116
+ /** Parse one whole agent transcript into timed child events (file order). */
117
+ export function parseAgentTranscriptTimed(filePath: string): TimedChatEvent[] {
83
118
  let text: string;
84
119
  try {
85
120
  text = readFileSync(filePath, "utf8");
86
121
  } catch {
87
- return children;
88
- }
89
- let isFirstUser = true;
90
- for (const line of text.split("\n")) {
91
- const trimmed = line.trim();
92
- if (!trimmed) continue;
93
- let entry: any;
94
- try {
95
- entry = JSON.parse(trimmed);
96
- } catch {
97
- continue;
98
- }
99
- if (entry.type !== "user" && entry.type !== "assistant") continue;
100
- if (!entry.message) continue;
101
- if (children.length >= MAX_CHILDREN_PER_AGENT) break;
102
- // The first user record is the agent's spawn prompt — the live stream
103
- // never showed it as a child, so skip it to match the streaming view.
104
- if (entry.type === "user" && isFirstUser) {
105
- isFirstUser = false;
106
- const content = entry.message?.content;
107
- const hasToolResult = Array.isArray(content) && content.some((b: any) => b?.type === "tool_result");
108
- if (!hasToolResult) continue;
109
- }
110
- const parsed = parseSessionMessage(entry);
111
- for (const ev of parsed.events ?? []) {
112
- // Keep single events from ballooning the history payload (agent files
113
- // can carry multi-MB tool outputs the live stream also showed in full,
114
- // but 24 agents × full outputs breaks mobile reloads).
115
- if (ev.type === "tool_result" && typeof ev.output === "string" && ev.output.length > MAX_CHILD_OUTPUT_CHARS) {
116
- ev.output = ev.output.slice(0, MAX_CHILD_OUTPUT_CHARS) + "\n… [truncated]";
117
- }
118
- children.push(ev);
119
- }
122
+ return [];
120
123
  }
121
- return children;
124
+ const parser = createAgentTranscriptLineParser();
125
+ const out: TimedChatEvent[] = [];
126
+ for (const line of text.split("\n")) out.push(...parser.feed(line));
127
+ return out;
128
+ }
129
+
130
+ /** Parse one agent transcript into a flat children event list (stream order). */
131
+ export function parseAgentTranscript(filePath: string): ChatEvent[] {
132
+ return parseAgentTranscriptTimed(filePath).map((t) => t.ev);
122
133
  }
123
134
 
124
135
  /**
125
136
  * Attach subagent transcript events as children of their Agent/Task cards.
126
137
  * `sessionDir` is the per-session directory next to the main JSONL
127
- * (…/projects/<slug>/<sessionId>). Existing children are replaced — the disk
128
- * transcript is the complete record, while live-collected children may be a
129
- * partial overlap. Mutates messages in-place.
138
+ * (…/projects/<slug>/<sessionId>). A card's group — the agent it spawned plus
139
+ * any agents that one spawned in turn is merged into one list ordered by
140
+ * record time, so a nested worker's steps read where they happened rather
141
+ * than after the parent agent's conclusion. Existing children are replaced —
142
+ * the disk transcripts are the complete record, while live-collected children
143
+ * may be a partial overlap. Mutates messages in-place.
130
144
  */
131
145
  export function mergeSubagentChildren(sessionDir: string, messages: MessageLike[]): void {
132
- const index = indexAgentTranscripts(join(sessionDir, "subagents"));
133
- if (index.size === 0) return;
146
+ const groups = groupSubagentsByCard(join(sessionDir, "subagents"));
147
+ if (groups.size === 0) return;
134
148
 
135
149
  for (const msg of messages) {
136
150
  for (const ev of msg.events ?? []) {
137
151
  if (ev.type !== "tool_use" || (ev.tool !== "Agent" && ev.tool !== "Task") || !ev.toolUseId) continue;
138
- const transcript = index.get(ev.toolUseId);
139
- if (!transcript) continue;
140
- const children = parseAgentTranscript(transcript);
141
- if (children.length > 0) ev.children = children;
152
+ const group = groups.get(ev.toolUseId);
153
+ if (!group) continue;
154
+ const timed: TimedChatEvent[] = [];
155
+ for (const agent of group) timed.push(...parseAgentTranscriptTimed(agent.transcriptPath));
156
+ // Stable sort: same-timestamp events keep transcript order.
157
+ timed.sort((a, b) => a.ts - b.ts);
158
+ if (timed.length > 0) ev.children = timed.map((t) => t.ev);
142
159
  }
143
160
  }
144
161
  }
@@ -13,13 +13,36 @@
13
13
  */
14
14
 
15
15
  /**
16
- * Anthropic's default prompt-cache lifetime. A prefix not re-sent within this window has to
17
- * be written to the cache again, so a subprocess older than this is worth nothing.
16
+ * Anthropic's prompt-cache lifetime, per auth mode. From the SDK's own typings: "1 hour on a
17
+ * Claude subscription within its usage limits, 5 minutes on an API key, Bedrock, Vertex or
18
+ * Foundry."
18
19
  *
19
- * Named here rather than inlined because the retention window is *derived* from it — the two
20
- * were previously equal by coincidence, which read as if the teardown delay were arbitrary.
20
+ * Both are named here because the retention window is *derived* from whichever applies — the
21
+ * delay was previously equal to the TTL by coincidence, which read as arbitrary.
22
+ *
23
+ * Which one a session gets is not a constant: PPM supports settings `api_key` and a custom
24
+ * `base_url` as first-class options, and "within its usage limits" excludes exactly the
25
+ * accounts the rotation exists for. Holding a subprocess for an hour on an API-key install
26
+ * guards a cache that died at minute five, at ~350MB a session. The provider decides; these
27
+ * are the two values it picks between.
28
+ */
29
+ export const SUBSCRIPTION_PROMPT_CACHE_TTL_MS = 60 * 60_000;
30
+ export const API_KEY_PROMPT_CACHE_TTL_MS = 5 * 60_000;
31
+
32
+ /** The longer of the two — what a timer that must outlast any cache window has to clear. */
33
+ export const PROMPT_CACHE_TTL_MS = SUBSCRIPTION_PROMPT_CACHE_TTL_MS;
34
+
35
+ /**
36
+ * What a provider that does not state a window gets.
37
+ *
38
+ * The short one, deliberately. `promptCacheTtlMs` is optional on the provider interface, so
39
+ * "no opinion" and "an hour" would otherwise be the same answer — and the providers without
40
+ * an opinion are exactly the ones with no Anthropic prompt cache to protect. Codex reaches
41
+ * this path today: it implements `hasStreamingSession` but not `promptCacheTtlMs`, so an
42
+ * hour here would hold ~350MB per abandoned session guarding a cache it never had. A
43
+ * provider that benefits from the long window says so.
21
44
  */
22
- export const PROMPT_CACHE_TTL_MS = 5 * 60_000;
45
+ const UNDECLARED_PROMPT_CACHE_TTL_MS = API_KEY_PROMPT_CACHE_TTL_MS;
23
46
 
24
47
  /**
25
48
  * Milliseconds until a session's prompt cache lapses.
@@ -31,7 +54,7 @@ export const PROMPT_CACHE_TTL_MS = 5 * 60_000;
31
54
  export function cacheReleaseDelayMs(
32
55
  lastTurnEndedAt: number | undefined,
33
56
  now: number,
34
- ttlMs: number = PROMPT_CACHE_TTL_MS,
57
+ ttlMs: number = UNDECLARED_PROMPT_CACHE_TTL_MS,
35
58
  ): number {
36
59
  // No completed turn means nothing has been cached, so there is nothing to protect.
37
60
  if (lastTurnEndedAt == null) return 0;
@@ -52,6 +52,9 @@ const TUNNEL_URL_REGEX = /https:\/\/(?!api\.)[a-z0-9-]+\.trycloudflare\.com/;
52
52
  const NAMED_TUNNEL_READY_REGEX = /Registered tunnel connection/;
53
53
  const UPGRADE_CHECK_INTERVAL_MS = 900_000; // 15min
54
54
  const UPGRADE_SKIP_INITIAL_MS = 300_000; // 5min delay before first check
55
+ const DB_BACKUP_INTERVAL_MS = 3_600_000; // 1h — the recovery-point target
56
+ const DB_BACKUP_START_DELAY_MS = 30_000; // let the server settle before the first snapshot
57
+ const DB_BACKUP_STALE_WARN_MS = 21_600_000; // 6h — snapshots stopped happening
55
58
  const SELF_REPLACE_TIMEOUT_MS = 30_000; // 30s to wait for new supervisor
56
59
  const EDGE_PROBE_INTERVAL_MS = 10_000; // the public port is dark while the edge is down — check often
57
60
  const SERVER_PORT_MIRROR_TIMEOUT_MS = 30_000; // how long to wait for the server to publish its port
@@ -127,6 +130,7 @@ let healthTimer: ReturnType<typeof setInterval> | null = null;
127
130
  let tunnelProbeTimer: ReturnType<typeof setInterval> | null = null;
128
131
  let heartbeatTimer: ReturnType<typeof setInterval> | null = null;
129
132
  let upgradeCheckTimer: ReturnType<typeof setInterval> | null = null;
133
+ let dbBackupTimer: ReturnType<typeof setInterval> | null = null;
130
134
  let upgradeDelayTimer: ReturnType<typeof setTimeout> | null = null;
131
135
  let cloudMonitorTimer: ReturnType<typeof setInterval> | null = null;
132
136
  let descendantSnapshotTimer: ReturnType<typeof setInterval> | null = null; // win32 only
@@ -936,6 +940,41 @@ export async function spawnTunnel(port: number, generation: number = ++tunnelGen
936
940
  return retryTunnelAfterFailure(generation);
937
941
  }
938
942
 
943
+ // ─── Config-database snapshots ─────────────────────────────────────────
944
+ let dbBackupDelayTimer: ReturnType<typeof setTimeout> | null = null;
945
+
946
+ /**
947
+ * Take one verified snapshot of the config database, then report whether
948
+ * snapshots are keeping up.
949
+ *
950
+ * Failures are logged and swallowed: a backup problem must never take the
951
+ * server down. The staleness warning exists because a silently-failing backup
952
+ * is worse than no backup — it is the state where recovery is believed to be
953
+ * available and is not.
954
+ */
955
+ async function runDbBackupTick(reason: "hourly" | "start"): Promise<void> {
956
+ if (shuttingDown) return;
957
+ try {
958
+ const { backupDb } = await import("./db-backup/db-backup.service.ts");
959
+ const result = await backupDb(reason);
960
+ const mb = (result.bytes / 1_048_576).toFixed(1);
961
+ const prunedNote = result.pruned.length ? `, pruned ${result.pruned.length}` : "";
962
+ log("INFO", `DB snapshot (${reason}): ${result.path} ${mb}MB in ${result.ms}ms${prunedNote}`);
963
+ } catch (e: any) {
964
+ log("ERROR", `DB snapshot (${reason}) failed: ${e?.message ?? e}`);
965
+ }
966
+
967
+ try {
968
+ const { newestBackupAgeMs } = await import("./db-backup/db-backup.service.ts");
969
+ const age = await newestBackupAgeMs();
970
+ if (age === null) {
971
+ log("WARN", "DB snapshot: no backups exist yet — the database is currently unrecoverable");
972
+ } else if (age > DB_BACKUP_STALE_WARN_MS) {
973
+ log("WARN", `DB snapshot: newest backup is ${Math.round(age / 3_600_000)}h old — recovery point is degraded`);
974
+ }
975
+ } catch { /* staleness reporting is advisory only */ }
976
+ }
977
+
939
978
  // ─── Health checks ─────────────────────────────────────────────────────
940
979
  function startServerHealthCheck() {
941
980
  healthTimer = setInterval(async () => {
@@ -1314,6 +1353,8 @@ async function selfReplace(): Promise<{ success: boolean; error?: string }> {
1314
1353
  if (heartbeatTimer) clearInterval(heartbeatTimer);
1315
1354
  if (upgradeCheckTimer) clearInterval(upgradeCheckTimer);
1316
1355
  if (upgradeDelayTimer) clearTimeout(upgradeDelayTimer);
1356
+ if (dbBackupTimer) clearInterval(dbBackupTimer);
1357
+ if (dbBackupDelayTimer) clearTimeout(dbBackupDelayTimer);
1317
1358
  if (cloudMonitorTimer) clearInterval(cloudMonitorTimer);
1318
1359
  // Disconnect Cloud WS so new supervisor can reconnect cleanly
1319
1360
  try { const { disconnect } = await import("./cloud-ws.service.ts"); disconnect(); } catch {}
@@ -1398,6 +1439,8 @@ async function selfReplace(): Promise<{ success: boolean; error?: string }> {
1398
1439
  if (heartbeatTimer) clearInterval(heartbeatTimer);
1399
1440
  if (upgradeCheckTimer) clearInterval(upgradeCheckTimer);
1400
1441
  if (upgradeDelayTimer) clearTimeout(upgradeDelayTimer);
1442
+ if (dbBackupTimer) clearInterval(dbBackupTimer);
1443
+ if (dbBackupDelayTimer) clearTimeout(dbBackupDelayTimer);
1401
1444
  process.exit(0);
1402
1445
  }
1403
1446
  } catch {}
@@ -1680,6 +1723,8 @@ export function shutdown() {
1680
1723
  if (heartbeatTimer) clearInterval(heartbeatTimer);
1681
1724
  if (upgradeCheckTimer) clearInterval(upgradeCheckTimer);
1682
1725
  if (upgradeDelayTimer) clearTimeout(upgradeDelayTimer);
1726
+ if (dbBackupTimer) clearInterval(dbBackupTimer);
1727
+ if (dbBackupDelayTimer) clearTimeout(dbBackupDelayTimer);
1683
1728
  if (cloudMonitorTimer) clearInterval(cloudMonitorTimer);
1684
1729
  if (descendantSnapshotTimer) clearInterval(descendantSnapshotTimer);
1685
1730
 
@@ -1962,6 +2007,19 @@ export async function runSupervisor(opts: {
1962
2007
  }
1963
2008
  }, RESUME_TICK_MS);
1964
2009
 
2010
+ // Snapshot the config database on a schedule. The supervisor is the only
2011
+ // always-on process, so this is the one place a periodic snapshot survives
2012
+ // both server restarts and crash-backoff windows.
2013
+ //
2014
+ // The start-up snapshot is not redundant with the hourly one: a machine that
2015
+ // is only powered on for short sessions, or a supervisor that sat paused
2016
+ // after exhausting its restart budget, would otherwise take no snapshot at
2017
+ // all for as long as that lasted.
2018
+ dbBackupDelayTimer = setTimeout(() => {
2019
+ void runDbBackupTick("start");
2020
+ dbBackupTimer = setInterval(() => void runDbBackupTick("hourly"), DB_BACKUP_INTERVAL_MS);
2021
+ }, DB_BACKUP_START_DELAY_MS);
2022
+
1965
2023
  // Start upgrade check timer (5min initial delay, then every 15min)
1966
2024
  upgradeDelayTimer = setTimeout(() => {
1967
2025
  checkAvailableVersion();
@@ -26,6 +26,15 @@ export interface SubagentTranscriptEntry {
26
26
  model?: string;
27
27
  /** tool_use id of the spawning Agent call — links back to the chat card. */
28
28
  toolUseId?: string;
29
+ /** Raw id from the `agent-<id>` file stem — the form `parentAgentId` uses. */
30
+ agentId: string;
31
+ /**
32
+ * agentId of the agent that spawned this one. Set on nested agents (a
33
+ * subagent that itself called Agent/Skill); those metas carry no toolUseId
34
+ * because the spawning tool_use lives in the parent agent's transcript, not
35
+ * the session's.
36
+ */
37
+ parentAgentId?: string;
29
38
  /** Absolute path of the agent's JSONL transcript. */
30
39
  transcriptPath: string;
31
40
  /** Transcript size in bytes — the cheap "how much work" signal. */
@@ -69,6 +78,8 @@ export function listSubagentTranscripts(subagentsDir: string): SubagentTranscrip
69
78
  description: typeof meta.description === "string" ? meta.description : undefined,
70
79
  model: typeof meta.model === "string" ? meta.model : undefined,
71
80
  toolUseId: typeof meta.toolUseId === "string" ? meta.toolUseId : undefined,
81
+ agentId: file.replace(/^agent-/, "").replace(/\.meta\.json$/, ""),
82
+ parentAgentId: typeof meta.parentAgentId === "string" ? meta.parentAgentId : undefined,
72
83
  transcriptPath,
73
84
  sizeBytes,
74
85
  modifiedAt,
@@ -77,6 +88,51 @@ export function listSubagentTranscripts(subagentsDir: string): SubagentTranscrip
77
88
  return out;
78
89
  }
79
90
 
91
+ /**
92
+ * Group every transcript under the session-level Agent card it belongs to.
93
+ *
94
+ * The CLI only stamps `toolUseId` on agents the session itself spawned. A
95
+ * nested agent (spawned by another agent, e.g. a reviewer running a skill that
96
+ * forks its own worker) has only `parentAgentId`, and the SDK streams none of
97
+ * its activity — so the card's transcript group is the only place its work can
98
+ * surface. Walk each chain up to the root and key by that root's toolUseId.
99
+ * Entries are ordered root first, then descendants by depth.
100
+ */
101
+ export function groupSubagentsByCard(subagentsDir: string): Map<string, SubagentTranscriptEntry[]> {
102
+ const entries = listSubagentTranscripts(subagentsDir);
103
+ const byId = new Map(entries.map((e) => [e.agentId, e]));
104
+ const rootOf = (entry: SubagentTranscriptEntry): { root: SubagentTranscriptEntry; depth: number } | null => {
105
+ let cur = entry;
106
+ let depth = 0;
107
+ const seen = new Set<string>();
108
+ while (cur.parentAgentId) {
109
+ if (seen.has(cur.agentId)) return null; // cyclic metas — corrupt, skip
110
+ seen.add(cur.agentId);
111
+ const parent = byId.get(cur.parentAgentId);
112
+ if (!parent) return null; // parent meta missing — cannot attach anywhere
113
+ cur = parent;
114
+ depth++;
115
+ }
116
+ return cur.toolUseId ? { root: cur, depth } : null;
117
+ };
118
+
119
+ const groups = new Map<string, Array<{ entry: SubagentTranscriptEntry; depth: number }>>();
120
+ for (const entry of entries) {
121
+ const resolved = rootOf(entry);
122
+ if (!resolved) continue;
123
+ const key = resolved.root.toolUseId!;
124
+ const list = groups.get(key) ?? [];
125
+ list.push({ entry, depth: resolved.depth });
126
+ groups.set(key, list);
127
+ }
128
+ const out = new Map<string, SubagentTranscriptEntry[]>();
129
+ for (const [key, list] of groups) {
130
+ list.sort((a, b) => a.depth - b.depth);
131
+ out.set(key, list.map((l) => l.entry));
132
+ }
133
+ return out;
134
+ }
135
+
80
136
  /**
81
137
  * Newest transcript per teammate name.
82
138
  *
package/src/types/chat.ts CHANGED
@@ -49,6 +49,8 @@ export interface AIProvider {
49
49
  getUsage?(): Promise<UsageInfo>;
50
50
  /** True when a live streaming subprocess exists for this session */
51
51
  hasStreamingSession?(sessionId: string): boolean;
52
+ /** Prompt-cache lifetime for this session, in ms — how long holding its subprocess pays. */
53
+ promptCacheTtlMs?(sessionId: string): number;
52
54
  }
53
55
 
54
56
  export interface ModelOption {
package/src/web/app.tsx CHANGED
@@ -48,6 +48,9 @@ const MobileExplorerSheet = lazy(() =>
48
48
  const TeamMemberSheet = lazy(() =>
49
49
  import("@/components/chat/team-member-sheet").then((m) => ({ default: m.TeamMemberSheet })),
50
50
  );
51
+ const RemoteDesktopMobileSheet = lazy(() =>
52
+ import("@/components/remote-desktop/remote-desktop-mobile-sheet").then((m) => ({ default: m.RemoteDesktopMobileSheet })),
53
+ );
51
54
 
52
55
  type AuthState = "checking" | "authenticated" | "unauthenticated";
53
56
 
@@ -357,6 +360,11 @@ export function App() {
357
360
  <TeamMemberSheet />
358
361
  </Suspense>
359
362
 
363
+ {/* Mobile full-screen remote-desktop viewer — same reason as TeamMemberSheet above */}
364
+ <Suspense fallback={null}>
365
+ <RemoteDesktopMobileSheet />
366
+ </Suspense>
367
+
360
368
  {/* Global bug report popup */}
361
369
  <BugReportPopup />
362
370
 
@@ -70,9 +70,14 @@ export function AddAccountDialog({ open, onOpenChange, onSuccess }: AddAccountDi
70
70
  try {
71
71
  let code = oauthCode.trim();
72
72
  if (code.includes("#")) code = code.split("#")[0] ?? code;
73
- await exchangeOAuthCode(code, oauthState);
73
+ const acc = await exchangeOAuthCode(code, oauthState);
74
74
  handleClose();
75
- onSuccess("Account connected via OAuth!");
75
+ // Signing in again on an account this machine parked carries the token in but
76
+ // deliberately leaves it out of the rotation. A flat success over a toggle that is
77
+ // still off reads as a bug, and this is the only place the user finds out otherwise.
78
+ onSuccess(acc?.status === "disabled"
79
+ ? "Signed in. This account is still disabled — enable it to use it."
80
+ : "Account connected via OAuth!");
76
81
  } catch (e) {
77
82
  setAddError((e as Error).message);
78
83
  }
@@ -84,9 +89,11 @@ export function AddAccountDialog({ open, onOpenChange, onSuccess }: AddAccountDi
84
89
  setAdding(true);
85
90
  setAddError(null);
86
91
  try {
87
- await addAccount({ apiKey: newToken.trim(), label: newLabel.trim() || undefined });
92
+ const acc = await addAccount({ apiKey: newToken.trim(), label: newLabel.trim() || undefined });
88
93
  handleClose();
89
- onSuccess("Account added!");
94
+ onSuccess(acc?.status === "disabled"
95
+ ? "Token updated. This account is still disabled — enable it to use it."
96
+ : "Account added!");
90
97
  } catch (e) {
91
98
  setAddError((e as Error).message);
92
99
  }
@@ -90,7 +90,11 @@ function useQuestionKeyboard(config: {
90
90
  useEffect(() => {
91
91
  if (!config.enabled) return;
92
92
  const handleKeyDown = (e: KeyboardEvent) => {
93
- const isTyping = document.activeElement === config.customInputRef.current;
93
+ // Resolve focus from the input's OWN document: this card can be rendered inside a
94
+ // picture-in-picture window, and the module-global `document` is always the main one —
95
+ // there the input is never active, so every printable key would be treated as a shortcut.
96
+ const input = config.customInputRef.current;
97
+ const isTyping = !!input && input.ownerDocument.activeElement === input;
94
98
 
95
99
  // Number keys 1-9
96
100
  if (!isTyping && e.key >= "1" && e.key <= "9") {
@@ -132,7 +136,10 @@ function useQuestionKeyboard(config: {
132
136
  if (el) {
133
137
  el.addEventListener("keydown", handleKeyDown);
134
138
  el.setAttribute("tabindex", "0");
135
- if (!el.contains(document.activeElement)) el.focus();
139
+ // Same reason: asking the main document whether focus is already inside the card answers
140
+ // "no" for a card living in another document, and this effect re-runs on every render —
141
+ // which would yank focus out of the custom input after each keystroke.
142
+ if (!el.contains(el.ownerDocument.activeElement)) el.focus();
136
143
  }
137
144
  return () => { el?.removeEventListener("keydown", handleKeyDown); };
138
145
  }, [config, focusedOption]);
@@ -154,11 +154,12 @@ function formatLastUpdated(ts: number | null | undefined): string | null {
154
154
  return `${days}d ago`;
155
155
  }
156
156
 
157
- function AccountUsageCard({ entry, isActive, accountInfo, onToggle, onDelete, onExport, onViewProfile, flash, fullscreen }: {
157
+ function AccountUsageCard({ entry, isActive, accountInfo, onToggle, toggling, onDelete, onExport, onViewProfile, flash, fullscreen }: {
158
158
  entry: AccountUsageEntry;
159
159
  isActive: boolean;
160
160
  accountInfo?: AccountInfo;
161
161
  onToggle?: (id: string, status: string) => void;
162
+ toggling?: boolean;
162
163
  onDelete?: (id: string, display: string) => void;
163
164
  onExport?: (id: string) => void;
164
165
  onViewProfile?: (profile: OAuthProfileData, accountId: string) => void;
@@ -207,7 +208,7 @@ function AccountUsageCard({ entry, isActive, accountInfo, onToggle, onDelete, on
207
208
  <Switch
208
209
  checked={status !== "disabled"}
209
210
  onCheckedChange={() => onToggle(entry.accountId, status)}
210
- disabled={status === "cooldown"}
211
+ disabled={toggling || status === "cooldown"}
211
212
  className="scale-[0.6] cursor-pointer"
212
213
  />
213
214
  )}
@@ -269,6 +270,7 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
269
270
  const [exportPreselect, setExportPreselect] = useState<string | null>(null);
270
271
  const [isFullscreen, setIsFullscreen] = useState(false);
271
272
  const [message, setMessage] = useState<string | null>(null);
273
+ const [togglingId, setTogglingId] = useState<string | null>(null);
272
274
  const msgTimer = useRef<ReturnType<typeof setTimeout>>(undefined);
273
275
  const prevUsagesRef = useRef<AccountUsageEntry[]>([]);
274
276
 
@@ -345,7 +347,18 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
345
347
  const fsRows = Math.ceil(fsCount / fsCols);
346
348
 
347
349
  async function handleToggle(id: string, status: string) {
348
- await patchAccount(id, { status: status === "disabled" ? "active" : "disabled" });
350
+ // Enabling a parked account proves its token server-side first, which is a network
351
+ // round trip that can come back 400 — and patchAccount throws on that. Without the
352
+ // catch the message the server takes care to write reaches nobody: unhandled
353
+ // rejection, no toast, no reload, and the Switch silently snaps back. The pending
354
+ // flag is because that round trip can take most of a minute in the worst case.
355
+ setTogglingId(id);
356
+ try {
357
+ await patchAccount(id, { status: status === "disabled" ? "active" : "disabled" });
358
+ } catch (e) {
359
+ showMessage((e as Error).message);
360
+ }
361
+ setTogglingId(null);
349
362
  loadAll();
350
363
  onReload?.();
351
364
  }
@@ -440,6 +453,7 @@ export function UsageDetailPanel({ usage, visible, onClose, onReload, loading, l
440
453
  isActive={entry.accountId === (activeAccountId ?? usage.activeAccountId)}
441
454
  accountInfo={accountMap.get(entry.accountId)}
442
455
  onToggle={handleToggle}
456
+ toggling={togglingId === entry.accountId}
443
457
  onDelete={(id, display) => setDeleteTarget({ id, display })}
444
458
  onExport={(id) => { setExportPreselect(id); setShowExportDialog(true); }}
445
459
  onViewProfile={(profile, accountId) => setProfileView({ profile, accountId })}
@@ -19,6 +19,7 @@ import { PortalContainerProvider } from "@/components/ui/portal-container-contex
19
19
  import { TITLEBAR_HEIGHT } from "./window-chrome-contract";
20
20
  import { useWindowBodyElement } from "./use-window-body-element";
21
21
  import { WindowPipPlaceholder } from "./window-pip-placeholder";
22
+ import { isPipOnlyWindow } from "./window-pip-registry";
22
23
  import { WindowSkinChrome } from "./window-skin-chrome";
23
24
  import { windowZIndex, type Bounds, type Rect } from "./window-geometry";
24
25
  import { useWindowStore, type WindowRuntimeState } from "./window-store";
@@ -120,6 +121,8 @@ export function FloatingWindow({
120
121
  movable: !maximized,
121
122
  });
122
123
 
124
+ const hiddenHost = Boolean(pip) && isPipOnlyWindow(win.id);
125
+
123
126
  return (
124
127
  <div
125
128
  ref={rootRef}
@@ -135,6 +138,10 @@ export function FloatingWindow({
135
138
  focused ? "shadow-2xl shadow-black/40" : "shadow-lg shadow-black/20",
136
139
  // Transitions are for maximize/restore only; during a gesture they would lag the pointer.
137
140
  gesturing ? "transition-none" : "transition-[transform,width,height] duration-150 motion-reduce:transition-none",
141
+ // A window opened only to carry a tab into PiP is never shown: the user asked for a
142
+ // PiP, not a window. `hidden` and not an unmount — the body must stay connected, or
143
+ // the PiP host has nowhere to put the tab back.
144
+ hiddenHost && "hidden",
138
145
  )}
139
146
  >
140
147
  <WindowSkinChrome
@@ -158,7 +165,7 @@ export function FloatingWindow({
158
165
  onClose={close}
159
166
  />
160
167
 
161
- {pip && <WindowPipPlaceholder pip={pip} minimized={minimized} />}
168
+ {pip && !hiddenHost && <WindowPipPlaceholder pip={pip} minimized={minimized} />}
162
169
 
163
170
  {!maximized && !minimized && (
164
171
  <WindowResizeHandles bind={(handle) => bindResize(handle) as Record<string, unknown>} />