@openhands/agent-canvas 1.11.0 → 1.13.0

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 (766) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{acp-providers-BkL3HWa3.js → acp-providers-D1PORKc0.js} +1 -1
  4. package/build/assets/active-backend-context-IqOjobNK.js +2 -0
  5. package/build/assets/{agent-context-settings-Bno1nwPL.js → agent-context-settings-DMrObcIS.js} +1 -1
  6. package/build/assets/agent-profiles-settings-DororXN5.js +1 -0
  7. package/build/assets/agent-server-conversation-service.api-DWBe0rbX.js +86 -0
  8. package/build/assets/{agent-settings-h8hQz-IU.js → agent-settings-BJVk92PU.js} +1 -1
  9. package/build/assets/agent-settings-COfJsQ1d.js +1 -0
  10. package/build/assets/{alert-banner-lrK-2_0Y.js → alert-banner-CzZsm7ms.js} +1 -1
  11. package/build/assets/{api-key-entry-screen-Qsb83VX1.js → api-key-entry-screen-D8Fade_-.js} +1 -1
  12. package/build/assets/app-settings-CpyGB3AT.js +1 -0
  13. package/build/assets/{automation-detail-7LkL91j9.js → automation-detail-B-jQ39Mm.js} +1 -1
  14. package/build/assets/{automation-export-U2QNyNy6.js → automation-export-B1dywoqk.js} +1 -1
  15. package/build/assets/{automation-setup-route-Bel9_Lue.js → automation-setup-route-C-bWUZBi.js} +1 -1
  16. package/build/assets/{automation-templates-CRLDssaI.js → automation-templates-DgpCGOJV.js} +1 -1
  17. package/build/assets/{automations-list-B2iV3ZKW.js → automations-list-DnMxlxsL.js} +1 -1
  18. package/build/assets/{back-nav-button-B3pYwmUJ.js → back-nav-button-HHEneWMw.js} +1 -1
  19. package/build/assets/backend-form-modal-DiVrD4Cm.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-DB4717Iu.js → backend-synced-settings-badge-7MIjpr8M.js} +1 -1
  21. package/build/assets/{badge-yR53enbU.js → badge-CpYmcVZV.js} +1 -1
  22. package/build/assets/{base-modal-3NmIOKDm.js → base-modal-Fnh6Uln9.js} +1 -1
  23. package/build/assets/bot-BQZx8e2i.js +1 -0
  24. package/build/assets/{brand-button-CJcwjnfb.js → brand-button-swT1RyO0.js} +1 -1
  25. package/build/assets/browser-B9AbV6kG.js +7 -0
  26. package/build/assets/{browser-tab-A94o33Vn.js → browser-tab-zAY12m_i.js} +1 -1
  27. package/build/assets/{chat-send-button-B8n6-DQA.js → chat-send-button-DsJtivmL.js} +1 -1
  28. package/build/assets/check-WAFXrrs_.js +1 -0
  29. package/build/assets/chevron-down-CifNWzme.js +1 -0
  30. package/build/assets/circle-check-CtnimJa8.js +1 -0
  31. package/build/assets/{circle-plus-check-toggle-BVzv5EkZ.js → circle-plus-check-toggle-Cuzx55bo.js} +1 -1
  32. package/build/assets/{cloud-funnel-analytics-CBQdPZl_.js → cloud-funnel-analytics-86CwNdSh.js} +1 -1
  33. package/build/assets/{combobox-caret-BlmRwiM8.js → combobox-caret-D0fRotFi.js} +1 -1
  34. package/build/assets/{command-menu-PsRXVBz2.js → command-menu-BXy03FBl.js} +1 -1
  35. package/build/assets/{condenser-settings-w7L3EFvm.js → condenser-settings-BmZa0ui_.js} +1 -1
  36. package/build/assets/{confirmation-modal-DAH0XtOp.js → confirmation-modal-BgoN6lbN.js} +1 -1
  37. package/build/assets/{context-menu-list-item-BQhmFVEK.js → context-menu-list-item-Bq02tc-P.js} +1 -1
  38. package/build/assets/conversation-BPo8SvLN.js +1 -0
  39. package/build/assets/conversation-CPCVsCUY.js +90 -0
  40. package/build/assets/conversation-panel-BPo8SvLN.js +1 -0
  41. package/build/assets/{conversation-state-store-Bs8gA263.js → conversation-state-store-C5YpjMNy.js} +1 -1
  42. package/build/assets/conversation-store-Blc5xysY.js +6 -0
  43. package/build/assets/{conversation-tab-empty-state-C9sfDOlt.js → conversation-tab-empty-state-CMqo_0Bd.js} +1 -1
  44. package/build/assets/{copy-to-clipboard-button-DatoElj8.js → copy-to-clipboard-button-Cg1pF0xn.js} +1 -1
  45. package/build/assets/{device-verify-BIjXeW9N.js → device-verify-BfQLgNyq.js} +1 -1
  46. package/build/assets/{dropdown-classes-zFW7qPSK.js → dropdown-classes-BIqQr5mx.js} +1 -1
  47. package/build/assets/{entry.client-DzbDyLkV.js → entry.client-CxFjRYqH.js} +2 -2
  48. package/build/assets/{enum-filter-dropdown-CctILg5t.js → enum-filter-dropdown-C_yV4_bo.js} +1 -1
  49. package/build/assets/{environment-switch-overlay-Cvmo6clk.js → environment-switch-overlay-Bi4qbh2g.js} +1 -1
  50. package/build/assets/{extensions-hub-DOmHPGTA.js → extensions-hub-BqUINAhp.js} +1 -1
  51. package/build/assets/{extensions-navigation-6zU5M1pa.js → extensions-navigation-lraj88Wk.js} +1 -1
  52. package/build/assets/external-link-CgtpA_a1.js +1 -0
  53. package/build/assets/{file-text-RecuaId_.js → file-text-DEK4LSVw.js} +1 -1
  54. package/build/assets/files-tab-DV0L-lFE.js +1 -0
  55. package/build/assets/{form-control-classes-DLN3Qgub.js → form-control-classes-7yFSQJq_.js} +1 -1
  56. package/build/assets/format-model-name-BN_rx1Y3.js +1 -0
  57. package/build/assets/{format-relative-time-DGPQlPx0.js → format-relative-time-DSErAuVr.js} +1 -1
  58. package/build/assets/git-provider-icon-BvlLAche.js +1 -0
  59. package/build/assets/{git-repo-dropdown-B7RvO70z.js → git-repo-dropdown-ByMzpNko.js} +1 -1
  60. package/build/assets/git-repo-dropdown-C6R7hgM8.js +1 -0
  61. package/build/assets/{highlighted-source-view-CgJfD5VT.js → highlighted-source-view-C_sfncLZ.js} +1 -1
  62. package/build/assets/{home-DEZaZqYv.js → home-BOrlwLqG.js} +1 -1
  63. package/build/assets/home-BvMNC6E-.js +1 -0
  64. package/build/assets/{home-store-D7CjMN_-.js → home-store-CxqsY9Fi.js} +1 -1
  65. package/build/assets/index-home-BvMNC6E-.js +1 -0
  66. package/build/assets/info-BRVcJAUg.js +1 -0
  67. package/build/assets/{install-server-modal-BTwK5lbi.js → install-server-modal-Bl4G1zF4.js} +1 -1
  68. package/build/assets/{kebab-menu-item-content-C4CkeiJO.js → kebab-menu-item-content-BGoGFEAg.js} +1 -1
  69. package/build/assets/{launch-DVlx4v9o.js → launch-CkqiSCqi.js} +1 -1
  70. package/build/assets/{list-todo-CheUtB7F.js → list-todo-DNWlzfaM.js} +1 -1
  71. package/build/assets/llm-not-configured-banner-DLw_tu-G.js +27 -0
  72. package/build/assets/llm-settings-B-pkVCrt.js +1 -0
  73. package/build/assets/llm-settings-DSC0-IQ7.js +1 -0
  74. package/build/assets/llm-subscription-service-BAdOl5Xx.js +1 -0
  75. package/build/assets/loader-circle-B47W9XqT.js +1 -0
  76. package/build/assets/{loading-spinner-CN3riY7k.js → loading-spinner-xhnrGP4Y.js} +1 -1
  77. package/build/assets/{manage-backends-modal-HNWKLYhq.js → manage-backends-modal-DhGk5mJt.js} +1 -1
  78. package/build/assets/manifest-653b89aa.js +1 -0
  79. package/build/assets/{manifest-subpage-layout-D-m-h8_t.js → manifest-subpage-layout-CKWvL2ig.js} +1 -1
  80. package/build/assets/{map-provider-7Bp2aih5.js → map-provider-Mx8mZuE-.js} +1 -1
  81. package/build/assets/markdown-renderer-DUmk7pFc.js +8 -0
  82. package/build/assets/{mcp-gLgPP7AS.js → mcp-DCkvl6K0.js} +2 -2
  83. package/build/assets/mcp-marketplace-utils-DEbMXlQr.js +1 -0
  84. package/build/assets/messages-DSXvyc21.js +40 -0
  85. package/build/assets/{modal-body-duRaxj_K.js → modal-body-gIe21fDv.js} +1 -1
  86. package/build/assets/{modal-classes-CLEXO6dj.js → modal-classes-D66dlgZ7.js} +1 -1
  87. package/build/assets/{modal-close-button-D0aT02hs.js → modal-close-button-B6iOGYpI.js} +1 -1
  88. package/build/assets/{mouse-pointer-click-Px-dXWcS.js → mouse-pointer-click-BYX1Pw5W.js} +1 -1
  89. package/build/assets/{npm-DRzJF5Zn.js → npm-DLckoeJc.js} +1 -1
  90. package/build/assets/{onboarding-DSr8hOpk.js → onboarding-D7WDG66N.js} +1 -1
  91. package/build/assets/{onboarding-modal-C6Euf7N4.js → onboarding-modal-C_xVrFNv.js} +1 -1
  92. package/build/assets/{organization-service.api-C4GYWwj9.js → organization-service.api-D-s9z18a.js} +1 -1
  93. package/build/assets/pencil-BGOCPRyu.js +1 -0
  94. package/build/assets/{planner-tab-CZcguxTK.js → planner-tab-AKaXIIfg.js} +1 -1
  95. package/build/assets/{plugins-management-service-BmDzOQsd.js → plugins-management-service-wtmY4QQw.js} +1 -1
  96. package/build/assets/plus-zNEL8rcX.js +1 -0
  97. package/build/assets/{profiles-service.api-CoLTwjc1.js → profiles-service.api-DCMYxHMi.js} +1 -1
  98. package/build/assets/{providers-tBzTpz1w.js → providers-BjrtjX4L.js} +1 -1
  99. package/build/assets/{proxy-DCI9S-LA.js → proxy-9Z_WZggZ.js} +1 -1
  100. package/build/assets/{puzzle-B3v361yY.js → puzzle-BWLmN8z4.js} +1 -1
  101. package/build/assets/recommended-automations-launcher-CFlzhu1o.js +1 -0
  102. package/build/assets/refresh-cw-CXtFWpwv.js +1 -0
  103. package/build/assets/root-CanxKlNV.css +1 -0
  104. package/build/assets/root-DkfG_UWR.js +2 -0
  105. package/build/assets/root-layout-D75LHYhh.js +2 -0
  106. package/build/assets/{schema-field-zhsVDllR.js → schema-field-OnM5Uob_.js} +1 -1
  107. package/build/assets/{sdk-section-page-CS4GWn0y.js → sdk-section-page-m5WQm2sy.js} +1 -1
  108. package/build/assets/search-CycosyjN.js +1 -0
  109. package/build/assets/{secrets-service-DjgGduXk.js → secrets-service-B26R61C1.js} +1 -1
  110. package/build/assets/{secrets-settings-f-qahfbi.js → secrets-settings-CbLYCKOX.js} +1 -1
  111. package/build/assets/{segmented-toggle-SmvsR3jW.js → segmented-toggle-DCspm2U5.js} +1 -1
  112. package/build/assets/{settings-DQd0xLEw.js → settings-BJQI_iBM.js} +1 -1
  113. package/build/assets/{settings-dropdown-input-TWIeiG02.js → settings-dropdown-input-BI8V_Nov.js} +1 -1
  114. package/build/assets/{settings-index-BVwy9JEp.js → settings-index-BiTqJoH7.js} +1 -1
  115. package/build/assets/{settings-input-D61Vzp9e.js → settings-input-Cz62hWVW.js} +1 -1
  116. package/build/assets/{settings-list-classes-sE0ASr1s.js → settings-list-classes-z_3FW5H3.js} +1 -1
  117. package/build/assets/{settings-modal-D6D5ngET.js → settings-modal-BIrIVn97.js} +1 -1
  118. package/build/assets/{settings-service.api-DZp7E7A9.js → settings-service.api--rJmnuLa.js} +1 -1
  119. package/build/assets/{settings-switch-Drt-rF5s.js → settings-switch-BRsSI8Vs.js} +1 -1
  120. package/build/assets/{settings-utils-gw2ppAvw.js → settings-utils-Ba7vel6T.js} +1 -1
  121. package/build/assets/shared-conversation-CQK_PqHx.js +1 -0
  122. package/build/assets/shield-check-T3JsDJiM.js +1 -0
  123. package/build/assets/{sidebar-layout-Dxg6zc2K.js → sidebar-layout-Bhi3hVcu.js} +1 -1
  124. package/build/assets/{sidebar-mobile-menu-toggle-OoZBHxaT.js → sidebar-mobile-menu-toggle-YWJNRN4p.js} +1 -1
  125. package/build/assets/{sidebar-nav-link-CxLoG-dG.js → sidebar-nav-link-BSUlY5Ml.js} +1 -1
  126. package/build/assets/{sidebar-store-BJeYY-E9.js → sidebar-store-Cl1cUv_F.js} +1 -1
  127. package/build/assets/{skill-card-pill-row-B_BYBONu.js → skill-card-pill-row-BXz8L8Rn.js} +1 -1
  128. package/build/assets/{skill-icon-badge-CN41Hy9l.js → skill-icon-badge-QgfKv31M.js} +1 -1
  129. package/build/assets/{skill-scope-B1Per5XI.js → skill-scope-Dc3JPW-8.js} +1 -1
  130. package/build/assets/{skills-plugins-B3AO6sMz.js → skills-plugins-CpTV27n3.js} +1 -1
  131. package/build/assets/{skills-settings-DmmgJ6CT.js → skills-settings-CSW__IW4.js} +1 -1
  132. package/build/assets/{sparkles-g7y3r-Ut.js → sparkles-TJ0mdECe.js} +1 -1
  133. package/build/assets/{styled-tooltip-C5C07RQC.js → styled-tooltip-BUMSbgrF.js} +1 -1
  134. package/build/assets/{task-list-tab-C19Q_JNf.js → task-list-tab-mAHMOkUz.js} +1 -1
  135. package/build/assets/{terminal-CbuMRYal.js → terminal-BzxBaHx2.js} +1 -1
  136. package/build/assets/{toggle-switch-CIioisto.js → toggle-switch-O5v1FgxW.js} +1 -1
  137. package/build/assets/trash-2-C0xNVp9c.js +1 -0
  138. package/build/assets/{typography-D9tiHct7.js → typography-NOKSk9_R.js} +1 -1
  139. package/build/assets/usage-tab-tEmVN0KD.js +1 -0
  140. package/build/assets/{use-acp-credential-form-DJmmJKMo.js → use-acp-credential-form-BPNi0Zbm.js} +1 -1
  141. package/build/assets/{use-activate-agent-profile-CeFfYwu_.js → use-activate-agent-profile-D9YRQPTF.js} +1 -1
  142. package/build/assets/{use-agent-profiles-BHLkLbRR.js → use-agent-profiles-Cb2zUW3l.js} +1 -1
  143. package/build/assets/{use-can-manage-org-profiles-Bu4y8gSx.js → use-can-manage-org-profiles-Dxzq2AEd.js} +1 -1
  144. package/build/assets/{use-cloud-current-user-id-QD9x3ySO.js → use-cloud-current-user-id-Co2eJk2A.js} +1 -1
  145. package/build/assets/{use-config-CPwqciXN.js → use-config-DKAMiI5R.js} +1 -1
  146. package/build/assets/use-create-conversation-CoHTiCbI.js +1 -0
  147. package/build/assets/{use-create-secret-BsOfOLvg.js → use-create-secret-2zBlCDsF.js} +1 -1
  148. package/build/assets/use-is-authed-C52-ivnB.js +1 -0
  149. package/build/assets/{use-is-creating-conversation-CVefC9Ns.js → use-is-creating-conversation-DdaAE6jV.js} +1 -1
  150. package/build/assets/{use-launch-skill-in-chat-BcKNjxxd.js → use-launch-skill-in-chat-COnDpxCQ.js} +1 -1
  151. package/build/assets/use-llm-profiles-C_BHue65.js +1 -0
  152. package/build/assets/{use-manifest-capabilities-DBYPjyV6.js → use-manifest-capabilities-DPrQsaPP.js} +1 -1
  153. package/build/assets/{use-onboarding-completion-CtNJxkXG.js → use-onboarding-completion-CCE8OQgV.js} +1 -1
  154. package/build/assets/{use-plugins-marketplace-ZEA-gyM9.js → use-plugins-marketplace-CEiak69h.js} +1 -1
  155. package/build/assets/use-save-agent-profile-DK9idD6a.js +1 -0
  156. package/build/assets/{use-save-settings-pSjRhD_m.js → use-save-settings-DNmnbG6f.js} +1 -1
  157. package/build/assets/{use-settings-nav-items-CQgkxUbV.js → use-settings-nav-items-BlFL7YhM.js} +1 -1
  158. package/build/assets/{use-settings-BHWsOcIG.js → use-settings-wCwPaSIv.js} +1 -1
  159. package/build/assets/{use-tracking-CGCsBWhg.js → use-tracking-CVwPmhlX.js} +1 -1
  160. package/build/assets/{use-user-conversation-DRfEU8x0.js → use-user-conversation-DVtfg0B_.js} +1 -1
  161. package/build/assets/{useInfiniteQuery-DbCv0Jzd.js → useInfiniteQuery-p1xq35r2.js} +1 -1
  162. package/build/assets/{useMutation-ChpkYxI5.js → useMutation-BFDQmgk9.js} +1 -1
  163. package/build/assets/{useQueries-Cjv3E8_1.js → useQueries-DF1NAAeC.js} +1 -1
  164. package/build/assets/{useQuery-BCnzy4LX.js → useQuery-DRv--6bK.js} +1 -1
  165. package/build/assets/utils-zxEjKE70.js +1 -0
  166. package/build/assets/{vendor~browser-DJ1pl_wn.js → vendor~browser-B-fae1JU.js} +14 -14
  167. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kv3pap4j-oGvLV0dO.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kv3pap4j-B8i84bh4.js} +3 -3
  168. package/build/assets/{vendor~files-tab-C87pjSpi.js → vendor~files-tab-D9wR9yM2.js} +1 -1
  169. package/build/assets/vendor~index-home~home~conversation-panel~conversation-Ipoe6hvY.js +13 -0
  170. package/build/assets/vendor~root-layout-C-IgVx6e.js +1 -0
  171. package/build/assets/{vendor~root-layout~conversation-panel~conversation-DEzjR4K7.js → vendor~root-layout~conversation-panel~conversation-Dhd_I4H4.js} +1 -1
  172. package/build/assets/{vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-U-xdsAEx.js → vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-Cc0f9kPP.js} +1 -1
  173. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-CQR5Sa0H.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-d7svw-BB.js} +1 -1
  174. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~gxu4j2ea-Cwhh3zLe.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~gxu4j2ea-D5DWZVc7.js} +1 -1
  175. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-CuQhkiS7.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-4WsUP-tz.js} +2 -2
  176. package/build/assets/{verification-settings-BItwt998.js → verification-settings-B8UMQ7MG.js} +1 -1
  177. package/build/assets/{wrench-B1_H-tL1.js → wrench-DIka3hCq.js} +1 -1
  178. package/build/assets/x-BWKA2Ver.js +1 -0
  179. package/build/assets/zap-DHddTPc0.js +1 -0
  180. package/build/index.html +4 -4
  181. package/build/locales/ar/openhands.json +28 -1
  182. package/build/locales/ca/openhands.json +28 -1
  183. package/build/locales/de/openhands.json +28 -1
  184. package/build/locales/en/openhands.json +28 -1
  185. package/build/locales/es/openhands.json +28 -1
  186. package/build/locales/fr/openhands.json +28 -1
  187. package/build/locales/it/openhands.json +28 -1
  188. package/build/locales/ja/openhands.json +28 -1
  189. package/build/locales/ko-KR/openhands.json +28 -1
  190. package/build/locales/no/openhands.json +28 -1
  191. package/build/locales/pt/openhands.json +28 -1
  192. package/build/locales/tr/openhands.json +28 -1
  193. package/build/locales/uk/openhands.json +28 -1
  194. package/build/locales/zh-CN/openhands.json +28 -1
  195. package/build/locales/zh-TW/openhands.json +28 -1
  196. package/config/defaults.json +3 -3
  197. package/dist/api/agent-server-compatibility.cjs +1 -1
  198. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  199. package/dist/api/agent-server-compatibility.js +1 -1
  200. package/dist/api/agent-server-compatibility.js.map +1 -1
  201. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  202. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  203. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +24 -1
  204. package/dist/api/conversation-service/agent-server-conversation-service.api.js +116 -97
  205. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  206. package/dist/api/llm-balance-service.cjs +2 -0
  207. package/dist/api/llm-balance-service.cjs.map +1 -0
  208. package/dist/api/llm-balance-service.d.ts +33 -0
  209. package/dist/api/llm-balance-service.js +53 -0
  210. package/dist/api/llm-balance-service.js.map +1 -0
  211. package/dist/api/llm-subscription-service.cjs +1 -1
  212. package/dist/api/llm-subscription-service.cjs.map +1 -1
  213. package/dist/api/llm-subscription-service.js +42 -34
  214. package/dist/api/llm-subscription-service.js.map +1 -1
  215. package/dist/components/conversation/index.cjs +1 -1
  216. package/dist/components/conversation/index.d.ts +0 -1
  217. package/dist/components/conversation/index.js +0 -1
  218. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
  219. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
  220. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +1 -1
  221. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
  222. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs +1 -1
  223. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs.map +1 -1
  224. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js +24 -23
  225. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js.map +1 -1
  226. package/dist/components/conversation-events/chat/group-events.cjs +1 -1
  227. package/dist/components/conversation-events/chat/group-events.cjs.map +1 -1
  228. package/dist/components/conversation-events/chat/group-events.d.ts +3 -3
  229. package/dist/components/conversation-events/chat/group-events.js +27 -26
  230. package/dist/components/conversation-events/chat/group-events.js.map +1 -1
  231. package/dist/components/features/automations/recommended-automations-section.d.ts +7 -0
  232. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  233. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  234. package/dist/components/features/chat/components/chat-input-actions.js +123 -118
  235. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  236. package/dist/components/features/chat/components/chat-input-grip.cjs +1 -1
  237. package/dist/components/features/chat/components/chat-input-grip.cjs.map +1 -1
  238. package/dist/components/features/chat/components/chat-input-grip.d.ts +2 -1
  239. package/dist/components/features/chat/components/chat-input-grip.js +7 -7
  240. package/dist/components/features/chat/components/chat-input-grip.js.map +1 -1
  241. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs +1 -1
  242. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs.map +1 -1
  243. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js +61 -60
  244. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js.map +1 -1
  245. package/dist/components/features/chat/components/context-window-meter.cjs +2 -0
  246. package/dist/components/features/chat/components/context-window-meter.cjs.map +1 -0
  247. package/dist/components/features/chat/components/context-window-meter.d.ts +2 -0
  248. package/dist/components/features/chat/components/context-window-meter.js +131 -0
  249. package/dist/components/features/chat/components/context-window-meter.js.map +1 -0
  250. package/dist/components/features/chat/components/context-window-ring.cjs +2 -0
  251. package/dist/components/features/chat/components/context-window-ring.cjs.map +1 -0
  252. package/dist/components/features/chat/components/context-window-ring.d.ts +26 -0
  253. package/dist/components/features/chat/components/context-window-ring.js +45 -0
  254. package/dist/components/features/chat/components/context-window-ring.js.map +1 -0
  255. package/dist/components/features/chat/custom-chat-input.cjs +1 -1
  256. package/dist/components/features/chat/custom-chat-input.cjs.map +1 -1
  257. package/dist/components/features/chat/custom-chat-input.js +68 -67
  258. package/dist/components/features/chat/custom-chat-input.js.map +1 -1
  259. package/dist/components/features/chat/model-messages.cjs +1 -1
  260. package/dist/components/features/chat/model-messages.cjs.map +1 -1
  261. package/dist/components/features/chat/model-messages.js +26 -25
  262. package/dist/components/features/chat/model-messages.js.map +1 -1
  263. package/dist/components/features/chat/path-component.cjs +1 -1
  264. package/dist/components/features/chat/path-component.cjs.map +1 -1
  265. package/dist/components/features/chat/path-component.js +9 -6
  266. package/dist/components/features/chat/path-component.js.map +1 -1
  267. package/dist/components/features/chat/plan-preview.cjs +1 -1
  268. package/dist/components/features/chat/plan-preview.js +4 -4
  269. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs +1 -1
  270. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs.map +1 -1
  271. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.d.ts +0 -8
  272. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js +92 -50
  273. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js.map +1 -1
  274. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs +1 -1
  275. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs.map +1 -1
  276. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.d.ts +7 -2
  277. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js +15 -9
  278. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js.map +1 -1
  279. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.cjs +2 -0
  280. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.cjs.map +1 -0
  281. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.d.ts +20 -0
  282. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.js +76 -0
  283. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.js.map +1 -0
  284. package/dist/components/features/controls/server-status-context-menu.cjs +1 -1
  285. package/dist/components/features/controls/server-status-context-menu.cjs.map +1 -1
  286. package/dist/components/features/controls/server-status-context-menu.d.ts +5 -3
  287. package/dist/components/features/controls/server-status-context-menu.js +8 -8
  288. package/dist/components/features/controls/server-status-context-menu.js.map +1 -1
  289. package/dist/components/features/conversation/conversation-name-context-menu.cjs +1 -1
  290. package/dist/components/features/conversation/conversation-name-context-menu.cjs.map +1 -1
  291. package/dist/components/features/conversation/conversation-name-context-menu.js +58 -61
  292. package/dist/components/features/conversation/conversation-name-context-menu.js.map +1 -1
  293. package/dist/components/features/conversation/conversation-name-with-status.cjs +1 -1
  294. package/dist/components/features/conversation/conversation-name-with-status.cjs.map +1 -1
  295. package/dist/components/features/conversation/conversation-name-with-status.js +74 -46
  296. package/dist/components/features/conversation/conversation-name-with-status.js.map +1 -1
  297. package/dist/components/features/conversation/conversation-name.cjs +1 -1
  298. package/dist/components/features/conversation/conversation-name.cjs.map +1 -1
  299. package/dist/components/features/conversation/conversation-name.js +69 -71
  300. package/dist/components/features/conversation/conversation-name.js.map +1 -1
  301. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.cjs +1 -1
  302. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.cjs.map +1 -1
  303. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.js +7 -6
  304. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.js.map +1 -1
  305. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs +1 -1
  306. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs.map +1 -1
  307. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js +92 -86
  308. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js.map +1 -1
  309. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
  310. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
  311. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +81 -71
  312. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
  313. package/dist/components/features/conversation/metrics-modal/cost-section.cjs +1 -1
  314. package/dist/components/features/conversation/metrics-modal/cost-section.cjs.map +1 -1
  315. package/dist/components/features/conversation/metrics-modal/cost-section.js +6 -6
  316. package/dist/components/features/conversation/metrics-modal/cost-section.js.map +1 -1
  317. package/dist/components/features/conversation/metrics-modal/metric-row.cjs +1 -1
  318. package/dist/components/features/conversation/metrics-modal/metric-row.cjs.map +1 -1
  319. package/dist/components/features/conversation/metrics-modal/metric-row.d.ts +2 -1
  320. package/dist/components/features/conversation/metrics-modal/metric-row.js +10 -9
  321. package/dist/components/features/conversation/metrics-modal/metric-row.js.map +1 -1
  322. package/dist/components/features/conversation/metrics-modal/usage-section.cjs +1 -1
  323. package/dist/components/features/conversation/metrics-modal/usage-section.cjs.map +1 -1
  324. package/dist/components/features/conversation/metrics-modal/usage-section.js +2 -1
  325. package/dist/components/features/conversation/metrics-modal/usage-section.js.map +1 -1
  326. package/dist/components/features/conversation/usage-panel/compact-context-button.cjs +2 -0
  327. package/dist/components/features/conversation/usage-panel/compact-context-button.cjs.map +1 -0
  328. package/dist/components/features/conversation/usage-panel/compact-context-button.d.ts +16 -0
  329. package/dist/components/features/conversation/usage-panel/compact-context-button.js +59 -0
  330. package/dist/components/features/conversation/usage-panel/compact-context-button.js.map +1 -0
  331. package/dist/components/features/conversation/usage-panel/context-meter.cjs +2 -0
  332. package/dist/components/features/conversation/usage-panel/context-meter.cjs.map +1 -0
  333. package/dist/components/features/conversation/usage-panel/context-meter.d.ts +19 -0
  334. package/dist/components/features/conversation/usage-panel/context-meter.js +46 -0
  335. package/dist/components/features/conversation/usage-panel/context-meter.js.map +1 -0
  336. package/dist/components/features/conversation/usage-panel/provider-balance-card.cjs +2 -0
  337. package/dist/components/features/conversation/usage-panel/provider-balance-card.cjs.map +1 -0
  338. package/dist/components/features/conversation/usage-panel/provider-balance-card.d.ts +7 -0
  339. package/dist/components/features/conversation/usage-panel/provider-balance-card.js +57 -0
  340. package/dist/components/features/conversation/usage-panel/provider-balance-card.js.map +1 -0
  341. package/dist/components/features/conversation/usage-panel/usage-panel.cjs +2 -0
  342. package/dist/components/features/conversation/usage-panel/usage-panel.cjs.map +1 -0
  343. package/dist/components/features/conversation/usage-panel/usage-panel.d.ts +9 -0
  344. package/dist/components/features/conversation/usage-panel/usage-panel.js +68 -0
  345. package/dist/components/features/conversation/usage-panel/usage-panel.js.map +1 -0
  346. package/dist/components/features/conversation-panel/budget-display.cjs +1 -1
  347. package/dist/components/features/conversation-panel/budget-display.cjs.map +1 -1
  348. package/dist/components/features/conversation-panel/budget-display.js +10 -13
  349. package/dist/components/features/conversation-panel/budget-display.js.map +1 -1
  350. package/dist/components/features/conversation-panel/budget-progress-bar.cjs +1 -1
  351. package/dist/components/features/conversation-panel/budget-progress-bar.cjs.map +1 -1
  352. package/dist/components/features/conversation-panel/budget-progress-bar.js +2 -2
  353. package/dist/components/features/conversation-panel/budget-progress-bar.js.map +1 -1
  354. package/dist/components/features/conversation-panel/confirm-archive-modal.cjs +2 -0
  355. package/dist/components/features/conversation-panel/confirm-archive-modal.cjs.map +1 -0
  356. package/dist/components/features/conversation-panel/confirm-archive-modal.d.ts +8 -0
  357. package/dist/components/features/conversation-panel/confirm-archive-modal.js +48 -0
  358. package/dist/components/features/conversation-panel/confirm-archive-modal.js.map +1 -0
  359. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs +1 -1
  360. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs.map +1 -1
  361. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.d.ts +3 -1
  362. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js +39 -34
  363. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js.map +1 -1
  364. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
  365. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
  366. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.d.ts +3 -1
  367. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +104 -80
  368. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
  369. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -1
  370. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
  371. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.d.ts +7 -1
  372. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +28 -20
  373. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
  374. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs +1 -1
  375. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs.map +1 -1
  376. package/dist/components/features/conversation-panel/conversation-card/conversation-card.d.ts +9 -1
  377. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js +94 -84
  378. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js.map +1 -1
  379. package/dist/components/features/conversation-panel/conversation-group-folder-list.cjs +1 -1
  380. package/dist/components/features/conversation-panel/conversation-group-folder-list.cjs.map +1 -1
  381. package/dist/components/features/conversation-panel/conversation-group-folder-list.d.ts +2 -1
  382. package/dist/components/features/conversation-panel/conversation-group-folder-list.js +32 -31
  383. package/dist/components/features/conversation-panel/conversation-group-folder-list.js.map +1 -1
  384. package/dist/components/features/conversation-panel/conversation-group-folder-row.cjs +1 -1
  385. package/dist/components/features/conversation-panel/conversation-group-folder-row.cjs.map +1 -1
  386. package/dist/components/features/conversation-panel/conversation-group-folder-row.d.ts +2 -1
  387. package/dist/components/features/conversation-panel/conversation-group-folder-row.js +27 -26
  388. package/dist/components/features/conversation-panel/conversation-group-folder-row.js.map +1 -1
  389. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +1 -1
  390. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +1 -1
  391. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +3 -1
  392. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +116 -106
  393. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +1 -1
  394. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
  395. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
  396. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +27 -0
  397. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +69 -46
  398. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
  399. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  400. package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
  401. package/dist/components/features/conversation-panel/conversation-panel.js +230 -165
  402. package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
  403. package/dist/components/features/files-tab/file-content-viewer.cjs +1 -1
  404. package/dist/components/features/files-tab/file-content-viewer.cjs.map +1 -1
  405. package/dist/components/features/files-tab/file-content-viewer.js +22 -25
  406. package/dist/components/features/files-tab/file-content-viewer.js.map +1 -1
  407. package/dist/components/features/markdown/markdown-table-scroll.cjs +1 -1
  408. package/dist/components/features/markdown/markdown-table-scroll.cjs.map +1 -1
  409. package/dist/components/features/markdown/markdown-table-scroll.js +2 -2
  410. package/dist/components/features/markdown/markdown-table-scroll.js.map +1 -1
  411. package/dist/components/features/settings/llm-profiles/profile-row.cjs +1 -1
  412. package/dist/components/features/settings/llm-profiles/profile-row.js +1 -0
  413. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs +1 -1
  414. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs.map +1 -1
  415. package/dist/components/shared/buttons/conversation-confirmation-buttons.js +2 -2
  416. package/dist/components/shared/buttons/conversation-confirmation-buttons.js.map +1 -1
  417. package/dist/components/shared/modals/confirmation-modals/base-modal.cjs +1 -1
  418. package/dist/components/shared/modals/confirmation-modals/base-modal.cjs.map +1 -1
  419. package/dist/components/shared/modals/confirmation-modals/base-modal.js +1 -1
  420. package/dist/components/shared/modals/confirmation-modals/base-modal.js.map +1 -1
  421. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  422. package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
  423. package/dist/components/shared/modals/settings/model-selector.js +154 -98
  424. package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
  425. package/dist/config/defaults.cjs +1 -1
  426. package/dist/config/defaults.cjs.map +1 -1
  427. package/dist/config/defaults.js +3 -3
  428. package/dist/config/defaults.js.map +1 -1
  429. package/dist/constants/llm-balance.cjs +2 -0
  430. package/dist/constants/llm-balance.cjs.map +1 -0
  431. package/dist/constants/llm-balance.d.ts +13 -0
  432. package/dist/constants/llm-balance.js +6 -0
  433. package/dist/constants/llm-balance.js.map +1 -0
  434. package/dist/contexts/conversation-websocket-context.cjs +2 -2
  435. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  436. package/dist/contexts/conversation-websocket-context.js +217 -209
  437. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  438. package/dist/hooks/chat/use-grip-resize.cjs +1 -1
  439. package/dist/hooks/chat/use-grip-resize.cjs.map +1 -1
  440. package/dist/hooks/chat/use-grip-resize.d.ts +1 -0
  441. package/dist/hooks/chat/use-grip-resize.js +39 -30
  442. package/dist/hooks/chat/use-grip-resize.js.map +1 -1
  443. package/dist/hooks/chat/use-slash-command.cjs +1 -1
  444. package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
  445. package/dist/hooks/chat/use-slash-command.js +61 -60
  446. package/dist/hooks/chat/use-slash-command.js.map +1 -1
  447. package/dist/hooks/mutation/use-condense-conversation.cjs +2 -0
  448. package/dist/hooks/mutation/use-condense-conversation.cjs.map +1 -0
  449. package/dist/hooks/mutation/use-condense-conversation.d.ts +12 -0
  450. package/dist/hooks/mutation/use-condense-conversation.js +17 -0
  451. package/dist/hooks/mutation/use-condense-conversation.js.map +1 -0
  452. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  453. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  454. package/dist/hooks/mutation/use-create-conversation.js +30 -21
  455. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  456. package/dist/hooks/mutation/use-new-conversation-command.cjs +1 -1
  457. package/dist/hooks/mutation/use-new-conversation-command.cjs.map +1 -1
  458. package/dist/hooks/mutation/use-new-conversation-command.js +1 -1
  459. package/dist/hooks/mutation/use-new-conversation-command.js.map +1 -1
  460. package/dist/hooks/query/use-conversation-metrics.cjs +1 -1
  461. package/dist/hooks/query/use-conversation-metrics.cjs.map +1 -1
  462. package/dist/hooks/query/use-conversation-metrics.js +1 -1
  463. package/dist/hooks/query/use-conversation-metrics.js.map +1 -1
  464. package/dist/hooks/query/use-llm-balance.cjs +2 -0
  465. package/dist/hooks/query/use-llm-balance.cjs.map +1 -0
  466. package/dist/hooks/query/use-llm-balance.d.ts +10 -0
  467. package/dist/hooks/query/use-llm-balance.js +24 -0
  468. package/dist/hooks/query/use-llm-balance.js.map +1 -0
  469. package/dist/hooks/query/use-search-providers.cjs +1 -1
  470. package/dist/hooks/query/use-search-providers.cjs.map +1 -1
  471. package/dist/hooks/query/use-search-providers.js +1 -1
  472. package/dist/hooks/query/use-search-providers.js.map +1 -1
  473. package/dist/hooks/use-await-context-compaction.cjs +2 -0
  474. package/dist/hooks/use-await-context-compaction.cjs.map +1 -0
  475. package/dist/hooks/use-await-context-compaction.d.ts +38 -0
  476. package/dist/hooks/use-await-context-compaction.js +61 -0
  477. package/dist/hooks/use-await-context-compaction.js.map +1 -0
  478. package/dist/hooks/use-compact-context-action.cjs +2 -0
  479. package/dist/hooks/use-compact-context-action.cjs.map +1 -0
  480. package/dist/hooks/use-compact-context-action.d.ts +10 -0
  481. package/dist/hooks/use-compact-context-action.js +61 -0
  482. package/dist/hooks/use-compact-context-action.js.map +1 -0
  483. package/dist/hooks/use-context-window-usage.cjs +2 -0
  484. package/dist/hooks/use-context-window-usage.cjs.map +1 -0
  485. package/dist/hooks/use-context-window-usage.d.ts +5 -0
  486. package/dist/hooks/use-context-window-usage.js +20 -0
  487. package/dist/hooks/use-context-window-usage.js.map +1 -0
  488. package/dist/hooks/use-conversation-name-context-menu.cjs +1 -1
  489. package/dist/hooks/use-conversation-name-context-menu.cjs.map +1 -1
  490. package/dist/hooks/use-conversation-name-context-menu.d.ts +0 -3
  491. package/dist/hooks/use-conversation-name-context-menu.js +54 -57
  492. package/dist/hooks/use-conversation-name-context-menu.js.map +1 -1
  493. package/dist/hooks/use-drag-resize.cjs +1 -1
  494. package/dist/hooks/use-drag-resize.cjs.map +1 -1
  495. package/dist/hooks/use-drag-resize.d.ts +8 -0
  496. package/dist/hooks/use-drag-resize.js +21 -18
  497. package/dist/hooks/use-drag-resize.js.map +1 -1
  498. package/dist/hooks/use-live-conversation-metrics.cjs +2 -0
  499. package/dist/hooks/use-live-conversation-metrics.cjs.map +1 -0
  500. package/dist/hooks/use-live-conversation-metrics.d.ts +18 -0
  501. package/dist/hooks/use-live-conversation-metrics.js +37 -0
  502. package/dist/hooks/use-live-conversation-metrics.js.map +1 -0
  503. package/dist/hooks/use-select-conversation-tab.cjs +1 -1
  504. package/dist/hooks/use-select-conversation-tab.cjs.map +1 -1
  505. package/dist/hooks/use-select-conversation-tab.js +2 -2
  506. package/dist/hooks/use-select-conversation-tab.js.map +1 -1
  507. package/dist/i18n/declaration.cjs +1 -1
  508. package/dist/i18n/declaration.cjs.map +1 -1
  509. package/dist/i18n/declaration.d.ts +27 -0
  510. package/dist/i18n/declaration.js +1 -1
  511. package/dist/i18n/declaration.js.map +1 -1
  512. package/dist/i18n/translation.cjs +2 -2
  513. package/dist/i18n/translation.cjs.map +1 -1
  514. package/dist/i18n/translation.js +474 -15
  515. package/dist/i18n/translation.js.map +1 -1
  516. package/dist/index.cjs +1 -1
  517. package/dist/index.js +28 -29
  518. package/dist/lib/index.cjs +1 -1
  519. package/dist/lib/index.js +0 -1
  520. package/dist/locales/ar/openhands.json +28 -1
  521. package/dist/locales/ca/openhands.json +28 -1
  522. package/dist/locales/de/openhands.json +28 -1
  523. package/dist/locales/en/openhands.json +28 -1
  524. package/dist/locales/es/openhands.json +28 -1
  525. package/dist/locales/fr/openhands.json +28 -1
  526. package/dist/locales/it/openhands.json +28 -1
  527. package/dist/locales/ja/openhands.json +28 -1
  528. package/dist/locales/ko-KR/openhands.json +28 -1
  529. package/dist/locales/no/openhands.json +28 -1
  530. package/dist/locales/pt/openhands.json +28 -1
  531. package/dist/locales/tr/openhands.json +28 -1
  532. package/dist/locales/uk/openhands.json +28 -1
  533. package/dist/locales/zh-CN/openhands.json +28 -1
  534. package/dist/locales/zh-TW/openhands.json +28 -1
  535. package/dist/node_modules/@microlink/react-json-view/dist/main.cjs +5 -5
  536. package/dist/node_modules/@microlink/react-json-view/dist/main.cjs.map +1 -1
  537. package/dist/node_modules/@microlink/react-json-view/dist/main.js +5194 -5187
  538. package/dist/node_modules/@microlink/react-json-view/dist/main.js.map +1 -1
  539. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs +2 -1
  540. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs.map +1 -0
  541. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js +21 -1
  542. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js.map +1 -0
  543. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.cjs +2 -1
  544. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.cjs.map +1 -0
  545. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.js +22 -1
  546. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.js.map +1 -0
  547. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.cjs +2 -1
  548. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.cjs.map +1 -0
  549. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.js +33 -1
  550. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.js.map +1 -0
  551. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs +1 -1
  552. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs.map +1 -1
  553. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js +56 -22
  554. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js.map +1 -1
  555. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.cjs +1 -1
  556. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.cjs.map +1 -1
  557. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.js +3 -0
  558. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.js.map +1 -1
  559. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.cjs +2 -1
  560. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.cjs.map +1 -0
  561. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.js +31 -1
  562. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.js.map +1 -0
  563. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.cjs +2 -1
  564. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.cjs.map +1 -0
  565. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.js +21 -1
  566. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.js.map +1 -0
  567. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.cjs +2 -1
  568. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.cjs.map +1 -0
  569. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.js +21 -1
  570. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.js.map +1 -0
  571. package/dist/node_modules/@openhands/typescript-client/dist/conversation/local-conversation.cjs.map +1 -1
  572. package/dist/node_modules/@openhands/typescript-client/dist/conversation/local-conversation.js.map +1 -1
  573. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  574. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  575. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +9 -5
  576. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  577. package/dist/node_modules/i18next-http-backend/esm/index.cjs +1 -1
  578. package/dist/node_modules/i18next-http-backend/esm/index.cjs.map +1 -1
  579. package/dist/node_modules/i18next-http-backend/esm/index.js +2 -2
  580. package/dist/node_modules/i18next-http-backend/esm/index.js.map +1 -1
  581. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.cjs +2 -0
  582. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.cjs.map +1 -0
  583. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.js +31 -0
  584. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.js.map +1 -0
  585. package/dist/node_modules/lucide-react/dist/esm/icons/archive.cjs +2 -0
  586. package/dist/node_modules/lucide-react/dist/esm/icons/archive.cjs.map +1 -0
  587. package/dist/node_modules/lucide-react/dist/esm/icons/archive.js +23 -0
  588. package/dist/node_modules/lucide-react/dist/esm/icons/archive.js.map +1 -0
  589. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.cjs +2 -0
  590. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.cjs.map +1 -0
  591. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.js +12 -0
  592. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.js.map +1 -0
  593. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.cjs +2 -0
  594. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.cjs.map +1 -0
  595. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.js +23 -0
  596. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.js.map +1 -0
  597. package/dist/node_modules/zustand/esm/middleware.cjs +3 -3
  598. package/dist/node_modules/zustand/esm/middleware.cjs.map +1 -1
  599. package/dist/node_modules/zustand/esm/middleware.js +33 -31
  600. package/dist/node_modules/zustand/esm/middleware.js.map +1 -1
  601. package/dist/package.cjs +1 -1
  602. package/dist/package.cjs.map +1 -1
  603. package/dist/package.js +17 -16
  604. package/dist/package.js.map +1 -1
  605. package/dist/routes/app-settings.cjs +1 -1
  606. package/dist/routes/app-settings.cjs.map +1 -1
  607. package/dist/routes/app-settings.js +94 -93
  608. package/dist/routes/app-settings.js.map +1 -1
  609. package/dist/routes/llm-settings.cjs +1 -1
  610. package/dist/routes/llm-settings.cjs.map +1 -1
  611. package/dist/routes/llm-settings.js +131 -119
  612. package/dist/routes/llm-settings.js.map +1 -1
  613. package/dist/routes/usage-tab.cjs +2 -0
  614. package/dist/routes/usage-tab.cjs.map +1 -0
  615. package/dist/routes/usage-tab.d.ts +2 -0
  616. package/dist/routes/usage-tab.js +10 -0
  617. package/dist/routes/usage-tab.js.map +1 -0
  618. package/dist/services/child-conversation-launch.cjs +1 -1
  619. package/dist/services/child-conversation-launch.cjs.map +1 -1
  620. package/dist/services/child-conversation-launch.js +11 -5
  621. package/dist/services/child-conversation-launch.js.map +1 -1
  622. package/dist/stores/archived-conversations-store.cjs +2 -0
  623. package/dist/stores/archived-conversations-store.cjs.map +1 -0
  624. package/dist/stores/archived-conversations-store.d.ts +24 -0
  625. package/dist/stores/archived-conversations-store.js +33 -0
  626. package/dist/stores/archived-conversations-store.js.map +1 -0
  627. package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
  628. package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
  629. package/dist/stores/conversation-panel-preferences-store.d.ts +3 -0
  630. package/dist/stores/conversation-panel-preferences-store.js +4 -0
  631. package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
  632. package/dist/stores/conversation-store.cjs.map +1 -1
  633. package/dist/stores/conversation-store.d.ts +1 -1
  634. package/dist/stores/conversation-store.js.map +1 -1
  635. package/dist/stores/metrics-store.cjs +1 -1
  636. package/dist/stores/metrics-store.cjs.map +1 -1
  637. package/dist/stores/metrics-store.d.ts +1 -0
  638. package/dist/stores/metrics-store.js +7 -4
  639. package/dist/stores/metrics-store.js.map +1 -1
  640. package/dist/types/agent-server/core/events/condensation-event.d.ts +3 -0
  641. package/dist/types/agent-server/core/events/conversation-state-event.d.ts +4 -6
  642. package/dist/types/agent-server/type-guards.cjs +1 -1
  643. package/dist/types/agent-server/type-guards.cjs.map +1 -1
  644. package/dist/types/agent-server/type-guards.d.ts +5 -0
  645. package/dist/types/agent-server/type-guards.js +3 -3
  646. package/dist/types/agent-server/type-guards.js.map +1 -1
  647. package/dist/utils/conversation-local-storage.cjs +1 -1
  648. package/dist/utils/conversation-local-storage.cjs.map +1 -1
  649. package/dist/utils/conversation-local-storage.js +2 -1
  650. package/dist/utils/conversation-local-storage.js.map +1 -1
  651. package/dist/utils/format-model-name.cjs +1 -1
  652. package/dist/utils/format-model-name.cjs.map +1 -1
  653. package/dist/utils/format-model-name.d.ts +11 -0
  654. package/dist/utils/format-model-name.js +11 -3
  655. package/dist/utils/format-model-name.js.map +1 -1
  656. package/dist/utils/format-token-count.cjs +2 -0
  657. package/dist/utils/format-token-count.cjs.map +1 -0
  658. package/dist/utils/format-token-count.d.ts +3 -0
  659. package/dist/utils/format-token-count.js +19 -0
  660. package/dist/utils/format-token-count.js.map +1 -0
  661. package/dist/utils/hover-reveal-classes.cjs +2 -0
  662. package/dist/utils/hover-reveal-classes.cjs.map +1 -0
  663. package/dist/utils/hover-reveal-classes.d.ts +34 -0
  664. package/dist/utils/hover-reveal-classes.js +41 -0
  665. package/dist/utils/hover-reveal-classes.js.map +1 -0
  666. package/dist/utils/is-markdown-file-path.cjs +2 -0
  667. package/dist/utils/is-markdown-file-path.cjs.map +1 -0
  668. package/dist/utils/is-markdown-file-path.d.ts +4 -0
  669. package/dist/utils/is-markdown-file-path.js +14 -0
  670. package/dist/utils/is-markdown-file-path.js.map +1 -0
  671. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  672. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  673. package/dist/utils/mcp-marketplace-utils.d.ts +7 -0
  674. package/dist/utils/mcp-marketplace-utils.js +19 -16
  675. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  676. package/dist/utils/transcript-export/index.cjs +3 -3
  677. package/dist/utils/transcript-export/index.cjs.map +1 -1
  678. package/dist/utils/transcript-export/index.js +12 -12
  679. package/dist/utils/transcript-export/index.js.map +1 -1
  680. package/dist/utils/transcript-export/load-complete-events.cjs +1 -1
  681. package/dist/utils/transcript-export/load-complete-events.cjs.map +1 -1
  682. package/dist/utils/transcript-export/load-complete-events.js +4 -4
  683. package/dist/utils/transcript-export/load-complete-events.js.map +1 -1
  684. package/dist/utils/workspace-relative-path.cjs +2 -0
  685. package/dist/utils/workspace-relative-path.cjs.map +1 -0
  686. package/dist/utils/workspace-relative-path.d.ts +10 -0
  687. package/dist/utils/workspace-relative-path.js +22 -0
  688. package/dist/utils/workspace-relative-path.js.map +1 -0
  689. package/package.json +17 -16
  690. package/scripts/check-sdk-version-sync.mjs +6 -6
  691. package/scripts/dev-process-utils.mjs +6 -1
  692. package/scripts/dev-safe.mjs +57 -2
  693. package/build/assets/active-backend-context-BBvP-bI2.js +0 -2
  694. package/build/assets/agent-profiles-settings-DqoS7RL3.js +0 -1
  695. package/build/assets/agent-server-conversation-service.api-0RztSdJJ.js +0 -86
  696. package/build/assets/agent-settings-BJHryp65.js +0 -1
  697. package/build/assets/app-settings-r1RUbgGi.js +0 -1
  698. package/build/assets/backend-form-modal-OroTqnAv.js +0 -1
  699. package/build/assets/bot-DuN83i3p.js +0 -1
  700. package/build/assets/browser--eoQOo4M.js +0 -6
  701. package/build/assets/check-DLOKoFU9.js +0 -1
  702. package/build/assets/chevron-down-CnIbtM6i.js +0 -1
  703. package/build/assets/circle-check-CxCYJrpZ.js +0 -1
  704. package/build/assets/conversation-BUzBZAc-.js +0 -90
  705. package/build/assets/conversation-panel-pzAKsSDb.js +0 -1
  706. package/build/assets/conversation-pzAKsSDb.js +0 -1
  707. package/build/assets/conversation-store-B8Eahyeg.js +0 -6
  708. package/build/assets/external-link-zWbg5KXS.js +0 -1
  709. package/build/assets/files-tab-BzofBmF4.js +0 -1
  710. package/build/assets/git-provider-icon-BLMlwy02.js +0 -1
  711. package/build/assets/git-repo-dropdown-B_PTq4ol.js +0 -1
  712. package/build/assets/home-r89Vj2td.js +0 -1
  713. package/build/assets/index-home-r89Vj2td.js +0 -1
  714. package/build/assets/llm-not-configured-banner-CwKu8C6g.js +0 -27
  715. package/build/assets/llm-settings-BJotLQND.js +0 -1
  716. package/build/assets/llm-settings-CDM-5H2A.js +0 -1
  717. package/build/assets/llm-subscription-service-7eVfDIE1.js +0 -1
  718. package/build/assets/loader-circle-C5AnEN8r.js +0 -1
  719. package/build/assets/manifest-2a36a8f8.js +0 -1
  720. package/build/assets/markdown-renderer-BiiiIznl.js +0 -8
  721. package/build/assets/mcp-marketplace-utils-Dr_HG_ol.js +0 -1
  722. package/build/assets/messages-D1cbVhRK.js +0 -40
  723. package/build/assets/pencil-Bl3GWhgq.js +0 -1
  724. package/build/assets/plus-CtzlyAoF.js +0 -1
  725. package/build/assets/recommended-automations-launcher-BbJAJVED.js +0 -1
  726. package/build/assets/refresh-cw-B9GhC3QC.js +0 -1
  727. package/build/assets/root-BfM_kC_d.css +0 -1
  728. package/build/assets/root-gstBEjty.js +0 -2
  729. package/build/assets/root-layout-BIAFLP9p.js +0 -2
  730. package/build/assets/search-CYaiKHRp.js +0 -1
  731. package/build/assets/shared-conversation-DIxZjElr.js +0 -1
  732. package/build/assets/shield-check-D2cpEpv3.js +0 -1
  733. package/build/assets/trash-2-CzDVN-Iw.js +0 -1
  734. package/build/assets/use-create-conversation-DWaSVUOB.js +0 -1
  735. package/build/assets/use-is-authed-Bka89Xmm.js +0 -1
  736. package/build/assets/use-llm-profiles-CU8ah1pz.js +0 -1
  737. package/build/assets/use-save-agent-profile-DgI_Uz9a.js +0 -1
  738. package/build/assets/utils-DPQfkd-7.js +0 -1
  739. package/build/assets/vendor~index-home~home~conversation-panel~conversation-DiXf0dkj.js +0 -13
  740. package/build/assets/x-RYDBg-e2.js +0 -1
  741. package/build/assets/zap-tbKDzCe3.js +0 -1
  742. package/dist/components/features/conversation/metrics-modal/context-window-section.cjs +0 -2
  743. package/dist/components/features/conversation/metrics-modal/context-window-section.cjs.map +0 -1
  744. package/dist/components/features/conversation/metrics-modal/context-window-section.d.ts +0 -6
  745. package/dist/components/features/conversation/metrics-modal/context-window-section.js +0 -46
  746. package/dist/components/features/conversation/metrics-modal/context-window-section.js.map +0 -1
  747. package/dist/components/features/conversation/metrics-modal/empty-state.cjs +0 -2
  748. package/dist/components/features/conversation/metrics-modal/empty-state.cjs.map +0 -1
  749. package/dist/components/features/conversation/metrics-modal/empty-state.d.ts +0 -1
  750. package/dist/components/features/conversation/metrics-modal/empty-state.js +0 -18
  751. package/dist/components/features/conversation/metrics-modal/empty-state.js.map +0 -1
  752. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.cjs +0 -2
  753. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.cjs.map +0 -1
  754. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.d.ts +0 -5
  755. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.js +0 -20
  756. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.js.map +0 -1
  757. package/dist/components/features/conversation/metrics-modal/metrics-modal.cjs +0 -2
  758. package/dist/components/features/conversation/metrics-modal/metrics-modal.cjs.map +0 -1
  759. package/dist/components/features/conversation/metrics-modal/metrics-modal.d.ts +0 -6
  760. package/dist/components/features/conversation/metrics-modal/metrics-modal.js +0 -57
  761. package/dist/components/features/conversation/metrics-modal/metrics-modal.js.map +0 -1
  762. package/dist/icons/u-credit-card.cjs +0 -2
  763. package/dist/icons/u-credit-card.cjs.map +0 -1
  764. package/dist/icons/u-credit-card.js +0 -21
  765. package/dist/icons/u-credit-card.js.map +0 -1
  766. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~cy4jtuc2-rfV8mQ9m.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dh9v73sa-rfV8mQ9m.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"child-conversation-launch.cjs","names":[],"sources":["../../src/services/child-conversation-launch.ts"],"sourcesContent":["import i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport {\n compareAgentServerVersions,\n getCachedAgentServerVersion,\n} from \"#/api/agent-server-compatibility\";\nimport {\n createCloudAppConversation,\n getCloudAppConversationStartTask,\n pickCloudBackendForLaunch,\n} from \"#/api/cloud/conversation-service.api\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport {\n getStoredConversationMetadata,\n type ConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport type { AppConversationStartTask } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n CHILD_CONVERSATION_ISOLATIONS,\n CHILD_CONVERSATION_RESULT_PREFIX,\n CHILD_CONVERSATION_TARGETS,\n LAUNCH_CHILD_CONVERSATION_TOOL_NAME,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n type ChildConversationIsolation,\n type ChildConversationTarget,\n} from \"#/constants/child-conversation\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport type { LaunchChildConversationAction } from \"#/types/agent-server/core\";\nimport { buildAgentCanvasPath } from \"#/utils/base-path\";\nimport {\n displayErrorToast,\n displaySuccessToastWithLink,\n} from \"#/utils/custom-toast-handlers\";\n\n/** Cadence and ceiling for waiting on a Cloud sandbox to expose its id. */\nconst CLOUD_START_POLL_INTERVAL_MS = 3_000;\nconst CLOUD_START_POLL_TIMEOUT_MS = 180_000;\n\nconst LEDGER_STORAGE_KEY_PREFIX = \"openhands-child-conversation-launches:\";\n\ninterface LaunchSuccess {\n status: \"launched\";\n target: ChildConversationTarget;\n conversation_id: string | null;\n url: string | null;\n initial_status: string;\n title: string | null;\n /** Local only: the directory the child inherited from this conversation. */\n workspace?: string;\n /** Local only: which isolation mode was actually applied. */\n isolation?: ChildConversationIsolation;\n /**\n * Present when the applied isolation is not the one that was asked for,\n * explaining why and what it means for the child.\n */\n isolation_note?: string;\n /** Cloud only: poll this if `conversation_id` is still null. */\n start_task_id?: string;\n /** Cloud only: which connected Cloud backend the child was launched on. */\n backend?: string;\n /** Whether the parent/child link was persisted server-side. */\n parent_link: boolean;\n /** Present when `parent_link` is false, explaining why. */\n parent_link_note?: string;\n}\n\ninterface LaunchFailure {\n status: \"error\";\n error: string;\n guidance: string;\n}\n\nexport type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;\n\ninterface ValidatedParams {\n target: ChildConversationTarget;\n task: string;\n title: string | null;\n repository: string | null;\n branch: string | null;\n isolation: ChildConversationIsolation;\n}\n\nconst failure = (error: string, guidance: string): LaunchFailure => ({\n status: \"error\",\n error,\n guidance,\n});\n\nconst quoted = (values: readonly string[]) =>\n values.map((value) => `\"${value}\"`).join(\" or \");\n\nconst blankToNull = (value: string | null | undefined) => {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n};\n\n/**\n * Validate what the agent-server cannot.\n *\n * The tool's JSON Schema is turned into a pydantic model with `extra=\"forbid\"`,\n * so unknown/misspelled parameter names, missing required parameters and wrong\n * types already fail server-side with a corrective message. `enum` is the gap:\n * the SDK advertises it to the LLM but drops it when building the model, so a\n * misspelled `target` reaches us intact and must be rejected here. The\n * cross-target rules (cloud-only / local-only parameters) are ours to enforce\n * too, since a schema cannot express them.\n */\nfunction validateLaunchParams(\n action: LaunchChildConversationAction,\n):\n | { ok: true; params: ValidatedParams }\n | { ok: false; failure: LaunchFailure } {\n const target = action.target as ChildConversationTarget;\n if (!CHILD_CONVERSATION_TARGETS.includes(target)) {\n return {\n ok: false,\n failure: failure(\n `Unknown target ${JSON.stringify(action.target)}.`,\n `\\`target\\` must be exactly ${quoted(CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`,\n ),\n };\n }\n\n const task = blankToNull(action.task);\n if (!task) {\n return {\n ok: false,\n failure: failure(\n \"`task` is empty.\",\n \"`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.\",\n ),\n };\n }\n\n const repository = blankToNull(action.repository);\n const branch = blankToNull(action.branch);\n const isolation = blankToNull(\n action.isolation,\n ) as ChildConversationIsolation | null;\n\n if (target === \"local\" && (repository || branch)) {\n return {\n ok: false,\n failure: failure(\n '`repository`/`branch` were passed with target=\"local\".',\n 'A local child always runs in this conversation\\'s workspace. Drop `repository` and `branch`, or use target=\"cloud\" to run against a repository in a Cloud sandbox.',\n ),\n };\n }\n\n if (target === \"cloud\" && isolation) {\n return {\n ok: false,\n failure: failure(\n '`isolation` was passed with target=\"cloud\".',\n 'Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target=\"local\" to choose between \"worktree\" and \"shared\".',\n ),\n };\n }\n\n if (branch && !repository) {\n return {\n ok: false,\n failure: failure(\n \"`branch` was passed without `repository`.\",\n 'Pass `repository` as \"owner/repo\" alongside `branch`, or drop `branch` to use the repository\\'s default branch.',\n ),\n };\n }\n\n if (isolation && !CHILD_CONVERSATION_ISOLATIONS.includes(isolation)) {\n return {\n ok: false,\n failure: failure(\n `Unknown isolation ${JSON.stringify(action.isolation)}.`,\n `\\`isolation\\` must be exactly ${quoted(CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`,\n ),\n };\n }\n\n return {\n ok: true,\n params: {\n target,\n task,\n title: blankToNull(action.title),\n repository,\n branch,\n isolation: isolation ?? \"worktree\",\n },\n };\n}\n\n/**\n * Remember which tool calls have already been acted on.\n *\n * Unlike the Canvas UI tool, a launch is not idempotent: replaying its\n * ActionEvent (a socket reconnect that falls back to `resend_mode: \"all\"`, or a\n * REST/WebSocket race after a reload) would start a second — on Cloud, billable\n * — conversation. Claim the tool call before any network work so a replay that\n * arrives mid-flight is dropped too.\n */\nfunction claimToolCall(parentConversationId: string, toolCallId: string) {\n const key = `${LEDGER_STORAGE_KEY_PREFIX}${parentConversationId}`;\n let handled: string[] = [];\n try {\n const raw = window.localStorage.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : null;\n if (Array.isArray(parsed)) {\n handled = parsed.filter((id): id is string => typeof id === \"string\");\n }\n } catch {\n // A corrupt ledger must not block the launch; start a fresh one.\n }\n\n if (handled.includes(toolCallId)) return false;\n\n try {\n window.localStorage.setItem(key, JSON.stringify([...handled, toolCallId]));\n } catch {\n // Storage full or unavailable — proceed, accepting replay risk over never\n // launching at all.\n }\n return true;\n}\n\nfunction absoluteCanvasUrl(path: string) {\n const canvasPath = buildAgentCanvasPath(path);\n if (typeof window === \"undefined\") return canvasPath;\n return new URL(canvasPath, window.location.origin).toString();\n}\n\n/**\n * `parent_conversation_id` on the local start request landed in agent-server\n * 1.37.1. Older servers ignore unknown fields, so the child is created without\n * the link — report that rather than letting the agent assume a relationship\n * that does not exist.\n */\nfunction localParentLinkNote(): string | null {\n const version = getCachedAgentServerVersion();\n if (!version) return null;\n const comparison = compareAgentServerVersions(\n version,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n );\n if (comparison === null || comparison >= 0) return null;\n return `Agent server ${version} does not persist parent/child conversation links (needs ${MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`;\n}\n\n/**\n * Whether the parent's workspace can host a git worktree.\n *\n * A conversation started without a repository or an attached workspace runs in\n * a scratch directory that is `git init`-ed but never committed to. `git\n * worktree add` cannot branch from an unborn HEAD, and the agent-server raises\n * the resulting error straight out of its start-conversation handler as a 500,\n * so asking for a worktree there fails the whole launch. Conversation metadata\n * is only persisted when a repository or an explicit workspace was chosen (see\n * `createConversation`), so its absence pinpoints exactly those scratch\n * workspaces. Anything else is attempted and falls back on failure, because the\n * frontend cannot inspect the workspace's git state directly.\n */\nconst parentSupportsWorktree = (\n metadata: ConversationMetadata | null,\n): boolean => !!(metadata?.selected_repository || metadata?.selected_workspace);\n\nconst SHARED_FALLBACK_CONSEQUENCE =\n \"The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.\";\n\nasync function launchLocalChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n // The agent-server rejects a parent whose workspace differs from the child's,\n // so the child requests the parent's own directory. `worktree` then carves an\n // isolated worktree out of it, which is what keeps siblings from colliding.\n const workspace =\n await AgentServerConversationService.resolveConversationWorkingDir(\n parentConversationId,\n );\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n\n const createChild = (isolation: ChildConversationIsolation) =>\n AgentServerConversationService.createConversation(\n params.task,\n undefined,\n undefined,\n parentMetadata\n ? {\n selected_repository: parentMetadata.selected_repository,\n selected_branch: parentMetadata.selected_branch,\n git_provider: parentMetadata.git_provider,\n }\n : null,\n workspace,\n isolation === \"shared\" ? \"local_repo\" : \"new_worktree\",\n parentConversationId,\n );\n\n let isolation = params.isolation;\n let isolationNote: string | null = null;\n\n if (isolation === \"worktree\" && !parentSupportsWorktree(parentMetadata)) {\n isolation = \"shared\";\n isolationNote = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n let startTask;\n try {\n startTask = await createChild(isolation);\n } catch (worktreeError) {\n if (isolation !== \"worktree\") throw worktreeError;\n // Creating the worktree is the one part of a local launch that can fail on\n // an otherwise healthy workspace, so retry without it rather than losing\n // the launch. Anything else fails both attempts and is reported as-is.\n try {\n startTask = await createChild(\"shared\");\n } catch {\n throw worktreeError;\n }\n isolation = \"shared\";\n isolationNote = `Creating a git worktree in this conversation's workspace failed (${worktreeError instanceof Error ? worktreeError.message : String(worktreeError)}). ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n const conversationId = startTask.app_conversation_id ?? startTask.id;\n\n if (params.title) {\n // Local start requests carry no title — only `autotitle`. Best effort: a\n // failed rename must not fail the launch.\n await AgentServerConversationService.updateConversationTitle(\n conversationId,\n params.title,\n ).catch(() => undefined);\n }\n\n const parentLinkNote = localParentLinkNote();\n return {\n status: \"launched\",\n target: \"local\",\n conversation_id: conversationId,\n url: absoluteCanvasUrl(`/conversations/${conversationId}`),\n initial_status: startTask.status,\n title: params.title,\n workspace,\n isolation,\n ...(isolationNote ? { isolation_note: isolationNote } : {}),\n parent_link: !parentLinkNote,\n ...(parentLinkNote ? { parent_link_note: parentLinkNote } : {}),\n };\n}\n\nconst delay = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Wait for the Cloud start task to expose its conversation id.\n *\n * The Cloud API provisions a sandbox asynchronously and only fills in\n * `app_conversation_id` once the task reaches READY, so without this the agent\n * would get a result with no conversation to open. Bounded: on timeout the\n * caller reports the still-provisioning task instead of hanging.\n */\nasync function waitForCloudConversationId(\n task: AppConversationStartTask,\n backend: Backend,\n): Promise<AppConversationStartTask> {\n const deadline = Date.now() + CLOUD_START_POLL_TIMEOUT_MS;\n let latest = task;\n while (!latest.app_conversation_id && latest.status !== \"ERROR\") {\n if (Date.now() >= deadline) break;\n\n await delay(CLOUD_START_POLL_INTERVAL_MS);\n\n const next = await getCloudAppConversationStartTask(\n latest.id,\n backend,\n ).catch(() => null);\n if (!next) break;\n latest = next;\n }\n return latest;\n}\n\nasync function launchCloudChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n const backend = pickCloudBackendForLaunch();\n if (!backend) {\n return failure(\n \"No OpenHands Cloud backend is connected in Agent Canvas.\",\n 'Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target=\"local\".',\n );\n }\n\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n const startTask = await createCloudAppConversation(\n {\n initial_message: {\n role: \"user\",\n content: [{ type: \"text\", text: params.task }],\n },\n title: params.title,\n // Fall back to this conversation's repository so a bare `target: \"cloud\"`\n // call still lands the child on the code the user is working on. The\n // provider only travels with the inherited repository — it may not apply\n // to one the agent named itself.\n selected_repository:\n params.repository ?? parentMetadata?.selected_repository ?? null,\n selected_branch: params.branch ?? null,\n git_provider: params.repository\n ? null\n : (parentMetadata?.git_provider ?? null),\n // The parent runs on the local agent-server, so its id means nothing to\n // Cloud. Sending it would only hide the child from the Cloud\n // conversation list, which filters out anything with a parent.\n parent_conversation_id: null,\n },\n backend,\n );\n\n const settled = await waitForCloudConversationId(startTask, backend);\n if (settled.status === \"ERROR\") {\n return failure(\n settled.detail || \"The Cloud conversation failed to start.\",\n 'The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target=\"local\".',\n );\n }\n\n const conversationId = settled.app_conversation_id;\n return {\n status: \"launched\",\n target: \"cloud\",\n conversation_id: conversationId,\n url: conversationId\n ? `${backend.host.replace(/\\/$/, \"\")}/conversations/${conversationId}`\n : null,\n initial_status: settled.status,\n title: params.title,\n start_task_id: settled.id,\n backend: backend.name,\n // Cloud children of a local parent carry no server-side link; see above.\n parent_link: false,\n parent_link_note:\n \"This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.\",\n };\n}\n\n/**\n * Hand the outcome back to the agent and the user.\n *\n * Client tools are acknowledged by the agent-server before the browser does any\n * work, so a message is the only way to give the agent the child's id or tell\n * it how to fix a malformed call. `sendMessage` runs the agent, which is what\n * makes it relay the result to the user in its next turn.\n */\nasync function reportLaunchResult(\n parentConversationId: string,\n result: LaunchChildConversationResult,\n) {\n if (result.status === \"error\") {\n displayErrorToast(\n i18n.t(I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED, {\n error: result.error,\n }),\n );\n } else if (result.url) {\n displaySuccessToastWithLink(\n i18n.t(\n result.target === \"cloud\"\n ? I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD\n : I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL,\n ),\n i18n.t(I18nKey.CHILD_CONVERSATION$OPEN),\n result.url,\n );\n }\n\n // The agent-server cancels an active `/goal` loop on any inbound message, so\n // a launch must not silently end one. The toast above still tells the user\n // what happened.\n const goalStatus =\n useGoalStore.getState().statusByConversation[parentConversationId];\n if (goalStatus?.active) return;\n\n await AgentServerConversationService.sendMessage(parentConversationId, {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: `${CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(result)}`,\n },\n ],\n });\n}\n\n/**\n * Execute a `launch_child_conversation` tool call.\n *\n * Never rejects: every failure is turned into corrective guidance for the\n * agent, because the agent-server has already told it the call succeeded.\n */\nexport async function handleLaunchChildConversationAction(\n action: LaunchChildConversationAction,\n parentConversationId: string,\n toolCallId: string,\n): Promise<void> {\n if (!claimToolCall(parentConversationId, toolCallId)) return;\n\n const validation = validateLaunchParams(action);\n let result: LaunchChildConversationResult;\n if (!validation.ok) {\n result = validation.failure;\n } else {\n try {\n result =\n validation.params.target === \"cloud\"\n ? await launchCloudChild(validation.params, parentConversationId)\n : await launchLocalChild(validation.params, parentConversationId);\n } catch (error) {\n result = failure(\n error instanceof Error ? error.message : String(error),\n \"The launch request failed. Report the error to the user; retry only if the cause looks transient.\",\n );\n }\n }\n\n await reportLaunchResult(parentConversationId, result).catch((error) => {\n console.warn(\n `[${LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,\n error,\n );\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"child-conversation-launch.cjs","names":[],"sources":["../../src/services/child-conversation-launch.ts"],"sourcesContent":["import i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport {\n compareAgentServerVersions,\n getCachedAgentServerVersion,\n} from \"#/api/agent-server-compatibility\";\nimport {\n createCloudAppConversation,\n getCloudAppConversationStartTask,\n pickCloudBackendForLaunch,\n} from \"#/api/cloud/conversation-service.api\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport {\n getStoredConversationMetadata,\n type ConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport type { AppConversationStartTask } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n CHILD_CONVERSATION_ISOLATIONS,\n CHILD_CONVERSATION_RESULT_PREFIX,\n CHILD_CONVERSATION_TARGETS,\n LAUNCH_CHILD_CONVERSATION_TOOL_NAME,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n type ChildConversationIsolation,\n type ChildConversationTarget,\n} from \"#/constants/child-conversation\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport type { LaunchChildConversationAction } from \"#/types/agent-server/core\";\nimport { buildAgentCanvasPath } from \"#/utils/base-path\";\nimport {\n displayErrorToast,\n displaySuccessToastWithLink,\n} from \"#/utils/custom-toast-handlers\";\n\n/** Cadence and ceiling for waiting on a Cloud sandbox to expose its id. */\nconst CLOUD_START_POLL_INTERVAL_MS = 3_000;\nconst CLOUD_START_POLL_TIMEOUT_MS = 180_000;\n\nconst LEDGER_STORAGE_KEY_PREFIX = \"openhands-child-conversation-launches:\";\n\ninterface LaunchSuccess {\n status: \"launched\";\n target: ChildConversationTarget;\n conversation_id: string | null;\n url: string | null;\n initial_status: string;\n title: string | null;\n /** Local only: the directory the child inherited from this conversation. */\n workspace?: string;\n /** Local only: which isolation mode was actually applied. */\n isolation?: ChildConversationIsolation;\n /**\n * Present when the applied isolation is not the one that was asked for,\n * explaining why and what it means for the child.\n */\n isolation_note?: string;\n /** Cloud only: poll this if `conversation_id` is still null. */\n start_task_id?: string;\n /** Cloud only: which connected Cloud backend the child was launched on. */\n backend?: string;\n /** Whether the parent/child link was persisted server-side. */\n parent_link: boolean;\n /** Present when `parent_link` is false, explaining why. */\n parent_link_note?: string;\n}\n\ninterface LaunchFailure {\n status: \"error\";\n error: string;\n guidance: string;\n}\n\nexport type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;\n\ninterface ValidatedParams {\n target: ChildConversationTarget;\n task: string;\n title: string | null;\n repository: string | null;\n branch: string | null;\n isolation: ChildConversationIsolation;\n}\n\nconst failure = (error: string, guidance: string): LaunchFailure => ({\n status: \"error\",\n error,\n guidance,\n});\n\nconst quoted = (values: readonly string[]) =>\n values.map((value) => `\"${value}\"`).join(\" or \");\n\nconst blankToNull = (value: string | null | undefined) => {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n};\n\n/**\n * Validate what the agent-server cannot.\n *\n * The tool's JSON Schema is turned into a pydantic model with `extra=\"forbid\"`,\n * so unknown/misspelled parameter names, missing required parameters and wrong\n * types already fail server-side with a corrective message. `enum` is the gap:\n * the SDK advertises it to the LLM but drops it when building the model, so a\n * misspelled `target` reaches us intact and must be rejected here. The\n * cross-target rules (cloud-only / local-only parameters) are ours to enforce\n * too, since a schema cannot express them.\n */\nfunction validateLaunchParams(\n action: LaunchChildConversationAction,\n):\n | { ok: true; params: ValidatedParams }\n | { ok: false; failure: LaunchFailure } {\n const target = action.target as ChildConversationTarget;\n if (!CHILD_CONVERSATION_TARGETS.includes(target)) {\n return {\n ok: false,\n failure: failure(\n `Unknown target ${JSON.stringify(action.target)}.`,\n `\\`target\\` must be exactly ${quoted(CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`,\n ),\n };\n }\n\n const task = blankToNull(action.task);\n if (!task) {\n return {\n ok: false,\n failure: failure(\n \"`task` is empty.\",\n \"`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.\",\n ),\n };\n }\n\n const repository = blankToNull(action.repository);\n const branch = blankToNull(action.branch);\n const isolation = blankToNull(\n action.isolation,\n ) as ChildConversationIsolation | null;\n\n if (target === \"local\" && (repository || branch)) {\n return {\n ok: false,\n failure: failure(\n '`repository`/`branch` were passed with target=\"local\".',\n 'A local child always runs in this conversation\\'s workspace. Drop `repository` and `branch`, or use target=\"cloud\" to run against a repository in a Cloud sandbox.',\n ),\n };\n }\n\n if (target === \"cloud\" && isolation) {\n return {\n ok: false,\n failure: failure(\n '`isolation` was passed with target=\"cloud\".',\n 'Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target=\"local\" to choose between \"worktree\" and \"shared\".',\n ),\n };\n }\n\n if (branch && !repository) {\n return {\n ok: false,\n failure: failure(\n \"`branch` was passed without `repository`.\",\n 'Pass `repository` as \"owner/repo\" alongside `branch`, or drop `branch` to use the repository\\'s default branch.',\n ),\n };\n }\n\n if (isolation && !CHILD_CONVERSATION_ISOLATIONS.includes(isolation)) {\n return {\n ok: false,\n failure: failure(\n `Unknown isolation ${JSON.stringify(action.isolation)}.`,\n `\\`isolation\\` must be exactly ${quoted(CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`,\n ),\n };\n }\n\n return {\n ok: true,\n params: {\n target,\n task,\n title: blankToNull(action.title),\n repository,\n branch,\n isolation: isolation ?? \"worktree\",\n },\n };\n}\n\n/**\n * Remember which tool calls have already been acted on.\n *\n * Unlike the Canvas UI tool, a launch is not idempotent: replaying its\n * ActionEvent (a socket reconnect that falls back to `resend_mode: \"all\"`, or a\n * REST/WebSocket race after a reload) would start a second — on Cloud, billable\n * — conversation. Claim the tool call before any network work so a replay that\n * arrives mid-flight is dropped too.\n */\nfunction claimToolCall(parentConversationId: string, toolCallId: string) {\n const key = `${LEDGER_STORAGE_KEY_PREFIX}${parentConversationId}`;\n let handled: string[] = [];\n try {\n const raw = window.localStorage.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : null;\n if (Array.isArray(parsed)) {\n handled = parsed.filter((id): id is string => typeof id === \"string\");\n }\n } catch {\n // A corrupt ledger must not block the launch; start a fresh one.\n }\n\n if (handled.includes(toolCallId)) return false;\n\n try {\n window.localStorage.setItem(key, JSON.stringify([...handled, toolCallId]));\n } catch {\n // Storage full or unavailable — proceed, accepting replay risk over never\n // launching at all.\n }\n return true;\n}\n\nfunction absoluteCanvasUrl(path: string) {\n const canvasPath = buildAgentCanvasPath(path);\n if (typeof window === \"undefined\") return canvasPath;\n return new URL(canvasPath, window.location.origin).toString();\n}\n\n/**\n * `parent_conversation_id` on the local start request landed in agent-server\n * 1.37.1. Older servers ignore unknown fields, so the child is created without\n * the link — report that rather than letting the agent assume a relationship\n * that does not exist.\n */\nfunction localParentLinkNote(): string | null {\n const version = getCachedAgentServerVersion();\n if (!version) return null;\n const comparison = compareAgentServerVersions(\n version,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n );\n if (comparison === null || comparison >= 0) return null;\n return `Agent server ${version} does not persist parent/child conversation links (needs ${MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`;\n}\n\n/**\n * Whether the parent's workspace can host a git worktree.\n *\n * A conversation started without a repository or an attached workspace runs in\n * a scratch directory that is `git init`-ed but never committed to. `git\n * worktree add` cannot branch from an unborn HEAD, and the agent-server raises\n * the resulting error straight out of its start-conversation handler as a 500,\n * so asking for a worktree there fails the whole launch. Conversation metadata\n * is only persisted when a repository or an explicit workspace was chosen (see\n * `createConversation`), so its absence pinpoints exactly those scratch\n * workspaces. Anything else is attempted and falls back on failure, because the\n * frontend cannot inspect the workspace's git state directly.\n */\nconst parentSupportsWorktree = (\n metadata: ConversationMetadata | null,\n): boolean => !!(metadata?.selected_repository || metadata?.selected_workspace);\n\nconst SHARED_FALLBACK_CONSEQUENCE =\n \"The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.\";\n\nasync function launchLocalChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n // The agent-server rejects a parent whose workspace differs from the child's,\n // so the child requests the parent's own directory. `worktree` then carves an\n // isolated worktree out of it, which is what keeps siblings from colliding.\n const workspace =\n await AgentServerConversationService.resolveConversationWorkingDir(\n parentConversationId,\n );\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n\n const createChild = (isolation: ChildConversationIsolation) =>\n AgentServerConversationService.createConversation({\n initialUserMsg: params.task,\n metadata: parentMetadata\n ? {\n selected_repository: parentMetadata.selected_repository,\n selected_branch: parentMetadata.selected_branch,\n git_provider: parentMetadata.git_provider,\n }\n : null,\n workingDirOverride: workspace,\n workspaceMode: isolation === \"shared\" ? \"local_repo\" : \"new_worktree\",\n parentConversationId,\n });\n\n let isolation = params.isolation;\n let isolationNote: string | null = null;\n\n if (isolation === \"worktree\" && !parentSupportsWorktree(parentMetadata)) {\n isolation = \"shared\";\n isolationNote = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n let startTask;\n try {\n startTask = await createChild(isolation);\n } catch (worktreeError) {\n if (isolation !== \"worktree\") throw worktreeError;\n // Creating the worktree is the one part of a local launch that can fail on\n // an otherwise healthy workspace, so retry without it rather than losing\n // the launch. Anything else fails both attempts and is reported as-is.\n try {\n startTask = await createChild(\"shared\");\n } catch {\n throw worktreeError;\n }\n isolation = \"shared\";\n isolationNote = `Creating a git worktree in this conversation's workspace failed (${worktreeError instanceof Error ? worktreeError.message : String(worktreeError)}). ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n const conversationId = startTask.app_conversation_id ?? startTask.id;\n\n if (params.title) {\n // Local start requests carry no title — only `autotitle`. Best effort: a\n // failed rename must not fail the launch.\n await AgentServerConversationService.updateConversationTitle(\n conversationId,\n params.title,\n ).catch(() => undefined);\n }\n\n const parentLinkNote = localParentLinkNote();\n return {\n status: \"launched\",\n target: \"local\",\n conversation_id: conversationId,\n url: absoluteCanvasUrl(`/conversations/${conversationId}`),\n initial_status: startTask.status,\n title: params.title,\n workspace,\n isolation,\n ...(isolationNote ? { isolation_note: isolationNote } : {}),\n parent_link: !parentLinkNote,\n ...(parentLinkNote ? { parent_link_note: parentLinkNote } : {}),\n };\n}\n\nconst delay = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Wait for the Cloud start task to expose its conversation id.\n *\n * The Cloud API provisions a sandbox asynchronously and only fills in\n * `app_conversation_id` once the task reaches READY, so without this the agent\n * would get a result with no conversation to open. Bounded: on timeout the\n * caller reports the still-provisioning task instead of hanging.\n */\nasync function waitForCloudConversationId(\n task: AppConversationStartTask,\n backend: Backend,\n): Promise<AppConversationStartTask> {\n const deadline = Date.now() + CLOUD_START_POLL_TIMEOUT_MS;\n let latest = task;\n while (!latest.app_conversation_id && latest.status !== \"ERROR\") {\n if (Date.now() >= deadline) break;\n\n await delay(CLOUD_START_POLL_INTERVAL_MS);\n\n const next = await getCloudAppConversationStartTask(\n latest.id,\n backend,\n ).catch(() => null);\n if (!next) break;\n latest = next;\n }\n return latest;\n}\n\nasync function launchCloudChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n const backend = pickCloudBackendForLaunch();\n if (!backend) {\n return failure(\n \"No OpenHands Cloud backend is connected in Agent Canvas.\",\n 'Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target=\"local\".',\n );\n }\n\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n const startTask = await createCloudAppConversation(\n {\n initial_message: {\n role: \"user\",\n content: [{ type: \"text\", text: params.task }],\n },\n title: params.title,\n // Fall back to this conversation's repository so a bare `target: \"cloud\"`\n // call still lands the child on the code the user is working on. The\n // provider only travels with the inherited repository — it may not apply\n // to one the agent named itself.\n selected_repository:\n params.repository ?? parentMetadata?.selected_repository ?? null,\n selected_branch: params.branch ?? null,\n git_provider: params.repository\n ? null\n : (parentMetadata?.git_provider ?? null),\n // The parent runs on the local agent-server, so its id means nothing to\n // Cloud. Sending it would only hide the child from the Cloud\n // conversation list, which filters out anything with a parent.\n parent_conversation_id: null,\n },\n backend,\n );\n\n const settled = await waitForCloudConversationId(startTask, backend);\n if (settled.status === \"ERROR\") {\n return failure(\n settled.detail || \"The Cloud conversation failed to start.\",\n 'The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target=\"local\".',\n );\n }\n\n const conversationId = settled.app_conversation_id;\n return {\n status: \"launched\",\n target: \"cloud\",\n conversation_id: conversationId,\n url: conversationId\n ? `${backend.host.replace(/\\/$/, \"\")}/conversations/${conversationId}`\n : null,\n initial_status: settled.status,\n title: params.title,\n start_task_id: settled.id,\n backend: backend.name,\n // Cloud children of a local parent carry no server-side link; see above.\n parent_link: false,\n parent_link_note:\n \"This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.\",\n };\n}\n\n/**\n * Hand the outcome back to the agent and the user.\n *\n * Client tools are acknowledged by the agent-server before the browser does any\n * work, so a message is the only way to give the agent the child's id or tell\n * it how to fix a malformed call. `sendMessage` runs the agent, which is what\n * makes it relay the result to the user in its next turn.\n */\nasync function reportLaunchResult(\n parentConversationId: string,\n result: LaunchChildConversationResult,\n) {\n if (result.status === \"error\") {\n displayErrorToast(\n i18n.t(I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED, {\n error: result.error,\n }),\n );\n } else if (result.url) {\n displaySuccessToastWithLink(\n i18n.t(\n result.target === \"cloud\"\n ? I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD\n : I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL,\n ),\n i18n.t(I18nKey.CHILD_CONVERSATION$OPEN),\n result.url,\n );\n }\n\n // The agent-server cancels an active `/goal` loop on any inbound message, so\n // a launch must not silently end one. The toast above still tells the user\n // what happened.\n const goalStatus =\n useGoalStore.getState().statusByConversation[parentConversationId];\n if (goalStatus?.active) return;\n\n await AgentServerConversationService.sendMessage(parentConversationId, {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: `${CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(result)}`,\n },\n ],\n });\n}\n\n/**\n * Execute a `launch_child_conversation` tool call.\n *\n * Never rejects: every failure is turned into corrective guidance for the\n * agent, because the agent-server has already told it the call succeeded.\n */\nexport async function handleLaunchChildConversationAction(\n action: LaunchChildConversationAction,\n parentConversationId: string,\n toolCallId: string,\n): Promise<void> {\n if (!claimToolCall(parentConversationId, toolCallId)) return;\n\n const validation = validateLaunchParams(action);\n let result: LaunchChildConversationResult;\n if (!validation.ok) {\n result = validation.failure;\n } else {\n try {\n result =\n validation.params.target === \"cloud\"\n ? await launchCloudChild(validation.params, parentConversationId)\n : await launchLocalChild(validation.params, parentConversationId);\n } catch (error) {\n result = failure(\n error instanceof Error ? error.message : String(error),\n \"The launch request failed. Report the error to the user; retry only if the cause looks transient.\",\n );\n }\n }\n\n await reportLaunchResult(parentConversationId, result).catch((error) => {\n console.warn(\n `[${LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,\n error,\n );\n });\n}\n"],"mappings":"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"}
@@ -74,11 +74,17 @@ function D() {
74
74
  }
75
75
  var O = (e) => !!(e?.selected_repository || e?.selected_workspace), k = "The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.";
76
76
  async function A(e, t) {
77
- let n = await _.resolveConversationWorkingDir(t), r = p(t), i = (i) => _.createConversation(e.task, void 0, void 0, r ? {
78
- selected_repository: r.selected_repository,
79
- selected_branch: r.selected_branch,
80
- git_provider: r.git_provider
81
- } : null, n, i === "shared" ? "local_repo" : "new_worktree", t), a = e.isolation, o = null;
77
+ let n = await _.resolveConversationWorkingDir(t), r = p(t), i = (i) => _.createConversation({
78
+ initialUserMsg: e.task,
79
+ metadata: r ? {
80
+ selected_repository: r.selected_repository,
81
+ selected_branch: r.selected_branch,
82
+ git_provider: r.git_provider
83
+ } : null,
84
+ workingDirOverride: n,
85
+ workspaceMode: i === "shared" ? "local_repo" : "new_worktree",
86
+ parentConversationId: t
87
+ }), a = e.isolation, o = null;
82
88
  a === "worktree" && !O(r) && (a = "shared", o = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${k}`);
83
89
  let s;
84
90
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"child-conversation-launch.js","names":[],"sources":["../../src/services/child-conversation-launch.ts"],"sourcesContent":["import i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport {\n compareAgentServerVersions,\n getCachedAgentServerVersion,\n} from \"#/api/agent-server-compatibility\";\nimport {\n createCloudAppConversation,\n getCloudAppConversationStartTask,\n pickCloudBackendForLaunch,\n} from \"#/api/cloud/conversation-service.api\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport {\n getStoredConversationMetadata,\n type ConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport type { AppConversationStartTask } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n CHILD_CONVERSATION_ISOLATIONS,\n CHILD_CONVERSATION_RESULT_PREFIX,\n CHILD_CONVERSATION_TARGETS,\n LAUNCH_CHILD_CONVERSATION_TOOL_NAME,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n type ChildConversationIsolation,\n type ChildConversationTarget,\n} from \"#/constants/child-conversation\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport type { LaunchChildConversationAction } from \"#/types/agent-server/core\";\nimport { buildAgentCanvasPath } from \"#/utils/base-path\";\nimport {\n displayErrorToast,\n displaySuccessToastWithLink,\n} from \"#/utils/custom-toast-handlers\";\n\n/** Cadence and ceiling for waiting on a Cloud sandbox to expose its id. */\nconst CLOUD_START_POLL_INTERVAL_MS = 3_000;\nconst CLOUD_START_POLL_TIMEOUT_MS = 180_000;\n\nconst LEDGER_STORAGE_KEY_PREFIX = \"openhands-child-conversation-launches:\";\n\ninterface LaunchSuccess {\n status: \"launched\";\n target: ChildConversationTarget;\n conversation_id: string | null;\n url: string | null;\n initial_status: string;\n title: string | null;\n /** Local only: the directory the child inherited from this conversation. */\n workspace?: string;\n /** Local only: which isolation mode was actually applied. */\n isolation?: ChildConversationIsolation;\n /**\n * Present when the applied isolation is not the one that was asked for,\n * explaining why and what it means for the child.\n */\n isolation_note?: string;\n /** Cloud only: poll this if `conversation_id` is still null. */\n start_task_id?: string;\n /** Cloud only: which connected Cloud backend the child was launched on. */\n backend?: string;\n /** Whether the parent/child link was persisted server-side. */\n parent_link: boolean;\n /** Present when `parent_link` is false, explaining why. */\n parent_link_note?: string;\n}\n\ninterface LaunchFailure {\n status: \"error\";\n error: string;\n guidance: string;\n}\n\nexport type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;\n\ninterface ValidatedParams {\n target: ChildConversationTarget;\n task: string;\n title: string | null;\n repository: string | null;\n branch: string | null;\n isolation: ChildConversationIsolation;\n}\n\nconst failure = (error: string, guidance: string): LaunchFailure => ({\n status: \"error\",\n error,\n guidance,\n});\n\nconst quoted = (values: readonly string[]) =>\n values.map((value) => `\"${value}\"`).join(\" or \");\n\nconst blankToNull = (value: string | null | undefined) => {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n};\n\n/**\n * Validate what the agent-server cannot.\n *\n * The tool's JSON Schema is turned into a pydantic model with `extra=\"forbid\"`,\n * so unknown/misspelled parameter names, missing required parameters and wrong\n * types already fail server-side with a corrective message. `enum` is the gap:\n * the SDK advertises it to the LLM but drops it when building the model, so a\n * misspelled `target` reaches us intact and must be rejected here. The\n * cross-target rules (cloud-only / local-only parameters) are ours to enforce\n * too, since a schema cannot express them.\n */\nfunction validateLaunchParams(\n action: LaunchChildConversationAction,\n):\n | { ok: true; params: ValidatedParams }\n | { ok: false; failure: LaunchFailure } {\n const target = action.target as ChildConversationTarget;\n if (!CHILD_CONVERSATION_TARGETS.includes(target)) {\n return {\n ok: false,\n failure: failure(\n `Unknown target ${JSON.stringify(action.target)}.`,\n `\\`target\\` must be exactly ${quoted(CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`,\n ),\n };\n }\n\n const task = blankToNull(action.task);\n if (!task) {\n return {\n ok: false,\n failure: failure(\n \"`task` is empty.\",\n \"`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.\",\n ),\n };\n }\n\n const repository = blankToNull(action.repository);\n const branch = blankToNull(action.branch);\n const isolation = blankToNull(\n action.isolation,\n ) as ChildConversationIsolation | null;\n\n if (target === \"local\" && (repository || branch)) {\n return {\n ok: false,\n failure: failure(\n '`repository`/`branch` were passed with target=\"local\".',\n 'A local child always runs in this conversation\\'s workspace. Drop `repository` and `branch`, or use target=\"cloud\" to run against a repository in a Cloud sandbox.',\n ),\n };\n }\n\n if (target === \"cloud\" && isolation) {\n return {\n ok: false,\n failure: failure(\n '`isolation` was passed with target=\"cloud\".',\n 'Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target=\"local\" to choose between \"worktree\" and \"shared\".',\n ),\n };\n }\n\n if (branch && !repository) {\n return {\n ok: false,\n failure: failure(\n \"`branch` was passed without `repository`.\",\n 'Pass `repository` as \"owner/repo\" alongside `branch`, or drop `branch` to use the repository\\'s default branch.',\n ),\n };\n }\n\n if (isolation && !CHILD_CONVERSATION_ISOLATIONS.includes(isolation)) {\n return {\n ok: false,\n failure: failure(\n `Unknown isolation ${JSON.stringify(action.isolation)}.`,\n `\\`isolation\\` must be exactly ${quoted(CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`,\n ),\n };\n }\n\n return {\n ok: true,\n params: {\n target,\n task,\n title: blankToNull(action.title),\n repository,\n branch,\n isolation: isolation ?? \"worktree\",\n },\n };\n}\n\n/**\n * Remember which tool calls have already been acted on.\n *\n * Unlike the Canvas UI tool, a launch is not idempotent: replaying its\n * ActionEvent (a socket reconnect that falls back to `resend_mode: \"all\"`, or a\n * REST/WebSocket race after a reload) would start a second — on Cloud, billable\n * — conversation. Claim the tool call before any network work so a replay that\n * arrives mid-flight is dropped too.\n */\nfunction claimToolCall(parentConversationId: string, toolCallId: string) {\n const key = `${LEDGER_STORAGE_KEY_PREFIX}${parentConversationId}`;\n let handled: string[] = [];\n try {\n const raw = window.localStorage.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : null;\n if (Array.isArray(parsed)) {\n handled = parsed.filter((id): id is string => typeof id === \"string\");\n }\n } catch {\n // A corrupt ledger must not block the launch; start a fresh one.\n }\n\n if (handled.includes(toolCallId)) return false;\n\n try {\n window.localStorage.setItem(key, JSON.stringify([...handled, toolCallId]));\n } catch {\n // Storage full or unavailable — proceed, accepting replay risk over never\n // launching at all.\n }\n return true;\n}\n\nfunction absoluteCanvasUrl(path: string) {\n const canvasPath = buildAgentCanvasPath(path);\n if (typeof window === \"undefined\") return canvasPath;\n return new URL(canvasPath, window.location.origin).toString();\n}\n\n/**\n * `parent_conversation_id` on the local start request landed in agent-server\n * 1.37.1. Older servers ignore unknown fields, so the child is created without\n * the link — report that rather than letting the agent assume a relationship\n * that does not exist.\n */\nfunction localParentLinkNote(): string | null {\n const version = getCachedAgentServerVersion();\n if (!version) return null;\n const comparison = compareAgentServerVersions(\n version,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n );\n if (comparison === null || comparison >= 0) return null;\n return `Agent server ${version} does not persist parent/child conversation links (needs ${MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`;\n}\n\n/**\n * Whether the parent's workspace can host a git worktree.\n *\n * A conversation started without a repository or an attached workspace runs in\n * a scratch directory that is `git init`-ed but never committed to. `git\n * worktree add` cannot branch from an unborn HEAD, and the agent-server raises\n * the resulting error straight out of its start-conversation handler as a 500,\n * so asking for a worktree there fails the whole launch. Conversation metadata\n * is only persisted when a repository or an explicit workspace was chosen (see\n * `createConversation`), so its absence pinpoints exactly those scratch\n * workspaces. Anything else is attempted and falls back on failure, because the\n * frontend cannot inspect the workspace's git state directly.\n */\nconst parentSupportsWorktree = (\n metadata: ConversationMetadata | null,\n): boolean => !!(metadata?.selected_repository || metadata?.selected_workspace);\n\nconst SHARED_FALLBACK_CONSEQUENCE =\n \"The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.\";\n\nasync function launchLocalChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n // The agent-server rejects a parent whose workspace differs from the child's,\n // so the child requests the parent's own directory. `worktree` then carves an\n // isolated worktree out of it, which is what keeps siblings from colliding.\n const workspace =\n await AgentServerConversationService.resolveConversationWorkingDir(\n parentConversationId,\n );\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n\n const createChild = (isolation: ChildConversationIsolation) =>\n AgentServerConversationService.createConversation(\n params.task,\n undefined,\n undefined,\n parentMetadata\n ? {\n selected_repository: parentMetadata.selected_repository,\n selected_branch: parentMetadata.selected_branch,\n git_provider: parentMetadata.git_provider,\n }\n : null,\n workspace,\n isolation === \"shared\" ? \"local_repo\" : \"new_worktree\",\n parentConversationId,\n );\n\n let isolation = params.isolation;\n let isolationNote: string | null = null;\n\n if (isolation === \"worktree\" && !parentSupportsWorktree(parentMetadata)) {\n isolation = \"shared\";\n isolationNote = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n let startTask;\n try {\n startTask = await createChild(isolation);\n } catch (worktreeError) {\n if (isolation !== \"worktree\") throw worktreeError;\n // Creating the worktree is the one part of a local launch that can fail on\n // an otherwise healthy workspace, so retry without it rather than losing\n // the launch. Anything else fails both attempts and is reported as-is.\n try {\n startTask = await createChild(\"shared\");\n } catch {\n throw worktreeError;\n }\n isolation = \"shared\";\n isolationNote = `Creating a git worktree in this conversation's workspace failed (${worktreeError instanceof Error ? worktreeError.message : String(worktreeError)}). ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n const conversationId = startTask.app_conversation_id ?? startTask.id;\n\n if (params.title) {\n // Local start requests carry no title — only `autotitle`. Best effort: a\n // failed rename must not fail the launch.\n await AgentServerConversationService.updateConversationTitle(\n conversationId,\n params.title,\n ).catch(() => undefined);\n }\n\n const parentLinkNote = localParentLinkNote();\n return {\n status: \"launched\",\n target: \"local\",\n conversation_id: conversationId,\n url: absoluteCanvasUrl(`/conversations/${conversationId}`),\n initial_status: startTask.status,\n title: params.title,\n workspace,\n isolation,\n ...(isolationNote ? { isolation_note: isolationNote } : {}),\n parent_link: !parentLinkNote,\n ...(parentLinkNote ? { parent_link_note: parentLinkNote } : {}),\n };\n}\n\nconst delay = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Wait for the Cloud start task to expose its conversation id.\n *\n * The Cloud API provisions a sandbox asynchronously and only fills in\n * `app_conversation_id` once the task reaches READY, so without this the agent\n * would get a result with no conversation to open. Bounded: on timeout the\n * caller reports the still-provisioning task instead of hanging.\n */\nasync function waitForCloudConversationId(\n task: AppConversationStartTask,\n backend: Backend,\n): Promise<AppConversationStartTask> {\n const deadline = Date.now() + CLOUD_START_POLL_TIMEOUT_MS;\n let latest = task;\n while (!latest.app_conversation_id && latest.status !== \"ERROR\") {\n if (Date.now() >= deadline) break;\n\n await delay(CLOUD_START_POLL_INTERVAL_MS);\n\n const next = await getCloudAppConversationStartTask(\n latest.id,\n backend,\n ).catch(() => null);\n if (!next) break;\n latest = next;\n }\n return latest;\n}\n\nasync function launchCloudChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n const backend = pickCloudBackendForLaunch();\n if (!backend) {\n return failure(\n \"No OpenHands Cloud backend is connected in Agent Canvas.\",\n 'Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target=\"local\".',\n );\n }\n\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n const startTask = await createCloudAppConversation(\n {\n initial_message: {\n role: \"user\",\n content: [{ type: \"text\", text: params.task }],\n },\n title: params.title,\n // Fall back to this conversation's repository so a bare `target: \"cloud\"`\n // call still lands the child on the code the user is working on. The\n // provider only travels with the inherited repository — it may not apply\n // to one the agent named itself.\n selected_repository:\n params.repository ?? parentMetadata?.selected_repository ?? null,\n selected_branch: params.branch ?? null,\n git_provider: params.repository\n ? null\n : (parentMetadata?.git_provider ?? null),\n // The parent runs on the local agent-server, so its id means nothing to\n // Cloud. Sending it would only hide the child from the Cloud\n // conversation list, which filters out anything with a parent.\n parent_conversation_id: null,\n },\n backend,\n );\n\n const settled = await waitForCloudConversationId(startTask, backend);\n if (settled.status === \"ERROR\") {\n return failure(\n settled.detail || \"The Cloud conversation failed to start.\",\n 'The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target=\"local\".',\n );\n }\n\n const conversationId = settled.app_conversation_id;\n return {\n status: \"launched\",\n target: \"cloud\",\n conversation_id: conversationId,\n url: conversationId\n ? `${backend.host.replace(/\\/$/, \"\")}/conversations/${conversationId}`\n : null,\n initial_status: settled.status,\n title: params.title,\n start_task_id: settled.id,\n backend: backend.name,\n // Cloud children of a local parent carry no server-side link; see above.\n parent_link: false,\n parent_link_note:\n \"This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.\",\n };\n}\n\n/**\n * Hand the outcome back to the agent and the user.\n *\n * Client tools are acknowledged by the agent-server before the browser does any\n * work, so a message is the only way to give the agent the child's id or tell\n * it how to fix a malformed call. `sendMessage` runs the agent, which is what\n * makes it relay the result to the user in its next turn.\n */\nasync function reportLaunchResult(\n parentConversationId: string,\n result: LaunchChildConversationResult,\n) {\n if (result.status === \"error\") {\n displayErrorToast(\n i18n.t(I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED, {\n error: result.error,\n }),\n );\n } else if (result.url) {\n displaySuccessToastWithLink(\n i18n.t(\n result.target === \"cloud\"\n ? I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD\n : I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL,\n ),\n i18n.t(I18nKey.CHILD_CONVERSATION$OPEN),\n result.url,\n );\n }\n\n // The agent-server cancels an active `/goal` loop on any inbound message, so\n // a launch must not silently end one. The toast above still tells the user\n // what happened.\n const goalStatus =\n useGoalStore.getState().statusByConversation[parentConversationId];\n if (goalStatus?.active) return;\n\n await AgentServerConversationService.sendMessage(parentConversationId, {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: `${CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(result)}`,\n },\n ],\n });\n}\n\n/**\n * Execute a `launch_child_conversation` tool call.\n *\n * Never rejects: every failure is turned into corrective guidance for the\n * agent, because the agent-server has already told it the call succeeded.\n */\nexport async function handleLaunchChildConversationAction(\n action: LaunchChildConversationAction,\n parentConversationId: string,\n toolCallId: string,\n): Promise<void> {\n if (!claimToolCall(parentConversationId, toolCallId)) return;\n\n const validation = validateLaunchParams(action);\n let result: LaunchChildConversationResult;\n if (!validation.ok) {\n result = validation.failure;\n } else {\n try {\n result =\n validation.params.target === \"cloud\"\n ? await launchCloudChild(validation.params, parentConversationId)\n : await launchLocalChild(validation.params, parentConversationId);\n } catch (error) {\n result = failure(\n error instanceof Error ? error.message : String(error),\n \"The launch request failed. Report the error to the user; retry only if the cause looks transient.\",\n );\n }\n }\n\n await reportLaunchResult(parentConversationId, result).catch((error) => {\n console.warn(\n `[${LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,\n error,\n );\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"child-conversation-launch.js","names":[],"sources":["../../src/services/child-conversation-launch.ts"],"sourcesContent":["import i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport {\n compareAgentServerVersions,\n getCachedAgentServerVersion,\n} from \"#/api/agent-server-compatibility\";\nimport {\n createCloudAppConversation,\n getCloudAppConversationStartTask,\n pickCloudBackendForLaunch,\n} from \"#/api/cloud/conversation-service.api\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport {\n getStoredConversationMetadata,\n type ConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport type { AppConversationStartTask } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n CHILD_CONVERSATION_ISOLATIONS,\n CHILD_CONVERSATION_RESULT_PREFIX,\n CHILD_CONVERSATION_TARGETS,\n LAUNCH_CHILD_CONVERSATION_TOOL_NAME,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n type ChildConversationIsolation,\n type ChildConversationTarget,\n} from \"#/constants/child-conversation\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport type { LaunchChildConversationAction } from \"#/types/agent-server/core\";\nimport { buildAgentCanvasPath } from \"#/utils/base-path\";\nimport {\n displayErrorToast,\n displaySuccessToastWithLink,\n} from \"#/utils/custom-toast-handlers\";\n\n/** Cadence and ceiling for waiting on a Cloud sandbox to expose its id. */\nconst CLOUD_START_POLL_INTERVAL_MS = 3_000;\nconst CLOUD_START_POLL_TIMEOUT_MS = 180_000;\n\nconst LEDGER_STORAGE_KEY_PREFIX = \"openhands-child-conversation-launches:\";\n\ninterface LaunchSuccess {\n status: \"launched\";\n target: ChildConversationTarget;\n conversation_id: string | null;\n url: string | null;\n initial_status: string;\n title: string | null;\n /** Local only: the directory the child inherited from this conversation. */\n workspace?: string;\n /** Local only: which isolation mode was actually applied. */\n isolation?: ChildConversationIsolation;\n /**\n * Present when the applied isolation is not the one that was asked for,\n * explaining why and what it means for the child.\n */\n isolation_note?: string;\n /** Cloud only: poll this if `conversation_id` is still null. */\n start_task_id?: string;\n /** Cloud only: which connected Cloud backend the child was launched on. */\n backend?: string;\n /** Whether the parent/child link was persisted server-side. */\n parent_link: boolean;\n /** Present when `parent_link` is false, explaining why. */\n parent_link_note?: string;\n}\n\ninterface LaunchFailure {\n status: \"error\";\n error: string;\n guidance: string;\n}\n\nexport type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;\n\ninterface ValidatedParams {\n target: ChildConversationTarget;\n task: string;\n title: string | null;\n repository: string | null;\n branch: string | null;\n isolation: ChildConversationIsolation;\n}\n\nconst failure = (error: string, guidance: string): LaunchFailure => ({\n status: \"error\",\n error,\n guidance,\n});\n\nconst quoted = (values: readonly string[]) =>\n values.map((value) => `\"${value}\"`).join(\" or \");\n\nconst blankToNull = (value: string | null | undefined) => {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n};\n\n/**\n * Validate what the agent-server cannot.\n *\n * The tool's JSON Schema is turned into a pydantic model with `extra=\"forbid\"`,\n * so unknown/misspelled parameter names, missing required parameters and wrong\n * types already fail server-side with a corrective message. `enum` is the gap:\n * the SDK advertises it to the LLM but drops it when building the model, so a\n * misspelled `target` reaches us intact and must be rejected here. The\n * cross-target rules (cloud-only / local-only parameters) are ours to enforce\n * too, since a schema cannot express them.\n */\nfunction validateLaunchParams(\n action: LaunchChildConversationAction,\n):\n | { ok: true; params: ValidatedParams }\n | { ok: false; failure: LaunchFailure } {\n const target = action.target as ChildConversationTarget;\n if (!CHILD_CONVERSATION_TARGETS.includes(target)) {\n return {\n ok: false,\n failure: failure(\n `Unknown target ${JSON.stringify(action.target)}.`,\n `\\`target\\` must be exactly ${quoted(CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`,\n ),\n };\n }\n\n const task = blankToNull(action.task);\n if (!task) {\n return {\n ok: false,\n failure: failure(\n \"`task` is empty.\",\n \"`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.\",\n ),\n };\n }\n\n const repository = blankToNull(action.repository);\n const branch = blankToNull(action.branch);\n const isolation = blankToNull(\n action.isolation,\n ) as ChildConversationIsolation | null;\n\n if (target === \"local\" && (repository || branch)) {\n return {\n ok: false,\n failure: failure(\n '`repository`/`branch` were passed with target=\"local\".',\n 'A local child always runs in this conversation\\'s workspace. Drop `repository` and `branch`, or use target=\"cloud\" to run against a repository in a Cloud sandbox.',\n ),\n };\n }\n\n if (target === \"cloud\" && isolation) {\n return {\n ok: false,\n failure: failure(\n '`isolation` was passed with target=\"cloud\".',\n 'Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target=\"local\" to choose between \"worktree\" and \"shared\".',\n ),\n };\n }\n\n if (branch && !repository) {\n return {\n ok: false,\n failure: failure(\n \"`branch` was passed without `repository`.\",\n 'Pass `repository` as \"owner/repo\" alongside `branch`, or drop `branch` to use the repository\\'s default branch.',\n ),\n };\n }\n\n if (isolation && !CHILD_CONVERSATION_ISOLATIONS.includes(isolation)) {\n return {\n ok: false,\n failure: failure(\n `Unknown isolation ${JSON.stringify(action.isolation)}.`,\n `\\`isolation\\` must be exactly ${quoted(CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`,\n ),\n };\n }\n\n return {\n ok: true,\n params: {\n target,\n task,\n title: blankToNull(action.title),\n repository,\n branch,\n isolation: isolation ?? \"worktree\",\n },\n };\n}\n\n/**\n * Remember which tool calls have already been acted on.\n *\n * Unlike the Canvas UI tool, a launch is not idempotent: replaying its\n * ActionEvent (a socket reconnect that falls back to `resend_mode: \"all\"`, or a\n * REST/WebSocket race after a reload) would start a second — on Cloud, billable\n * — conversation. Claim the tool call before any network work so a replay that\n * arrives mid-flight is dropped too.\n */\nfunction claimToolCall(parentConversationId: string, toolCallId: string) {\n const key = `${LEDGER_STORAGE_KEY_PREFIX}${parentConversationId}`;\n let handled: string[] = [];\n try {\n const raw = window.localStorage.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : null;\n if (Array.isArray(parsed)) {\n handled = parsed.filter((id): id is string => typeof id === \"string\");\n }\n } catch {\n // A corrupt ledger must not block the launch; start a fresh one.\n }\n\n if (handled.includes(toolCallId)) return false;\n\n try {\n window.localStorage.setItem(key, JSON.stringify([...handled, toolCallId]));\n } catch {\n // Storage full or unavailable — proceed, accepting replay risk over never\n // launching at all.\n }\n return true;\n}\n\nfunction absoluteCanvasUrl(path: string) {\n const canvasPath = buildAgentCanvasPath(path);\n if (typeof window === \"undefined\") return canvasPath;\n return new URL(canvasPath, window.location.origin).toString();\n}\n\n/**\n * `parent_conversation_id` on the local start request landed in agent-server\n * 1.37.1. Older servers ignore unknown fields, so the child is created without\n * the link — report that rather than letting the agent assume a relationship\n * that does not exist.\n */\nfunction localParentLinkNote(): string | null {\n const version = getCachedAgentServerVersion();\n if (!version) return null;\n const comparison = compareAgentServerVersions(\n version,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n );\n if (comparison === null || comparison >= 0) return null;\n return `Agent server ${version} does not persist parent/child conversation links (needs ${MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`;\n}\n\n/**\n * Whether the parent's workspace can host a git worktree.\n *\n * A conversation started without a repository or an attached workspace runs in\n * a scratch directory that is `git init`-ed but never committed to. `git\n * worktree add` cannot branch from an unborn HEAD, and the agent-server raises\n * the resulting error straight out of its start-conversation handler as a 500,\n * so asking for a worktree there fails the whole launch. Conversation metadata\n * is only persisted when a repository or an explicit workspace was chosen (see\n * `createConversation`), so its absence pinpoints exactly those scratch\n * workspaces. Anything else is attempted and falls back on failure, because the\n * frontend cannot inspect the workspace's git state directly.\n */\nconst parentSupportsWorktree = (\n metadata: ConversationMetadata | null,\n): boolean => !!(metadata?.selected_repository || metadata?.selected_workspace);\n\nconst SHARED_FALLBACK_CONSEQUENCE =\n \"The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.\";\n\nasync function launchLocalChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n // The agent-server rejects a parent whose workspace differs from the child's,\n // so the child requests the parent's own directory. `worktree` then carves an\n // isolated worktree out of it, which is what keeps siblings from colliding.\n const workspace =\n await AgentServerConversationService.resolveConversationWorkingDir(\n parentConversationId,\n );\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n\n const createChild = (isolation: ChildConversationIsolation) =>\n AgentServerConversationService.createConversation({\n initialUserMsg: params.task,\n metadata: parentMetadata\n ? {\n selected_repository: parentMetadata.selected_repository,\n selected_branch: parentMetadata.selected_branch,\n git_provider: parentMetadata.git_provider,\n }\n : null,\n workingDirOverride: workspace,\n workspaceMode: isolation === \"shared\" ? \"local_repo\" : \"new_worktree\",\n parentConversationId,\n });\n\n let isolation = params.isolation;\n let isolationNote: string | null = null;\n\n if (isolation === \"worktree\" && !parentSupportsWorktree(parentMetadata)) {\n isolation = \"shared\";\n isolationNote = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n let startTask;\n try {\n startTask = await createChild(isolation);\n } catch (worktreeError) {\n if (isolation !== \"worktree\") throw worktreeError;\n // Creating the worktree is the one part of a local launch that can fail on\n // an otherwise healthy workspace, so retry without it rather than losing\n // the launch. Anything else fails both attempts and is reported as-is.\n try {\n startTask = await createChild(\"shared\");\n } catch {\n throw worktreeError;\n }\n isolation = \"shared\";\n isolationNote = `Creating a git worktree in this conversation's workspace failed (${worktreeError instanceof Error ? worktreeError.message : String(worktreeError)}). ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n const conversationId = startTask.app_conversation_id ?? startTask.id;\n\n if (params.title) {\n // Local start requests carry no title — only `autotitle`. Best effort: a\n // failed rename must not fail the launch.\n await AgentServerConversationService.updateConversationTitle(\n conversationId,\n params.title,\n ).catch(() => undefined);\n }\n\n const parentLinkNote = localParentLinkNote();\n return {\n status: \"launched\",\n target: \"local\",\n conversation_id: conversationId,\n url: absoluteCanvasUrl(`/conversations/${conversationId}`),\n initial_status: startTask.status,\n title: params.title,\n workspace,\n isolation,\n ...(isolationNote ? { isolation_note: isolationNote } : {}),\n parent_link: !parentLinkNote,\n ...(parentLinkNote ? { parent_link_note: parentLinkNote } : {}),\n };\n}\n\nconst delay = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Wait for the Cloud start task to expose its conversation id.\n *\n * The Cloud API provisions a sandbox asynchronously and only fills in\n * `app_conversation_id` once the task reaches READY, so without this the agent\n * would get a result with no conversation to open. Bounded: on timeout the\n * caller reports the still-provisioning task instead of hanging.\n */\nasync function waitForCloudConversationId(\n task: AppConversationStartTask,\n backend: Backend,\n): Promise<AppConversationStartTask> {\n const deadline = Date.now() + CLOUD_START_POLL_TIMEOUT_MS;\n let latest = task;\n while (!latest.app_conversation_id && latest.status !== \"ERROR\") {\n if (Date.now() >= deadline) break;\n\n await delay(CLOUD_START_POLL_INTERVAL_MS);\n\n const next = await getCloudAppConversationStartTask(\n latest.id,\n backend,\n ).catch(() => null);\n if (!next) break;\n latest = next;\n }\n return latest;\n}\n\nasync function launchCloudChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n const backend = pickCloudBackendForLaunch();\n if (!backend) {\n return failure(\n \"No OpenHands Cloud backend is connected in Agent Canvas.\",\n 'Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target=\"local\".',\n );\n }\n\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n const startTask = await createCloudAppConversation(\n {\n initial_message: {\n role: \"user\",\n content: [{ type: \"text\", text: params.task }],\n },\n title: params.title,\n // Fall back to this conversation's repository so a bare `target: \"cloud\"`\n // call still lands the child on the code the user is working on. The\n // provider only travels with the inherited repository — it may not apply\n // to one the agent named itself.\n selected_repository:\n params.repository ?? parentMetadata?.selected_repository ?? null,\n selected_branch: params.branch ?? null,\n git_provider: params.repository\n ? null\n : (parentMetadata?.git_provider ?? null),\n // The parent runs on the local agent-server, so its id means nothing to\n // Cloud. Sending it would only hide the child from the Cloud\n // conversation list, which filters out anything with a parent.\n parent_conversation_id: null,\n },\n backend,\n );\n\n const settled = await waitForCloudConversationId(startTask, backend);\n if (settled.status === \"ERROR\") {\n return failure(\n settled.detail || \"The Cloud conversation failed to start.\",\n 'The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target=\"local\".',\n );\n }\n\n const conversationId = settled.app_conversation_id;\n return {\n status: \"launched\",\n target: \"cloud\",\n conversation_id: conversationId,\n url: conversationId\n ? `${backend.host.replace(/\\/$/, \"\")}/conversations/${conversationId}`\n : null,\n initial_status: settled.status,\n title: params.title,\n start_task_id: settled.id,\n backend: backend.name,\n // Cloud children of a local parent carry no server-side link; see above.\n parent_link: false,\n parent_link_note:\n \"This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.\",\n };\n}\n\n/**\n * Hand the outcome back to the agent and the user.\n *\n * Client tools are acknowledged by the agent-server before the browser does any\n * work, so a message is the only way to give the agent the child's id or tell\n * it how to fix a malformed call. `sendMessage` runs the agent, which is what\n * makes it relay the result to the user in its next turn.\n */\nasync function reportLaunchResult(\n parentConversationId: string,\n result: LaunchChildConversationResult,\n) {\n if (result.status === \"error\") {\n displayErrorToast(\n i18n.t(I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED, {\n error: result.error,\n }),\n );\n } else if (result.url) {\n displaySuccessToastWithLink(\n i18n.t(\n result.target === \"cloud\"\n ? I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD\n : I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL,\n ),\n i18n.t(I18nKey.CHILD_CONVERSATION$OPEN),\n result.url,\n );\n }\n\n // The agent-server cancels an active `/goal` loop on any inbound message, so\n // a launch must not silently end one. The toast above still tells the user\n // what happened.\n const goalStatus =\n useGoalStore.getState().statusByConversation[parentConversationId];\n if (goalStatus?.active) return;\n\n await AgentServerConversationService.sendMessage(parentConversationId, {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: `${CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(result)}`,\n },\n ],\n });\n}\n\n/**\n * Execute a `launch_child_conversation` tool call.\n *\n * Never rejects: every failure is turned into corrective guidance for the\n * agent, because the agent-server has already told it the call succeeded.\n */\nexport async function handleLaunchChildConversationAction(\n action: LaunchChildConversationAction,\n parentConversationId: string,\n toolCallId: string,\n): Promise<void> {\n if (!claimToolCall(parentConversationId, toolCallId)) return;\n\n const validation = validateLaunchParams(action);\n let result: LaunchChildConversationResult;\n if (!validation.ok) {\n result = validation.failure;\n } else {\n try {\n result =\n validation.params.target === \"cloud\"\n ? await launchCloudChild(validation.params, parentConversationId)\n : await launchLocalChild(validation.params, parentConversationId);\n } catch (error) {\n result = failure(\n error instanceof Error ? error.message : String(error),\n \"The launch request failed. Report the error to the user; retry only if the cause looks transient.\",\n );\n }\n }\n\n await reportLaunchResult(parentConversationId, result).catch((error) => {\n console.warn(\n `[${LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,\n error,\n );\n });\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/zustand/esm/react.cjs"),t=require("../node_modules/zustand/esm/middleware.cjs");var n=`archived-conversations`,r={archivesByBackendId:{}};function i(e,t){return e[t]??[]}var a=e.create()(t.persist((e,t)=>({...r,archiveConversation:(n,r)=>{let a=i(t().archivesByBackendId,n);a.includes(r)||e(e=>({archivesByBackendId:{...e.archivesByBackendId,[n]:[r,...a]}}))},removeArchivedConversation:(n,r)=>{let a=i(t().archivesByBackendId,n);a.includes(r)&&e(e=>({archivesByBackendId:{...e.archivesByBackendId,[n]:a.filter(e=>e!==r)}}))},isArchived:(e,n)=>i(t().archivesByBackendId,e).includes(n)}),{name:n,storage:t.createJSONStorage(()=>localStorage),partialize:e=>({archivesByBackendId:e.archivesByBackendId})}));exports.useArchivedConversationsStore=a;
2
+ //# sourceMappingURL=archived-conversations-store.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archived-conversations-store.cjs","names":[],"sources":["../../src/stores/archived-conversations-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport const ARCHIVED_CONVERSATIONS_STORAGE_KEY = \"archived-conversations\";\n\ninterface ArchivedConversationsState {\n archivesByBackendId: Record<string, string[]>;\n}\n\ninterface ArchivedConversationsActions {\n archiveConversation: (backendId: string, conversationId: string) => void;\n removeArchivedConversation: (\n backendId: string,\n conversationId: string,\n ) => void;\n isArchived: (backendId: string, conversationId: string) => boolean;\n}\n\ntype ArchivedConversationsStore = ArchivedConversationsState &\n ArchivedConversationsActions;\n\nconst initialState: ArchivedConversationsState = {\n archivesByBackendId: {},\n};\n\nfunction getArchivesForBackend(\n archivesByBackendId: Record<string, string[]>,\n backendId: string,\n): string[] {\n return archivesByBackendId[backendId] ?? [];\n}\n\nexport const useArchivedConversationsStore =\n create<ArchivedConversationsStore>()(\n persist(\n (set, get) => ({\n ...initialState,\n\n archiveConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: [conversationId, ...current],\n },\n }));\n },\n\n removeArchivedConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (!current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: current.filter((id) => id !== conversationId),\n },\n }));\n },\n\n isArchived: (backendId, conversationId) =>\n getArchivesForBackend(get().archivesByBackendId, backendId).includes(\n conversationId,\n ),\n }),\n {\n name: ARCHIVED_CONVERSATIONS_STORAGE_KEY,\n storage: createJSONStorage(() => localStorage),\n partialize: (state): ArchivedConversationsState => ({\n archivesByBackendId: state.archivesByBackendId,\n }),\n },\n ),\n );\n"],"mappings":"8JAGA,IAAa,EAAqC,yBAkB5C,EAA2C,CAC/C,oBAAqB,CAAC,CACxB,EAEA,SAAS,EACP,EACA,EACU,CACV,OAAO,EAAoB,IAAc,CAAC,CAC5C,CAEA,IAAa,EACX,EAAA,OAAmC,EACjC,EAAA,SACG,EAAK,KAAS,CACb,GAAG,EAEH,qBAAsB,EAAW,IAAmB,CAClD,IAAM,EAAU,EACd,EAAI,EAAE,oBACN,CACF,EACI,EAAQ,SAAS,CAAc,GAGnC,EAAK,IAAW,CACd,oBAAqB,CACnB,GAAG,EAAM,qBACR,GAAY,CAAC,EAAgB,GAAG,CAAO,CAC1C,CACF,EAAE,CACJ,EAEA,4BAA6B,EAAW,IAAmB,CACzD,IAAM,EAAU,EACd,EAAI,EAAE,oBACN,CACF,EACK,EAAQ,SAAS,CAAc,GAGpC,EAAK,IAAW,CACd,oBAAqB,CACnB,GAAG,EAAM,qBACR,GAAY,EAAQ,OAAQ,GAAO,IAAO,CAAc,CAC3D,CACF,EAAE,CACJ,EAEA,YAAa,EAAW,IACtB,EAAsB,EAAI,EAAE,oBAAqB,CAAS,EAAE,SAC1D,CACF,CACJ,GACA,CACE,KAAM,EACN,QAAS,EAAA,sBAAwB,YAAY,EAC7C,WAAa,IAAuC,CAClD,oBAAqB,EAAM,mBAC7B,EACF,CACF,CACF"}
@@ -0,0 +1,24 @@
1
+ export declare const ARCHIVED_CONVERSATIONS_STORAGE_KEY = "archived-conversations";
2
+ interface ArchivedConversationsState {
3
+ archivesByBackendId: Record<string, string[]>;
4
+ }
5
+ interface ArchivedConversationsActions {
6
+ archiveConversation: (backendId: string, conversationId: string) => void;
7
+ removeArchivedConversation: (backendId: string, conversationId: string) => void;
8
+ isArchived: (backendId: string, conversationId: string) => boolean;
9
+ }
10
+ type ArchivedConversationsStore = ArchivedConversationsState & ArchivedConversationsActions;
11
+ export declare const useArchivedConversationsStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ArchivedConversationsStore>, "setState" | "persist"> & {
12
+ setState(partial: ArchivedConversationsStore | Partial<ArchivedConversationsStore> | ((state: ArchivedConversationsStore) => ArchivedConversationsStore | Partial<ArchivedConversationsStore>), replace?: false | undefined): unknown;
13
+ setState(state: ArchivedConversationsStore | ((state: ArchivedConversationsStore) => ArchivedConversationsStore), replace: true): unknown;
14
+ persist: {
15
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ArchivedConversationsStore, unknown, unknown>>) => void;
16
+ clearStorage: () => void;
17
+ rehydrate: () => Promise<void> | void;
18
+ hasHydrated: () => boolean;
19
+ onHydrate: (fn: (state: ArchivedConversationsStore) => void) => () => void;
20
+ onFinishHydration: (fn: (state: ArchivedConversationsStore) => void) => () => void;
21
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<ArchivedConversationsStore, unknown, unknown>>;
22
+ };
23
+ }>;
24
+ export {};
@@ -0,0 +1,33 @@
1
+ import { create as e } from "../node_modules/zustand/esm/react.js";
2
+ import { createJSONStorage as t, persist as n } from "../node_modules/zustand/esm/middleware.js";
3
+ //#region src/stores/archived-conversations-store.ts
4
+ var r = "archived-conversations", i = { archivesByBackendId: {} };
5
+ function a(e, t) {
6
+ return e[t] ?? [];
7
+ }
8
+ var o = e()(n((e, t) => ({
9
+ ...i,
10
+ archiveConversation: (n, r) => {
11
+ let i = a(t().archivesByBackendId, n);
12
+ i.includes(r) || e((e) => ({ archivesByBackendId: {
13
+ ...e.archivesByBackendId,
14
+ [n]: [r, ...i]
15
+ } }));
16
+ },
17
+ removeArchivedConversation: (n, r) => {
18
+ let i = a(t().archivesByBackendId, n);
19
+ i.includes(r) && e((e) => ({ archivesByBackendId: {
20
+ ...e.archivesByBackendId,
21
+ [n]: i.filter((e) => e !== r)
22
+ } }));
23
+ },
24
+ isArchived: (e, n) => a(t().archivesByBackendId, e).includes(n)
25
+ }), {
26
+ name: r,
27
+ storage: t(() => localStorage),
28
+ partialize: (e) => ({ archivesByBackendId: e.archivesByBackendId })
29
+ }));
30
+ //#endregion
31
+ export { o as useArchivedConversationsStore };
32
+
33
+ //# sourceMappingURL=archived-conversations-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archived-conversations-store.js","names":[],"sources":["../../src/stores/archived-conversations-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport const ARCHIVED_CONVERSATIONS_STORAGE_KEY = \"archived-conversations\";\n\ninterface ArchivedConversationsState {\n archivesByBackendId: Record<string, string[]>;\n}\n\ninterface ArchivedConversationsActions {\n archiveConversation: (backendId: string, conversationId: string) => void;\n removeArchivedConversation: (\n backendId: string,\n conversationId: string,\n ) => void;\n isArchived: (backendId: string, conversationId: string) => boolean;\n}\n\ntype ArchivedConversationsStore = ArchivedConversationsState &\n ArchivedConversationsActions;\n\nconst initialState: ArchivedConversationsState = {\n archivesByBackendId: {},\n};\n\nfunction getArchivesForBackend(\n archivesByBackendId: Record<string, string[]>,\n backendId: string,\n): string[] {\n return archivesByBackendId[backendId] ?? [];\n}\n\nexport const useArchivedConversationsStore =\n create<ArchivedConversationsStore>()(\n persist(\n (set, get) => ({\n ...initialState,\n\n archiveConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: [conversationId, ...current],\n },\n }));\n },\n\n removeArchivedConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (!current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: current.filter((id) => id !== conversationId),\n },\n }));\n },\n\n isArchived: (backendId, conversationId) =>\n getArchivesForBackend(get().archivesByBackendId, backendId).includes(\n conversationId,\n ),\n }),\n {\n name: ARCHIVED_CONVERSATIONS_STORAGE_KEY,\n storage: createJSONStorage(() => localStorage),\n partialize: (state): ArchivedConversationsState => ({\n archivesByBackendId: state.archivesByBackendId,\n }),\n },\n ),\n );\n"],"mappings":";;;AAGA,IAAa,IAAqC,0BAkB5C,IAA2C,EAC/C,qBAAqB,CAAC,EACxB;AAEA,SAAS,EACP,GACA,GACU;CACV,OAAO,EAAoB,MAAc,CAAC;AAC5C;AAEA,IAAa,IACX,EAAmC,EACjC,GACG,GAAK,OAAS;CACb,GAAG;CAEH,sBAAsB,GAAW,MAAmB;EAClD,IAAM,IAAU,EACd,EAAI,EAAE,qBACN,CACF;EACI,EAAQ,SAAS,CAAc,KAGnC,GAAK,OAAW,EACd,qBAAqB;GACnB,GAAG,EAAM;IACR,IAAY,CAAC,GAAgB,GAAG,CAAO;EAC1C,EACF,EAAE;CACJ;CAEA,6BAA6B,GAAW,MAAmB;EACzD,IAAM,IAAU,EACd,EAAI,EAAE,qBACN,CACF;EACK,EAAQ,SAAS,CAAc,KAGpC,GAAK,OAAW,EACd,qBAAqB;GACnB,GAAG,EAAM;IACR,IAAY,EAAQ,QAAQ,MAAO,MAAO,CAAc;EAC3D,EACF,EAAE;CACJ;CAEA,aAAa,GAAW,MACtB,EAAsB,EAAI,EAAE,qBAAqB,CAAS,EAAE,SAC1D,CACF;AACJ,IACA;CACE,MAAM;CACN,SAAS,QAAwB,YAAY;CAC7C,aAAa,OAAuC,EAClD,qBAAqB,EAAM,oBAC7B;AACF,CACF,CACF"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/zustand/esm/react.cjs"),t=require("../node_modules/zustand/esm/middleware.cjs");var n={showOlderConversations:!0,showRepoBranchMetadata:!1,showLlmProfiles:!1,showTagsMetadata:!1,showHoverMetadata:!0,organizeMode:`chronological`,conversationSort:`updated`,threadScope:`all`,automationFilterMode:`all`,selectedAutomationNames:[],groupFolderOrder:[]},r=e.create()(t.persist(e=>({...n,setShowOlderConversations:t=>e(()=>({showOlderConversations:t})),toggleShowOlderConversations:()=>e(e=>({showOlderConversations:!e.showOlderConversations})),setShowRepoBranchMetadata:t=>e(()=>({showRepoBranchMetadata:t})),toggleShowRepoBranchMetadata:()=>e(e=>({showRepoBranchMetadata:!e.showRepoBranchMetadata})),setShowLlmProfiles:t=>e(()=>({showLlmProfiles:t})),toggleShowLlmProfiles:()=>e(e=>({showLlmProfiles:!e.showLlmProfiles})),setShowTagsMetadata:t=>e(()=>({showTagsMetadata:t})),toggleShowTagsMetadata:()=>e(e=>({showTagsMetadata:!e.showTagsMetadata})),setShowHoverMetadata:t=>e(()=>({showHoverMetadata:t})),toggleShowHoverMetadata:()=>e(e=>({showHoverMetadata:!e.showHoverMetadata})),setOrganizeMode:t=>e(()=>({organizeMode:t})),setConversationSort:t=>e(()=>({conversationSort:t})),setThreadScope:t=>e(()=>({threadScope:t})),setAutomationFilterMode:t=>e(()=>({automationFilterMode:t})),toggleAutomationName:t=>e(e=>({selectedAutomationNames:e.selectedAutomationNames.includes(t)?e.selectedAutomationNames.filter(e=>e!==t):[...e.selectedAutomationNames,t]})),setGroupFolderOrder:t=>e(()=>({groupFolderOrder:[...t]}))}),{name:`conversation-panel-preferences`,storage:t.createJSONStorage(()=>localStorage),partialize:e=>({showOlderConversations:e.showOlderConversations,showRepoBranchMetadata:e.showRepoBranchMetadata,showLlmProfiles:e.showLlmProfiles,showTagsMetadata:e.showTagsMetadata,showHoverMetadata:e.showHoverMetadata,organizeMode:e.organizeMode,conversationSort:e.conversationSort,threadScope:e.threadScope,automationFilterMode:e.automationFilterMode,selectedAutomationNames:e.selectedAutomationNames,groupFolderOrder:e.groupFolderOrder})}));exports.useConversationPanelPreferencesStore=r;
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/zustand/esm/react.cjs"),t=require("../node_modules/zustand/esm/middleware.cjs");var n={showOlderConversations:!0,showArchivedConversations:!1,showRepoBranchMetadata:!1,showLlmProfiles:!1,showTagsMetadata:!1,showHoverMetadata:!0,organizeMode:`chronological`,conversationSort:`updated`,threadScope:`all`,automationFilterMode:`all`,selectedAutomationNames:[],groupFolderOrder:[]},r=e.create()(t.persist(e=>({...n,setShowOlderConversations:t=>e(()=>({showOlderConversations:t})),toggleShowOlderConversations:()=>e(e=>({showOlderConversations:!e.showOlderConversations})),setShowArchivedConversations:t=>e(()=>({showArchivedConversations:t})),toggleShowArchivedConversations:()=>e(e=>({showArchivedConversations:!e.showArchivedConversations})),setShowRepoBranchMetadata:t=>e(()=>({showRepoBranchMetadata:t})),toggleShowRepoBranchMetadata:()=>e(e=>({showRepoBranchMetadata:!e.showRepoBranchMetadata})),setShowLlmProfiles:t=>e(()=>({showLlmProfiles:t})),toggleShowLlmProfiles:()=>e(e=>({showLlmProfiles:!e.showLlmProfiles})),setShowTagsMetadata:t=>e(()=>({showTagsMetadata:t})),toggleShowTagsMetadata:()=>e(e=>({showTagsMetadata:!e.showTagsMetadata})),setShowHoverMetadata:t=>e(()=>({showHoverMetadata:t})),toggleShowHoverMetadata:()=>e(e=>({showHoverMetadata:!e.showHoverMetadata})),setOrganizeMode:t=>e(()=>({organizeMode:t})),setConversationSort:t=>e(()=>({conversationSort:t})),setThreadScope:t=>e(()=>({threadScope:t})),setAutomationFilterMode:t=>e(()=>({automationFilterMode:t})),toggleAutomationName:t=>e(e=>({selectedAutomationNames:e.selectedAutomationNames.includes(t)?e.selectedAutomationNames.filter(e=>e!==t):[...e.selectedAutomationNames,t]})),setGroupFolderOrder:t=>e(()=>({groupFolderOrder:[...t]}))}),{name:`conversation-panel-preferences`,storage:t.createJSONStorage(()=>localStorage),partialize:e=>({showOlderConversations:e.showOlderConversations,showArchivedConversations:e.showArchivedConversations,showRepoBranchMetadata:e.showRepoBranchMetadata,showLlmProfiles:e.showLlmProfiles,showTagsMetadata:e.showTagsMetadata,showHoverMetadata:e.showHoverMetadata,organizeMode:e.organizeMode,conversationSort:e.conversationSort,threadScope:e.threadScope,automationFilterMode:e.automationFilterMode,selectedAutomationNames:e.selectedAutomationNames,groupFolderOrder:e.groupFolderOrder})}));exports.useConversationPanelPreferencesStore=r;
2
2
  //# sourceMappingURL=conversation-panel-preferences-store.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-panel-preferences-store.cjs","names":[],"sources":["../../src/stores/conversation-panel-preferences-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport {\n type AutomationFilterMode,\n type ConversationSortField,\n type OrganizeMode,\n type ThreadScope,\n} from \"#/components/features/conversation-panel/conversation-panel-list-helpers\";\n\n/**\n * User-toggleable display preferences for the sidebar conversation list\n * filter menu. These are intentionally persisted to localStorage (via the\n * same `zustand/persist` pattern used by `home-store` and `workspaces-store`)\n * so the menu state survives full reloads.\n *\n * To add a new preference exposed by the filter menu:\n * 1. Add a field here with a sensible default in `initialState`.\n * 2. Add matching `setX`/`toggleX` actions below.\n * 3. Read/write through the store in `conversation-panel.tsx`.\n * No additional plumbing (storage keys, sanitization, etc.) is required —\n * `persist` handles migration of unknown fields gracefully.\n */\ninterface ConversationPanelPreferencesState {\n showOlderConversations: boolean;\n showRepoBranchMetadata: boolean;\n showLlmProfiles: boolean;\n showTagsMetadata: boolean;\n showHoverMetadata: boolean;\n organizeMode: OrganizeMode;\n conversationSort: ConversationSortField;\n threadScope: ThreadScope;\n automationFilterMode: AutomationFilterMode;\n selectedAutomationNames: string[];\n groupFolderOrder: string[];\n}\n\ninterface ConversationPanelPreferencesActions {\n setShowOlderConversations: (value: boolean) => void;\n toggleShowOlderConversations: () => void;\n setShowRepoBranchMetadata: (value: boolean) => void;\n toggleShowRepoBranchMetadata: () => void;\n setShowLlmProfiles: (value: boolean) => void;\n toggleShowLlmProfiles: () => void;\n setShowTagsMetadata: (value: boolean) => void;\n toggleShowTagsMetadata: () => void;\n setShowHoverMetadata: (value: boolean) => void;\n toggleShowHoverMetadata: () => void;\n setOrganizeMode: (value: OrganizeMode) => void;\n setConversationSort: (value: ConversationSortField) => void;\n setThreadScope: (value: ThreadScope) => void;\n setAutomationFilterMode: (value: AutomationFilterMode) => void;\n toggleAutomationName: (name: string) => void;\n setGroupFolderOrder: (order: readonly string[]) => void;\n}\n\ntype ConversationPanelPreferencesStore = ConversationPanelPreferencesState &\n ConversationPanelPreferencesActions;\n\nconst initialState: ConversationPanelPreferencesState = {\n showOlderConversations: true,\n showRepoBranchMetadata: false,\n showLlmProfiles: false,\n showTagsMetadata: false,\n showHoverMetadata: true,\n organizeMode: \"chronological\",\n conversationSort: \"updated\",\n threadScope: \"all\",\n automationFilterMode: \"all\",\n selectedAutomationNames: [],\n groupFolderOrder: [],\n};\n\nexport const useConversationPanelPreferencesStore =\n create<ConversationPanelPreferencesStore>()(\n persist(\n (set) => ({\n ...initialState,\n\n setShowOlderConversations: (value) =>\n set(() => ({ showOlderConversations: value })),\n toggleShowOlderConversations: () =>\n set((state) => ({\n showOlderConversations: !state.showOlderConversations,\n })),\n\n setShowRepoBranchMetadata: (value) =>\n set(() => ({ showRepoBranchMetadata: value })),\n toggleShowRepoBranchMetadata: () =>\n set((state) => ({\n showRepoBranchMetadata: !state.showRepoBranchMetadata,\n })),\n\n setShowLlmProfiles: (value) => set(() => ({ showLlmProfiles: value })),\n toggleShowLlmProfiles: () =>\n set((state) => ({\n showLlmProfiles: !state.showLlmProfiles,\n })),\n\n setShowTagsMetadata: (value) =>\n set(() => ({ showTagsMetadata: value })),\n toggleShowTagsMetadata: () =>\n set((state) => ({\n showTagsMetadata: !state.showTagsMetadata,\n })),\n\n setShowHoverMetadata: (value) =>\n set(() => ({ showHoverMetadata: value })),\n toggleShowHoverMetadata: () =>\n set((state) => ({\n showHoverMetadata: !state.showHoverMetadata,\n })),\n\n setOrganizeMode: (value) => set(() => ({ organizeMode: value })),\n setConversationSort: (value) =>\n set(() => ({ conversationSort: value })),\n setThreadScope: (value) => set(() => ({ threadScope: value })),\n setAutomationFilterMode: (value) =>\n set(() => ({ automationFilterMode: value })),\n toggleAutomationName: (name) =>\n set((state) => ({\n selectedAutomationNames: state.selectedAutomationNames.includes(\n name,\n )\n ? state.selectedAutomationNames.filter(\n (existing) => existing !== name,\n )\n : [...state.selectedAutomationNames, name],\n })),\n setGroupFolderOrder: (order) =>\n set(() => ({ groupFolderOrder: [...order] })),\n }),\n {\n name: \"conversation-panel-preferences\",\n storage: createJSONStorage(() => localStorage),\n // Only persist the data fields — actions are recreated on each load.\n partialize: (state): ConversationPanelPreferencesState => ({\n showOlderConversations: state.showOlderConversations,\n showRepoBranchMetadata: state.showRepoBranchMetadata,\n showLlmProfiles: state.showLlmProfiles,\n showTagsMetadata: state.showTagsMetadata,\n showHoverMetadata: state.showHoverMetadata,\n organizeMode: state.organizeMode,\n conversationSort: state.conversationSort,\n threadScope: state.threadScope,\n automationFilterMode: state.automationFilterMode,\n selectedAutomationNames: state.selectedAutomationNames,\n groupFolderOrder: state.groupFolderOrder,\n }),\n },\n ),\n );\n"],"mappings":"8JA0DA,IAAM,EAAkD,CACtD,uBAAwB,GACxB,uBAAwB,GACxB,gBAAiB,GACjB,iBAAkB,GAClB,kBAAmB,GACnB,aAAc,gBACd,iBAAkB,UAClB,YAAa,MACb,qBAAsB,MACtB,wBAAyB,CAAC,EAC1B,iBAAkB,CAAC,CACrB,EAEa,EACX,EAAA,OAA0C,EACxC,EAAA,QACG,IAAS,CACR,GAAG,EAEH,0BAA4B,GAC1B,OAAW,CAAE,uBAAwB,CAAM,EAAE,EAC/C,iCACE,EAAK,IAAW,CACd,uBAAwB,CAAC,EAAM,sBACjC,EAAE,EAEJ,0BAA4B,GAC1B,OAAW,CAAE,uBAAwB,CAAM,EAAE,EAC/C,iCACE,EAAK,IAAW,CACd,uBAAwB,CAAC,EAAM,sBACjC,EAAE,EAEJ,mBAAqB,GAAU,OAAW,CAAE,gBAAiB,CAAM,EAAE,EACrE,0BACE,EAAK,IAAW,CACd,gBAAiB,CAAC,EAAM,eAC1B,EAAE,EAEJ,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAM,EAAE,EACzC,2BACE,EAAK,IAAW,CACd,iBAAkB,CAAC,EAAM,gBAC3B,EAAE,EAEJ,qBAAuB,GACrB,OAAW,CAAE,kBAAmB,CAAM,EAAE,EAC1C,4BACE,EAAK,IAAW,CACd,kBAAmB,CAAC,EAAM,iBAC5B,EAAE,EAEJ,gBAAkB,GAAU,OAAW,CAAE,aAAc,CAAM,EAAE,EAC/D,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAM,EAAE,EACzC,eAAiB,GAAU,OAAW,CAAE,YAAa,CAAM,EAAE,EAC7D,wBAA0B,GACxB,OAAW,CAAE,qBAAsB,CAAM,EAAE,EAC7C,qBAAuB,GACrB,EAAK,IAAW,CACd,wBAAyB,EAAM,wBAAwB,SACrD,CACF,EACI,EAAM,wBAAwB,OAC3B,GAAa,IAAa,CAC7B,EACA,CAAC,GAAG,EAAM,wBAAyB,CAAI,CAC7C,EAAE,EACJ,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAC,GAAG,CAAK,CAAE,EAAE,CAChD,GACA,CACE,KAAM,iCACN,QAAS,EAAA,sBAAwB,YAAY,EAE7C,WAAa,IAA8C,CACzD,uBAAwB,EAAM,uBAC9B,uBAAwB,EAAM,uBAC9B,gBAAiB,EAAM,gBACvB,iBAAkB,EAAM,iBACxB,kBAAmB,EAAM,kBACzB,aAAc,EAAM,aACpB,iBAAkB,EAAM,iBACxB,YAAa,EAAM,YACnB,qBAAsB,EAAM,qBAC5B,wBAAyB,EAAM,wBAC/B,iBAAkB,EAAM,gBAC1B,EACF,CACF,CACF"}
1
+ {"version":3,"file":"conversation-panel-preferences-store.cjs","names":[],"sources":["../../src/stores/conversation-panel-preferences-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport {\n type AutomationFilterMode,\n type ConversationSortField,\n type OrganizeMode,\n type ThreadScope,\n} from \"#/components/features/conversation-panel/conversation-panel-list-helpers\";\n\n/**\n * User-toggleable display preferences for the sidebar conversation list\n * filter menu. These are intentionally persisted to localStorage (via the\n * same `zustand/persist` pattern used by `home-store` and `workspaces-store`)\n * so the menu state survives full reloads.\n *\n * To add a new preference exposed by the filter menu:\n * 1. Add a field here with a sensible default in `initialState`.\n * 2. Add matching `setX`/`toggleX` actions below.\n * 3. Read/write through the store in `conversation-panel.tsx`.\n * No additional plumbing (storage keys, sanitization, etc.) is required —\n * `persist` handles migration of unknown fields gracefully.\n */\ninterface ConversationPanelPreferencesState {\n showOlderConversations: boolean;\n showArchivedConversations: boolean;\n showRepoBranchMetadata: boolean;\n showLlmProfiles: boolean;\n showTagsMetadata: boolean;\n showHoverMetadata: boolean;\n organizeMode: OrganizeMode;\n conversationSort: ConversationSortField;\n threadScope: ThreadScope;\n automationFilterMode: AutomationFilterMode;\n selectedAutomationNames: string[];\n groupFolderOrder: string[];\n}\n\ninterface ConversationPanelPreferencesActions {\n setShowOlderConversations: (value: boolean) => void;\n toggleShowOlderConversations: () => void;\n setShowArchivedConversations: (value: boolean) => void;\n toggleShowArchivedConversations: () => void;\n setShowRepoBranchMetadata: (value: boolean) => void;\n toggleShowRepoBranchMetadata: () => void;\n setShowLlmProfiles: (value: boolean) => void;\n toggleShowLlmProfiles: () => void;\n setShowTagsMetadata: (value: boolean) => void;\n toggleShowTagsMetadata: () => void;\n setShowHoverMetadata: (value: boolean) => void;\n toggleShowHoverMetadata: () => void;\n setOrganizeMode: (value: OrganizeMode) => void;\n setConversationSort: (value: ConversationSortField) => void;\n setThreadScope: (value: ThreadScope) => void;\n setAutomationFilterMode: (value: AutomationFilterMode) => void;\n toggleAutomationName: (name: string) => void;\n setGroupFolderOrder: (order: readonly string[]) => void;\n}\n\ntype ConversationPanelPreferencesStore = ConversationPanelPreferencesState &\n ConversationPanelPreferencesActions;\n\nconst initialState: ConversationPanelPreferencesState = {\n showOlderConversations: true,\n showArchivedConversations: false,\n showRepoBranchMetadata: false,\n showLlmProfiles: false,\n showTagsMetadata: false,\n showHoverMetadata: true,\n organizeMode: \"chronological\",\n conversationSort: \"updated\",\n threadScope: \"all\",\n automationFilterMode: \"all\",\n selectedAutomationNames: [],\n groupFolderOrder: [],\n};\n\nexport const useConversationPanelPreferencesStore =\n create<ConversationPanelPreferencesStore>()(\n persist(\n (set) => ({\n ...initialState,\n\n setShowOlderConversations: (value) =>\n set(() => ({ showOlderConversations: value })),\n toggleShowOlderConversations: () =>\n set((state) => ({\n showOlderConversations: !state.showOlderConversations,\n })),\n\n setShowArchivedConversations: (value) =>\n set(() => ({ showArchivedConversations: value })),\n toggleShowArchivedConversations: () =>\n set((state) => ({\n showArchivedConversations: !state.showArchivedConversations,\n })),\n\n setShowRepoBranchMetadata: (value) =>\n set(() => ({ showRepoBranchMetadata: value })),\n toggleShowRepoBranchMetadata: () =>\n set((state) => ({\n showRepoBranchMetadata: !state.showRepoBranchMetadata,\n })),\n\n setShowLlmProfiles: (value) => set(() => ({ showLlmProfiles: value })),\n toggleShowLlmProfiles: () =>\n set((state) => ({\n showLlmProfiles: !state.showLlmProfiles,\n })),\n\n setShowTagsMetadata: (value) =>\n set(() => ({ showTagsMetadata: value })),\n toggleShowTagsMetadata: () =>\n set((state) => ({\n showTagsMetadata: !state.showTagsMetadata,\n })),\n\n setShowHoverMetadata: (value) =>\n set(() => ({ showHoverMetadata: value })),\n toggleShowHoverMetadata: () =>\n set((state) => ({\n showHoverMetadata: !state.showHoverMetadata,\n })),\n\n setOrganizeMode: (value) => set(() => ({ organizeMode: value })),\n setConversationSort: (value) =>\n set(() => ({ conversationSort: value })),\n setThreadScope: (value) => set(() => ({ threadScope: value })),\n setAutomationFilterMode: (value) =>\n set(() => ({ automationFilterMode: value })),\n toggleAutomationName: (name) =>\n set((state) => ({\n selectedAutomationNames: state.selectedAutomationNames.includes(\n name,\n )\n ? state.selectedAutomationNames.filter(\n (existing) => existing !== name,\n )\n : [...state.selectedAutomationNames, name],\n })),\n setGroupFolderOrder: (order) =>\n set(() => ({ groupFolderOrder: [...order] })),\n }),\n {\n name: \"conversation-panel-preferences\",\n storage: createJSONStorage(() => localStorage),\n // Only persist the data fields — actions are recreated on each load.\n partialize: (state): ConversationPanelPreferencesState => ({\n showOlderConversations: state.showOlderConversations,\n showArchivedConversations: state.showArchivedConversations,\n showRepoBranchMetadata: state.showRepoBranchMetadata,\n showLlmProfiles: state.showLlmProfiles,\n showTagsMetadata: state.showTagsMetadata,\n showHoverMetadata: state.showHoverMetadata,\n organizeMode: state.organizeMode,\n conversationSort: state.conversationSort,\n threadScope: state.threadScope,\n automationFilterMode: state.automationFilterMode,\n selectedAutomationNames: state.selectedAutomationNames,\n groupFolderOrder: state.groupFolderOrder,\n }),\n },\n ),\n );\n"],"mappings":"8JA6DA,IAAM,EAAkD,CACtD,uBAAwB,GACxB,0BAA2B,GAC3B,uBAAwB,GACxB,gBAAiB,GACjB,iBAAkB,GAClB,kBAAmB,GACnB,aAAc,gBACd,iBAAkB,UAClB,YAAa,MACb,qBAAsB,MACtB,wBAAyB,CAAC,EAC1B,iBAAkB,CAAC,CACrB,EAEa,EACX,EAAA,OAA0C,EACxC,EAAA,QACG,IAAS,CACR,GAAG,EAEH,0BAA4B,GAC1B,OAAW,CAAE,uBAAwB,CAAM,EAAE,EAC/C,iCACE,EAAK,IAAW,CACd,uBAAwB,CAAC,EAAM,sBACjC,EAAE,EAEJ,6BAA+B,GAC7B,OAAW,CAAE,0BAA2B,CAAM,EAAE,EAClD,oCACE,EAAK,IAAW,CACd,0BAA2B,CAAC,EAAM,yBACpC,EAAE,EAEJ,0BAA4B,GAC1B,OAAW,CAAE,uBAAwB,CAAM,EAAE,EAC/C,iCACE,EAAK,IAAW,CACd,uBAAwB,CAAC,EAAM,sBACjC,EAAE,EAEJ,mBAAqB,GAAU,OAAW,CAAE,gBAAiB,CAAM,EAAE,EACrE,0BACE,EAAK,IAAW,CACd,gBAAiB,CAAC,EAAM,eAC1B,EAAE,EAEJ,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAM,EAAE,EACzC,2BACE,EAAK,IAAW,CACd,iBAAkB,CAAC,EAAM,gBAC3B,EAAE,EAEJ,qBAAuB,GACrB,OAAW,CAAE,kBAAmB,CAAM,EAAE,EAC1C,4BACE,EAAK,IAAW,CACd,kBAAmB,CAAC,EAAM,iBAC5B,EAAE,EAEJ,gBAAkB,GAAU,OAAW,CAAE,aAAc,CAAM,EAAE,EAC/D,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAM,EAAE,EACzC,eAAiB,GAAU,OAAW,CAAE,YAAa,CAAM,EAAE,EAC7D,wBAA0B,GACxB,OAAW,CAAE,qBAAsB,CAAM,EAAE,EAC7C,qBAAuB,GACrB,EAAK,IAAW,CACd,wBAAyB,EAAM,wBAAwB,SACrD,CACF,EACI,EAAM,wBAAwB,OAC3B,GAAa,IAAa,CAC7B,EACA,CAAC,GAAG,EAAM,wBAAyB,CAAI,CAC7C,EAAE,EACJ,oBAAsB,GACpB,OAAW,CAAE,iBAAkB,CAAC,GAAG,CAAK,CAAE,EAAE,CAChD,GACA,CACE,KAAM,iCACN,QAAS,EAAA,sBAAwB,YAAY,EAE7C,WAAa,IAA8C,CACzD,uBAAwB,EAAM,uBAC9B,0BAA2B,EAAM,0BACjC,uBAAwB,EAAM,uBAC9B,gBAAiB,EAAM,gBACvB,iBAAkB,EAAM,iBACxB,kBAAmB,EAAM,kBACzB,aAAc,EAAM,aACpB,iBAAkB,EAAM,iBACxB,YAAa,EAAM,YACnB,qBAAsB,EAAM,qBAC5B,wBAAyB,EAAM,wBAC/B,iBAAkB,EAAM,gBAC1B,EACF,CACF,CACF"}
@@ -14,6 +14,7 @@ import { type AutomationFilterMode, type ConversationSortField, type OrganizeMod
14
14
  */
15
15
  interface ConversationPanelPreferencesState {
16
16
  showOlderConversations: boolean;
17
+ showArchivedConversations: boolean;
17
18
  showRepoBranchMetadata: boolean;
18
19
  showLlmProfiles: boolean;
19
20
  showTagsMetadata: boolean;
@@ -28,6 +29,8 @@ interface ConversationPanelPreferencesState {
28
29
  interface ConversationPanelPreferencesActions {
29
30
  setShowOlderConversations: (value: boolean) => void;
30
31
  toggleShowOlderConversations: () => void;
32
+ setShowArchivedConversations: (value: boolean) => void;
33
+ toggleShowArchivedConversations: () => void;
31
34
  setShowRepoBranchMetadata: (value: boolean) => void;
32
35
  toggleShowRepoBranchMetadata: () => void;
33
36
  setShowLlmProfiles: (value: boolean) => void;
@@ -3,6 +3,7 @@ import { createJSONStorage as t, persist as n } from "../node_modules/zustand/es
3
3
  //#region src/stores/conversation-panel-preferences-store.ts
4
4
  var r = {
5
5
  showOlderConversations: !0,
6
+ showArchivedConversations: !1,
6
7
  showRepoBranchMetadata: !1,
7
8
  showLlmProfiles: !1,
8
9
  showTagsMetadata: !1,
@@ -17,6 +18,8 @@ var r = {
17
18
  ...r,
18
19
  setShowOlderConversations: (t) => e(() => ({ showOlderConversations: t })),
19
20
  toggleShowOlderConversations: () => e((e) => ({ showOlderConversations: !e.showOlderConversations })),
21
+ setShowArchivedConversations: (t) => e(() => ({ showArchivedConversations: t })),
22
+ toggleShowArchivedConversations: () => e((e) => ({ showArchivedConversations: !e.showArchivedConversations })),
20
23
  setShowRepoBranchMetadata: (t) => e(() => ({ showRepoBranchMetadata: t })),
21
24
  toggleShowRepoBranchMetadata: () => e((e) => ({ showRepoBranchMetadata: !e.showRepoBranchMetadata })),
22
25
  setShowLlmProfiles: (t) => e(() => ({ showLlmProfiles: t })),
@@ -36,6 +39,7 @@ var r = {
36
39
  storage: t(() => localStorage),
37
40
  partialize: (e) => ({
38
41
  showOlderConversations: e.showOlderConversations,
42
+ showArchivedConversations: e.showArchivedConversations,
39
43
  showRepoBranchMetadata: e.showRepoBranchMetadata,
40
44
  showLlmProfiles: e.showLlmProfiles,
41
45
  showTagsMetadata: e.showTagsMetadata,
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-panel-preferences-store.js","names":[],"sources":["../../src/stores/conversation-panel-preferences-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport {\n type AutomationFilterMode,\n type ConversationSortField,\n type OrganizeMode,\n type ThreadScope,\n} from \"#/components/features/conversation-panel/conversation-panel-list-helpers\";\n\n/**\n * User-toggleable display preferences for the sidebar conversation list\n * filter menu. These are intentionally persisted to localStorage (via the\n * same `zustand/persist` pattern used by `home-store` and `workspaces-store`)\n * so the menu state survives full reloads.\n *\n * To add a new preference exposed by the filter menu:\n * 1. Add a field here with a sensible default in `initialState`.\n * 2. Add matching `setX`/`toggleX` actions below.\n * 3. Read/write through the store in `conversation-panel.tsx`.\n * No additional plumbing (storage keys, sanitization, etc.) is required —\n * `persist` handles migration of unknown fields gracefully.\n */\ninterface ConversationPanelPreferencesState {\n showOlderConversations: boolean;\n showRepoBranchMetadata: boolean;\n showLlmProfiles: boolean;\n showTagsMetadata: boolean;\n showHoverMetadata: boolean;\n organizeMode: OrganizeMode;\n conversationSort: ConversationSortField;\n threadScope: ThreadScope;\n automationFilterMode: AutomationFilterMode;\n selectedAutomationNames: string[];\n groupFolderOrder: string[];\n}\n\ninterface ConversationPanelPreferencesActions {\n setShowOlderConversations: (value: boolean) => void;\n toggleShowOlderConversations: () => void;\n setShowRepoBranchMetadata: (value: boolean) => void;\n toggleShowRepoBranchMetadata: () => void;\n setShowLlmProfiles: (value: boolean) => void;\n toggleShowLlmProfiles: () => void;\n setShowTagsMetadata: (value: boolean) => void;\n toggleShowTagsMetadata: () => void;\n setShowHoverMetadata: (value: boolean) => void;\n toggleShowHoverMetadata: () => void;\n setOrganizeMode: (value: OrganizeMode) => void;\n setConversationSort: (value: ConversationSortField) => void;\n setThreadScope: (value: ThreadScope) => void;\n setAutomationFilterMode: (value: AutomationFilterMode) => void;\n toggleAutomationName: (name: string) => void;\n setGroupFolderOrder: (order: readonly string[]) => void;\n}\n\ntype ConversationPanelPreferencesStore = ConversationPanelPreferencesState &\n ConversationPanelPreferencesActions;\n\nconst initialState: ConversationPanelPreferencesState = {\n showOlderConversations: true,\n showRepoBranchMetadata: false,\n showLlmProfiles: false,\n showTagsMetadata: false,\n showHoverMetadata: true,\n organizeMode: \"chronological\",\n conversationSort: \"updated\",\n threadScope: \"all\",\n automationFilterMode: \"all\",\n selectedAutomationNames: [],\n groupFolderOrder: [],\n};\n\nexport const useConversationPanelPreferencesStore =\n create<ConversationPanelPreferencesStore>()(\n persist(\n (set) => ({\n ...initialState,\n\n setShowOlderConversations: (value) =>\n set(() => ({ showOlderConversations: value })),\n toggleShowOlderConversations: () =>\n set((state) => ({\n showOlderConversations: !state.showOlderConversations,\n })),\n\n setShowRepoBranchMetadata: (value) =>\n set(() => ({ showRepoBranchMetadata: value })),\n toggleShowRepoBranchMetadata: () =>\n set((state) => ({\n showRepoBranchMetadata: !state.showRepoBranchMetadata,\n })),\n\n setShowLlmProfiles: (value) => set(() => ({ showLlmProfiles: value })),\n toggleShowLlmProfiles: () =>\n set((state) => ({\n showLlmProfiles: !state.showLlmProfiles,\n })),\n\n setShowTagsMetadata: (value) =>\n set(() => ({ showTagsMetadata: value })),\n toggleShowTagsMetadata: () =>\n set((state) => ({\n showTagsMetadata: !state.showTagsMetadata,\n })),\n\n setShowHoverMetadata: (value) =>\n set(() => ({ showHoverMetadata: value })),\n toggleShowHoverMetadata: () =>\n set((state) => ({\n showHoverMetadata: !state.showHoverMetadata,\n })),\n\n setOrganizeMode: (value) => set(() => ({ organizeMode: value })),\n setConversationSort: (value) =>\n set(() => ({ conversationSort: value })),\n setThreadScope: (value) => set(() => ({ threadScope: value })),\n setAutomationFilterMode: (value) =>\n set(() => ({ automationFilterMode: value })),\n toggleAutomationName: (name) =>\n set((state) => ({\n selectedAutomationNames: state.selectedAutomationNames.includes(\n name,\n )\n ? state.selectedAutomationNames.filter(\n (existing) => existing !== name,\n )\n : [...state.selectedAutomationNames, name],\n })),\n setGroupFolderOrder: (order) =>\n set(() => ({ groupFolderOrder: [...order] })),\n }),\n {\n name: \"conversation-panel-preferences\",\n storage: createJSONStorage(() => localStorage),\n // Only persist the data fields — actions are recreated on each load.\n partialize: (state): ConversationPanelPreferencesState => ({\n showOlderConversations: state.showOlderConversations,\n showRepoBranchMetadata: state.showRepoBranchMetadata,\n showLlmProfiles: state.showLlmProfiles,\n showTagsMetadata: state.showTagsMetadata,\n showHoverMetadata: state.showHoverMetadata,\n organizeMode: state.organizeMode,\n conversationSort: state.conversationSort,\n threadScope: state.threadScope,\n automationFilterMode: state.automationFilterMode,\n selectedAutomationNames: state.selectedAutomationNames,\n groupFolderOrder: state.groupFolderOrder,\n }),\n },\n ),\n );\n"],"mappings":";;;AA0DA,IAAM,IAAkD;CACtD,wBAAwB;CACxB,wBAAwB;CACxB,iBAAiB;CACjB,kBAAkB;CAClB,mBAAmB;CACnB,cAAc;CACd,kBAAkB;CAClB,aAAa;CACb,sBAAsB;CACtB,yBAAyB,CAAC;CAC1B,kBAAkB,CAAC;AACrB,GAEa,IACX,EAA0C,EACxC,GACG,OAAS;CACR,GAAG;CAEH,4BAA4B,MAC1B,SAAW,EAAE,wBAAwB,EAAM,EAAE;CAC/C,oCACE,GAAK,OAAW,EACd,wBAAwB,CAAC,EAAM,uBACjC,EAAE;CAEJ,4BAA4B,MAC1B,SAAW,EAAE,wBAAwB,EAAM,EAAE;CAC/C,oCACE,GAAK,OAAW,EACd,wBAAwB,CAAC,EAAM,uBACjC,EAAE;CAEJ,qBAAqB,MAAU,SAAW,EAAE,iBAAiB,EAAM,EAAE;CACrE,6BACE,GAAK,OAAW,EACd,iBAAiB,CAAC,EAAM,gBAC1B,EAAE;CAEJ,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,EAAM,EAAE;CACzC,8BACE,GAAK,OAAW,EACd,kBAAkB,CAAC,EAAM,iBAC3B,EAAE;CAEJ,uBAAuB,MACrB,SAAW,EAAE,mBAAmB,EAAM,EAAE;CAC1C,+BACE,GAAK,OAAW,EACd,mBAAmB,CAAC,EAAM,kBAC5B,EAAE;CAEJ,kBAAkB,MAAU,SAAW,EAAE,cAAc,EAAM,EAAE;CAC/D,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,EAAM,EAAE;CACzC,iBAAiB,MAAU,SAAW,EAAE,aAAa,EAAM,EAAE;CAC7D,0BAA0B,MACxB,SAAW,EAAE,sBAAsB,EAAM,EAAE;CAC7C,uBAAuB,MACrB,GAAK,OAAW,EACd,yBAAyB,EAAM,wBAAwB,SACrD,CACF,IACI,EAAM,wBAAwB,QAC3B,MAAa,MAAa,CAC7B,IACA,CAAC,GAAG,EAAM,yBAAyB,CAAI,EAC7C,EAAE;CACJ,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,CAAC,GAAG,CAAK,EAAE,EAAE;AAChD,IACA;CACE,MAAM;CACN,SAAS,QAAwB,YAAY;CAE7C,aAAa,OAA8C;EACzD,wBAAwB,EAAM;EAC9B,wBAAwB,EAAM;EAC9B,iBAAiB,EAAM;EACvB,kBAAkB,EAAM;EACxB,mBAAmB,EAAM;EACzB,cAAc,EAAM;EACpB,kBAAkB,EAAM;EACxB,aAAa,EAAM;EACnB,sBAAsB,EAAM;EAC5B,yBAAyB,EAAM;EAC/B,kBAAkB,EAAM;CAC1B;AACF,CACF,CACF"}
1
+ {"version":3,"file":"conversation-panel-preferences-store.js","names":[],"sources":["../../src/stores/conversation-panel-preferences-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\nimport {\n type AutomationFilterMode,\n type ConversationSortField,\n type OrganizeMode,\n type ThreadScope,\n} from \"#/components/features/conversation-panel/conversation-panel-list-helpers\";\n\n/**\n * User-toggleable display preferences for the sidebar conversation list\n * filter menu. These are intentionally persisted to localStorage (via the\n * same `zustand/persist` pattern used by `home-store` and `workspaces-store`)\n * so the menu state survives full reloads.\n *\n * To add a new preference exposed by the filter menu:\n * 1. Add a field here with a sensible default in `initialState`.\n * 2. Add matching `setX`/`toggleX` actions below.\n * 3. Read/write through the store in `conversation-panel.tsx`.\n * No additional plumbing (storage keys, sanitization, etc.) is required —\n * `persist` handles migration of unknown fields gracefully.\n */\ninterface ConversationPanelPreferencesState {\n showOlderConversations: boolean;\n showArchivedConversations: boolean;\n showRepoBranchMetadata: boolean;\n showLlmProfiles: boolean;\n showTagsMetadata: boolean;\n showHoverMetadata: boolean;\n organizeMode: OrganizeMode;\n conversationSort: ConversationSortField;\n threadScope: ThreadScope;\n automationFilterMode: AutomationFilterMode;\n selectedAutomationNames: string[];\n groupFolderOrder: string[];\n}\n\ninterface ConversationPanelPreferencesActions {\n setShowOlderConversations: (value: boolean) => void;\n toggleShowOlderConversations: () => void;\n setShowArchivedConversations: (value: boolean) => void;\n toggleShowArchivedConversations: () => void;\n setShowRepoBranchMetadata: (value: boolean) => void;\n toggleShowRepoBranchMetadata: () => void;\n setShowLlmProfiles: (value: boolean) => void;\n toggleShowLlmProfiles: () => void;\n setShowTagsMetadata: (value: boolean) => void;\n toggleShowTagsMetadata: () => void;\n setShowHoverMetadata: (value: boolean) => void;\n toggleShowHoverMetadata: () => void;\n setOrganizeMode: (value: OrganizeMode) => void;\n setConversationSort: (value: ConversationSortField) => void;\n setThreadScope: (value: ThreadScope) => void;\n setAutomationFilterMode: (value: AutomationFilterMode) => void;\n toggleAutomationName: (name: string) => void;\n setGroupFolderOrder: (order: readonly string[]) => void;\n}\n\ntype ConversationPanelPreferencesStore = ConversationPanelPreferencesState &\n ConversationPanelPreferencesActions;\n\nconst initialState: ConversationPanelPreferencesState = {\n showOlderConversations: true,\n showArchivedConversations: false,\n showRepoBranchMetadata: false,\n showLlmProfiles: false,\n showTagsMetadata: false,\n showHoverMetadata: true,\n organizeMode: \"chronological\",\n conversationSort: \"updated\",\n threadScope: \"all\",\n automationFilterMode: \"all\",\n selectedAutomationNames: [],\n groupFolderOrder: [],\n};\n\nexport const useConversationPanelPreferencesStore =\n create<ConversationPanelPreferencesStore>()(\n persist(\n (set) => ({\n ...initialState,\n\n setShowOlderConversations: (value) =>\n set(() => ({ showOlderConversations: value })),\n toggleShowOlderConversations: () =>\n set((state) => ({\n showOlderConversations: !state.showOlderConversations,\n })),\n\n setShowArchivedConversations: (value) =>\n set(() => ({ showArchivedConversations: value })),\n toggleShowArchivedConversations: () =>\n set((state) => ({\n showArchivedConversations: !state.showArchivedConversations,\n })),\n\n setShowRepoBranchMetadata: (value) =>\n set(() => ({ showRepoBranchMetadata: value })),\n toggleShowRepoBranchMetadata: () =>\n set((state) => ({\n showRepoBranchMetadata: !state.showRepoBranchMetadata,\n })),\n\n setShowLlmProfiles: (value) => set(() => ({ showLlmProfiles: value })),\n toggleShowLlmProfiles: () =>\n set((state) => ({\n showLlmProfiles: !state.showLlmProfiles,\n })),\n\n setShowTagsMetadata: (value) =>\n set(() => ({ showTagsMetadata: value })),\n toggleShowTagsMetadata: () =>\n set((state) => ({\n showTagsMetadata: !state.showTagsMetadata,\n })),\n\n setShowHoverMetadata: (value) =>\n set(() => ({ showHoverMetadata: value })),\n toggleShowHoverMetadata: () =>\n set((state) => ({\n showHoverMetadata: !state.showHoverMetadata,\n })),\n\n setOrganizeMode: (value) => set(() => ({ organizeMode: value })),\n setConversationSort: (value) =>\n set(() => ({ conversationSort: value })),\n setThreadScope: (value) => set(() => ({ threadScope: value })),\n setAutomationFilterMode: (value) =>\n set(() => ({ automationFilterMode: value })),\n toggleAutomationName: (name) =>\n set((state) => ({\n selectedAutomationNames: state.selectedAutomationNames.includes(\n name,\n )\n ? state.selectedAutomationNames.filter(\n (existing) => existing !== name,\n )\n : [...state.selectedAutomationNames, name],\n })),\n setGroupFolderOrder: (order) =>\n set(() => ({ groupFolderOrder: [...order] })),\n }),\n {\n name: \"conversation-panel-preferences\",\n storage: createJSONStorage(() => localStorage),\n // Only persist the data fields — actions are recreated on each load.\n partialize: (state): ConversationPanelPreferencesState => ({\n showOlderConversations: state.showOlderConversations,\n showArchivedConversations: state.showArchivedConversations,\n showRepoBranchMetadata: state.showRepoBranchMetadata,\n showLlmProfiles: state.showLlmProfiles,\n showTagsMetadata: state.showTagsMetadata,\n showHoverMetadata: state.showHoverMetadata,\n organizeMode: state.organizeMode,\n conversationSort: state.conversationSort,\n threadScope: state.threadScope,\n automationFilterMode: state.automationFilterMode,\n selectedAutomationNames: state.selectedAutomationNames,\n groupFolderOrder: state.groupFolderOrder,\n }),\n },\n ),\n );\n"],"mappings":";;;AA6DA,IAAM,IAAkD;CACtD,wBAAwB;CACxB,2BAA2B;CAC3B,wBAAwB;CACxB,iBAAiB;CACjB,kBAAkB;CAClB,mBAAmB;CACnB,cAAc;CACd,kBAAkB;CAClB,aAAa;CACb,sBAAsB;CACtB,yBAAyB,CAAC;CAC1B,kBAAkB,CAAC;AACrB,GAEa,IACX,EAA0C,EACxC,GACG,OAAS;CACR,GAAG;CAEH,4BAA4B,MAC1B,SAAW,EAAE,wBAAwB,EAAM,EAAE;CAC/C,oCACE,GAAK,OAAW,EACd,wBAAwB,CAAC,EAAM,uBACjC,EAAE;CAEJ,+BAA+B,MAC7B,SAAW,EAAE,2BAA2B,EAAM,EAAE;CAClD,uCACE,GAAK,OAAW,EACd,2BAA2B,CAAC,EAAM,0BACpC,EAAE;CAEJ,4BAA4B,MAC1B,SAAW,EAAE,wBAAwB,EAAM,EAAE;CAC/C,oCACE,GAAK,OAAW,EACd,wBAAwB,CAAC,EAAM,uBACjC,EAAE;CAEJ,qBAAqB,MAAU,SAAW,EAAE,iBAAiB,EAAM,EAAE;CACrE,6BACE,GAAK,OAAW,EACd,iBAAiB,CAAC,EAAM,gBAC1B,EAAE;CAEJ,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,EAAM,EAAE;CACzC,8BACE,GAAK,OAAW,EACd,kBAAkB,CAAC,EAAM,iBAC3B,EAAE;CAEJ,uBAAuB,MACrB,SAAW,EAAE,mBAAmB,EAAM,EAAE;CAC1C,+BACE,GAAK,OAAW,EACd,mBAAmB,CAAC,EAAM,kBAC5B,EAAE;CAEJ,kBAAkB,MAAU,SAAW,EAAE,cAAc,EAAM,EAAE;CAC/D,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,EAAM,EAAE;CACzC,iBAAiB,MAAU,SAAW,EAAE,aAAa,EAAM,EAAE;CAC7D,0BAA0B,MACxB,SAAW,EAAE,sBAAsB,EAAM,EAAE;CAC7C,uBAAuB,MACrB,GAAK,OAAW,EACd,yBAAyB,EAAM,wBAAwB,SACrD,CACF,IACI,EAAM,wBAAwB,QAC3B,MAAa,MAAa,CAC7B,IACA,CAAC,GAAG,EAAM,yBAAyB,CAAI,EAC7C,EAAE;CACJ,sBAAsB,MACpB,SAAW,EAAE,kBAAkB,CAAC,GAAG,CAAK,EAAE,EAAE;AAChD,IACA;CACE,MAAM;CACN,SAAS,QAAwB,YAAY;CAE7C,aAAa,OAA8C;EACzD,wBAAwB,EAAM;EAC9B,2BAA2B,EAAM;EACjC,wBAAwB,EAAM;EAC9B,iBAAiB,EAAM;EACvB,kBAAkB,EAAM;EACxB,mBAAmB,EAAM;EACzB,cAAc,EAAM;EACpB,kBAAkB,EAAM;EACxB,aAAa,EAAM;EACnB,sBAAsB,EAAM;EAC5B,yBAAyB,EAAM;EAC/B,kBAAkB,EAAM;CAC1B;AACF,CACF,CACF"}
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-store.cjs","names":[],"sources":["../../src/stores/conversation-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport {\n getConversationState,\n setConversationState,\n} from \"#/utils/conversation-local-storage\";\n\nexport type ConversationTab =\n | \"files\"\n | \"browser\"\n | \"terminal\"\n | \"planner\"\n | \"tasklist\";\n\nexport type ConversationMode = \"code\" | \"plan\";\n\nexport interface IMessageToSend {\n text: string;\n timestamp: number;\n}\n\ninterface ConversationState {\n isRightPanelShown: boolean;\n selectedTab: ConversationTab | null;\n images: File[];\n files: File[];\n /** Image file names (e.g. pasted screenshots) to send via file upload instead of vision embed. */\n imagesMarkedUploadAsFile: string[];\n /** Image file names attached in chat (controls per-image upload-as-file UI). */\n pastedImageNames: string[];\n loadingFiles: string[]; // File names currently being processed\n loadingImages: string[]; // Image names currently being processed\n messageToSend: IMessageToSend | null;\n /** One-shot restore request consumed by the chat input when empty. */\n messageRestoreIfEmpty: IMessageToSend | null;\n shouldShownAgentLoading: boolean;\n submittedMessage: string | null;\n shouldHideSuggestions: boolean; // New state to hide suggestions when input expands\n hasRightPanelToggled: boolean;\n planContent: string | null;\n conversationMode: ConversationMode;\n subConversationTaskId: string | null; // Task ID for sub-conversation creation\n}\n\ninterface ConversationActions {\n setIsRightPanelShown: (isRightPanelShown: boolean) => void;\n setSelectedTab: (selectedTab: ConversationTab | null) => void;\n setShouldShownAgentLoading: (shouldShownAgentLoading: boolean) => void;\n setShouldHideSuggestions: (shouldHideSuggestions: boolean) => void;\n addImages: (images: File[]) => void;\n addFiles: (files: File[]) => void;\n toggleImageUploadAsFile: (fileName: string) => void;\n markImagesAsPasted: (fileNames: string[]) => void;\n removeImage: (index: number) => void;\n removeFile: (index: number) => void;\n clearImages: () => void;\n clearFiles: () => void;\n clearAllFiles: () => void;\n addFileLoading: (fileName: string) => void;\n removeFileLoading: (fileName: string) => void;\n addImageLoading: (imageName: string) => void;\n removeImageLoading: (imageName: string) => void;\n clearAllLoading: () => void;\n setMessageToSend: (text: string) => void;\n clearMessageToSend: () => void;\n restoreMessageToInputIfEmpty: (text: string) => void;\n clearMessageRestoreIfEmpty: () => void;\n setSubmittedMessage: (message: string | null) => void;\n resetConversationState: () => void;\n setHasRightPanelToggled: (hasRightPanelToggled: boolean) => void;\n setConversationMode: (conversationMode: ConversationMode) => void;\n setSubConversationTaskId: (taskId: string | null) => void;\n setPlanContent: (planContent: string | null) => void;\n}\n\ntype ConversationStore = ConversationState & ConversationActions;\n\nconst getConversationIdFromLocation = (): string | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const match = window.location.pathname.match(/\\/conversations\\/([^/]+)/);\n return match ? match[1] : null;\n};\n\nconst getInitialConversationMode = (): ConversationMode => {\n if (typeof window === \"undefined\") {\n return \"code\";\n }\n\n const conversationId = getConversationIdFromLocation();\n if (!conversationId) {\n return \"code\";\n }\n\n const state = getConversationState(conversationId);\n return state.conversationMode;\n};\n\nexport const useConversationStore = create<ConversationStore>()(\n devtools(\n (set) => ({\n // Initial state.\n //\n // The right-side drawer (`isRightPanelShown` / `hasRightPanelToggled`)\n // is intentionally *session-only* state: it always starts closed on\n // app load (or on opening a fresh/existing conversation after a\n // restart), but it survives in-app navigation because the Zustand\n // store stays alive across React Router transitions. Persisting the\n // open/closed state in localStorage made the panel feel sticky in\n // a way users didn't expect — they want a clean, focused chat view\n // when they come back to the app and only want the panel back when\n // they themselves opened it during the current session.\n isRightPanelShown: false,\n selectedTab: \"files\" as ConversationTab,\n images: [],\n files: [],\n imagesMarkedUploadAsFile: [],\n pastedImageNames: [],\n loadingFiles: [],\n loadingImages: [],\n messageToSend: null,\n messageRestoreIfEmpty: null,\n shouldShownAgentLoading: false,\n submittedMessage: null,\n shouldHideSuggestions: false,\n hasRightPanelToggled: false,\n planContent: null,\n conversationMode: getInitialConversationMode(),\n subConversationTaskId: null,\n\n // Actions\n setIsRightPanelShown: (isRightPanelShown) =>\n set({ isRightPanelShown }, false, \"setIsRightPanelShown\"),\n\n setSelectedTab: (selectedTab) =>\n set({ selectedTab }, false, \"setSelectedTab\"),\n\n setShouldShownAgentLoading: (shouldShownAgentLoading) =>\n set({ shouldShownAgentLoading }, false, \"setShouldShownAgentLoading\"),\n\n setShouldHideSuggestions: (shouldHideSuggestions) =>\n set({ shouldHideSuggestions }, false, \"setShouldHideSuggestions\"),\n\n addImages: (images) =>\n set(\n (state) => ({ images: [...state.images, ...images] }),\n false,\n \"addImages\",\n ),\n\n addFiles: (files) =>\n set(\n (state) => ({ files: [...state.files, ...files] }),\n false,\n \"addFiles\",\n ),\n\n toggleImageUploadAsFile: (fileName) =>\n set(\n (state) => {\n const marked = new Set(state.imagesMarkedUploadAsFile);\n if (marked.has(fileName)) {\n marked.delete(fileName);\n } else {\n marked.add(fileName);\n }\n return { imagesMarkedUploadAsFile: [...marked] };\n },\n false,\n \"toggleImageUploadAsFile\",\n ),\n\n markImagesAsPasted: (fileNames) =>\n set(\n (state) => {\n const merged = new Set([...state.pastedImageNames, ...fileNames]);\n return { pastedImageNames: [...merged] };\n },\n false,\n \"markImagesAsPasted\",\n ),\n\n removeImage: (index) =>\n set(\n (state) => {\n const removed = state.images[index];\n const newImages = [...state.images];\n newImages.splice(index, 1);\n return {\n images: newImages,\n imagesMarkedUploadAsFile: removed\n ? state.imagesMarkedUploadAsFile.filter(\n (name) => name !== removed.name,\n )\n : state.imagesMarkedUploadAsFile,\n pastedImageNames: removed\n ? state.pastedImageNames.filter((name) => name !== removed.name)\n : state.pastedImageNames,\n };\n },\n false,\n \"removeImage\",\n ),\n\n removeFile: (index) =>\n set(\n (state) => {\n const newFiles = [...state.files];\n newFiles.splice(index, 1);\n return { files: newFiles };\n },\n false,\n \"removeFile\",\n ),\n\n clearImages: () => set({ images: [] }, false, \"clearImages\"),\n\n clearFiles: () => set({ files: [] }, false, \"clearFiles\"),\n\n clearAllFiles: () =>\n set(\n {\n images: [],\n files: [],\n imagesMarkedUploadAsFile: [],\n pastedImageNames: [],\n loadingFiles: [],\n loadingImages: [],\n },\n false,\n \"clearAllFiles\",\n ),\n\n addFileLoading: (fileName) =>\n set(\n (state) => {\n if (!state.loadingFiles.includes(fileName)) {\n return { loadingFiles: [...state.loadingFiles, fileName] };\n }\n return state;\n },\n false,\n \"addFileLoading\",\n ),\n\n removeFileLoading: (fileName) =>\n set(\n (state) => ({\n loadingFiles: state.loadingFiles.filter(\n (name) => name !== fileName,\n ),\n }),\n false,\n \"removeFileLoading\",\n ),\n\n addImageLoading: (imageName) =>\n set(\n (state) => {\n if (!state.loadingImages.includes(imageName)) {\n return { loadingImages: [...state.loadingImages, imageName] };\n }\n return state;\n },\n false,\n \"addImageLoading\",\n ),\n\n removeImageLoading: (imageName) =>\n set(\n (state) => ({\n loadingImages: state.loadingImages.filter(\n (name) => name !== imageName,\n ),\n }),\n false,\n \"removeImageLoading\",\n ),\n\n clearAllLoading: () =>\n set({ loadingFiles: [], loadingImages: [] }, false, \"clearAllLoading\"),\n\n setMessageToSend: (text) =>\n set(\n {\n messageToSend: {\n text,\n timestamp: Date.now(),\n },\n },\n false,\n \"setMessageToSend\",\n ),\n\n // One-shot consume: clear after the composer applies it, so a never-sent\n // value can't replay into another conversation's composer on remount.\n clearMessageToSend: () =>\n set({ messageToSend: null }, false, \"clearMessageToSend\"),\n\n restoreMessageToInputIfEmpty: (text) =>\n set(\n {\n messageRestoreIfEmpty: {\n text,\n timestamp: Date.now(),\n },\n },\n false,\n \"restoreMessageToInputIfEmpty\",\n ),\n\n clearMessageRestoreIfEmpty: () =>\n set(\n { messageRestoreIfEmpty: null },\n false,\n \"clearMessageRestoreIfEmpty\",\n ),\n\n setSubmittedMessage: (submittedMessage) =>\n set({ submittedMessage }, false, \"setSubmittedMessage\"),\n\n resetConversationState: () =>\n set(\n {\n shouldHideSuggestions: false,\n conversationMode: getInitialConversationMode(),\n subConversationTaskId: null,\n planContent: null,\n },\n false,\n \"resetConversationState\",\n ),\n\n setHasRightPanelToggled: (hasRightPanelToggled) =>\n set({ hasRightPanelToggled }, false, \"setHasRightPanelToggled\"),\n\n setConversationMode: (conversationMode) => {\n const conversationId = getConversationIdFromLocation();\n if (conversationId) {\n setConversationState(conversationId, { conversationMode });\n }\n set({ conversationMode }, false, \"setConversationMode\");\n },\n\n setSubConversationTaskId: (subConversationTaskId) =>\n set({ subConversationTaskId }, false, \"setSubConversationTaskId\"),\n\n setPlanContent: (planContent) =>\n set({ planContent }, false, \"setPlanContent\"),\n }),\n {\n name: \"conversation-store\",\n },\n ),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversation-store.cjs","names":[],"sources":["../../src/stores/conversation-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { devtools } from \"zustand/middleware\";\nimport {\n getConversationState,\n setConversationState,\n} from \"#/utils/conversation-local-storage\";\n\nexport type ConversationTab =\n | \"files\"\n | \"browser\"\n | \"terminal\"\n | \"planner\"\n | \"tasklist\"\n | \"usage\";\n\nexport type ConversationMode = \"code\" | \"plan\";\n\nexport interface IMessageToSend {\n text: string;\n timestamp: number;\n}\n\ninterface ConversationState {\n isRightPanelShown: boolean;\n selectedTab: ConversationTab | null;\n images: File[];\n files: File[];\n /** Image file names (e.g. pasted screenshots) to send via file upload instead of vision embed. */\n imagesMarkedUploadAsFile: string[];\n /** Image file names attached in chat (controls per-image upload-as-file UI). */\n pastedImageNames: string[];\n loadingFiles: string[]; // File names currently being processed\n loadingImages: string[]; // Image names currently being processed\n messageToSend: IMessageToSend | null;\n /** One-shot restore request consumed by the chat input when empty. */\n messageRestoreIfEmpty: IMessageToSend | null;\n shouldShownAgentLoading: boolean;\n submittedMessage: string | null;\n shouldHideSuggestions: boolean; // New state to hide suggestions when input expands\n hasRightPanelToggled: boolean;\n planContent: string | null;\n conversationMode: ConversationMode;\n subConversationTaskId: string | null; // Task ID for sub-conversation creation\n}\n\ninterface ConversationActions {\n setIsRightPanelShown: (isRightPanelShown: boolean) => void;\n setSelectedTab: (selectedTab: ConversationTab | null) => void;\n setShouldShownAgentLoading: (shouldShownAgentLoading: boolean) => void;\n setShouldHideSuggestions: (shouldHideSuggestions: boolean) => void;\n addImages: (images: File[]) => void;\n addFiles: (files: File[]) => void;\n toggleImageUploadAsFile: (fileName: string) => void;\n markImagesAsPasted: (fileNames: string[]) => void;\n removeImage: (index: number) => void;\n removeFile: (index: number) => void;\n clearImages: () => void;\n clearFiles: () => void;\n clearAllFiles: () => void;\n addFileLoading: (fileName: string) => void;\n removeFileLoading: (fileName: string) => void;\n addImageLoading: (imageName: string) => void;\n removeImageLoading: (imageName: string) => void;\n clearAllLoading: () => void;\n setMessageToSend: (text: string) => void;\n clearMessageToSend: () => void;\n restoreMessageToInputIfEmpty: (text: string) => void;\n clearMessageRestoreIfEmpty: () => void;\n setSubmittedMessage: (message: string | null) => void;\n resetConversationState: () => void;\n setHasRightPanelToggled: (hasRightPanelToggled: boolean) => void;\n setConversationMode: (conversationMode: ConversationMode) => void;\n setSubConversationTaskId: (taskId: string | null) => void;\n setPlanContent: (planContent: string | null) => void;\n}\n\ntype ConversationStore = ConversationState & ConversationActions;\n\nconst getConversationIdFromLocation = (): string | null => {\n if (typeof window === \"undefined\") {\n return null;\n }\n\n const match = window.location.pathname.match(/\\/conversations\\/([^/]+)/);\n return match ? match[1] : null;\n};\n\nconst getInitialConversationMode = (): ConversationMode => {\n if (typeof window === \"undefined\") {\n return \"code\";\n }\n\n const conversationId = getConversationIdFromLocation();\n if (!conversationId) {\n return \"code\";\n }\n\n const state = getConversationState(conversationId);\n return state.conversationMode;\n};\n\nexport const useConversationStore = create<ConversationStore>()(\n devtools(\n (set) => ({\n // Initial state.\n //\n // The right-side drawer (`isRightPanelShown` / `hasRightPanelToggled`)\n // is intentionally *session-only* state: it always starts closed on\n // app load (or on opening a fresh/existing conversation after a\n // restart), but it survives in-app navigation because the Zustand\n // store stays alive across React Router transitions. Persisting the\n // open/closed state in localStorage made the panel feel sticky in\n // a way users didn't expect — they want a clean, focused chat view\n // when they come back to the app and only want the panel back when\n // they themselves opened it during the current session.\n isRightPanelShown: false,\n selectedTab: \"files\" as ConversationTab,\n images: [],\n files: [],\n imagesMarkedUploadAsFile: [],\n pastedImageNames: [],\n loadingFiles: [],\n loadingImages: [],\n messageToSend: null,\n messageRestoreIfEmpty: null,\n shouldShownAgentLoading: false,\n submittedMessage: null,\n shouldHideSuggestions: false,\n hasRightPanelToggled: false,\n planContent: null,\n conversationMode: getInitialConversationMode(),\n subConversationTaskId: null,\n\n // Actions\n setIsRightPanelShown: (isRightPanelShown) =>\n set({ isRightPanelShown }, false, \"setIsRightPanelShown\"),\n\n setSelectedTab: (selectedTab) =>\n set({ selectedTab }, false, \"setSelectedTab\"),\n\n setShouldShownAgentLoading: (shouldShownAgentLoading) =>\n set({ shouldShownAgentLoading }, false, \"setShouldShownAgentLoading\"),\n\n setShouldHideSuggestions: (shouldHideSuggestions) =>\n set({ shouldHideSuggestions }, false, \"setShouldHideSuggestions\"),\n\n addImages: (images) =>\n set(\n (state) => ({ images: [...state.images, ...images] }),\n false,\n \"addImages\",\n ),\n\n addFiles: (files) =>\n set(\n (state) => ({ files: [...state.files, ...files] }),\n false,\n \"addFiles\",\n ),\n\n toggleImageUploadAsFile: (fileName) =>\n set(\n (state) => {\n const marked = new Set(state.imagesMarkedUploadAsFile);\n if (marked.has(fileName)) {\n marked.delete(fileName);\n } else {\n marked.add(fileName);\n }\n return { imagesMarkedUploadAsFile: [...marked] };\n },\n false,\n \"toggleImageUploadAsFile\",\n ),\n\n markImagesAsPasted: (fileNames) =>\n set(\n (state) => {\n const merged = new Set([...state.pastedImageNames, ...fileNames]);\n return { pastedImageNames: [...merged] };\n },\n false,\n \"markImagesAsPasted\",\n ),\n\n removeImage: (index) =>\n set(\n (state) => {\n const removed = state.images[index];\n const newImages = [...state.images];\n newImages.splice(index, 1);\n return {\n images: newImages,\n imagesMarkedUploadAsFile: removed\n ? state.imagesMarkedUploadAsFile.filter(\n (name) => name !== removed.name,\n )\n : state.imagesMarkedUploadAsFile,\n pastedImageNames: removed\n ? state.pastedImageNames.filter((name) => name !== removed.name)\n : state.pastedImageNames,\n };\n },\n false,\n \"removeImage\",\n ),\n\n removeFile: (index) =>\n set(\n (state) => {\n const newFiles = [...state.files];\n newFiles.splice(index, 1);\n return { files: newFiles };\n },\n false,\n \"removeFile\",\n ),\n\n clearImages: () => set({ images: [] }, false, \"clearImages\"),\n\n clearFiles: () => set({ files: [] }, false, \"clearFiles\"),\n\n clearAllFiles: () =>\n set(\n {\n images: [],\n files: [],\n imagesMarkedUploadAsFile: [],\n pastedImageNames: [],\n loadingFiles: [],\n loadingImages: [],\n },\n false,\n \"clearAllFiles\",\n ),\n\n addFileLoading: (fileName) =>\n set(\n (state) => {\n if (!state.loadingFiles.includes(fileName)) {\n return { loadingFiles: [...state.loadingFiles, fileName] };\n }\n return state;\n },\n false,\n \"addFileLoading\",\n ),\n\n removeFileLoading: (fileName) =>\n set(\n (state) => ({\n loadingFiles: state.loadingFiles.filter(\n (name) => name !== fileName,\n ),\n }),\n false,\n \"removeFileLoading\",\n ),\n\n addImageLoading: (imageName) =>\n set(\n (state) => {\n if (!state.loadingImages.includes(imageName)) {\n return { loadingImages: [...state.loadingImages, imageName] };\n }\n return state;\n },\n false,\n \"addImageLoading\",\n ),\n\n removeImageLoading: (imageName) =>\n set(\n (state) => ({\n loadingImages: state.loadingImages.filter(\n (name) => name !== imageName,\n ),\n }),\n false,\n \"removeImageLoading\",\n ),\n\n clearAllLoading: () =>\n set({ loadingFiles: [], loadingImages: [] }, false, \"clearAllLoading\"),\n\n setMessageToSend: (text) =>\n set(\n {\n messageToSend: {\n text,\n timestamp: Date.now(),\n },\n },\n false,\n \"setMessageToSend\",\n ),\n\n // One-shot consume: clear after the composer applies it, so a never-sent\n // value can't replay into another conversation's composer on remount.\n clearMessageToSend: () =>\n set({ messageToSend: null }, false, \"clearMessageToSend\"),\n\n restoreMessageToInputIfEmpty: (text) =>\n set(\n {\n messageRestoreIfEmpty: {\n text,\n timestamp: Date.now(),\n },\n },\n false,\n \"restoreMessageToInputIfEmpty\",\n ),\n\n clearMessageRestoreIfEmpty: () =>\n set(\n { messageRestoreIfEmpty: null },\n false,\n \"clearMessageRestoreIfEmpty\",\n ),\n\n setSubmittedMessage: (submittedMessage) =>\n set({ submittedMessage }, false, \"setSubmittedMessage\"),\n\n resetConversationState: () =>\n set(\n {\n shouldHideSuggestions: false,\n conversationMode: getInitialConversationMode(),\n subConversationTaskId: null,\n planContent: null,\n },\n false,\n \"resetConversationState\",\n ),\n\n setHasRightPanelToggled: (hasRightPanelToggled) =>\n set({ hasRightPanelToggled }, false, \"setHasRightPanelToggled\"),\n\n setConversationMode: (conversationMode) => {\n const conversationId = getConversationIdFromLocation();\n if (conversationId) {\n setConversationState(conversationId, { conversationMode });\n }\n set({ conversationMode }, false, \"setConversationMode\");\n },\n\n setSubConversationTaskId: (subConversationTaskId) =>\n set({ subConversationTaskId }, false, \"setSubConversationTaskId\"),\n\n setPlanContent: (planContent) =>\n set({ planContent }, false, \"setPlanContent\"),\n }),\n {\n name: \"conversation-store\",\n },\n ),\n);\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- export type ConversationTab = "files" | "browser" | "terminal" | "planner" | "tasklist";
1
+ export type ConversationTab = "files" | "browser" | "terminal" | "planner" | "tasklist" | "usage";
2
2
  export type ConversationMode = "code" | "plan";
3
3
  export interface IMessageToSend {
4
4
  text: string;