@openhands/agent-canvas 1.17.0 → 1.19.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 (601) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/acp-providers-BQ4o4vqy.js +1 -0
  4. package/build/assets/active-backend-context-BsLfwrXB.js +2 -0
  5. package/build/assets/{agent-context-settings-CJUrcBut.js → agent-context-settings-D9i6ezoJ.js} +1 -1
  6. package/build/assets/{agent-profiles-service.api-Biieml2L.js → agent-profiles-service.api-U-9YzDFU.js} +1 -1
  7. package/build/assets/agent-profiles-settings-D-bUV2PX.js +1 -0
  8. package/build/assets/agent-server-client-options-EHB23gyx.js +1 -0
  9. package/build/assets/agent-server-config-C_x8Otd9.js +1 -0
  10. package/build/assets/agent-server-conversation-service.api-BaVQ6NpE.js +88 -0
  11. package/build/assets/agent-settings-BZJHtaIK.js +2 -0
  12. package/build/assets/agent-settings-D4snDd6u.js +1 -0
  13. package/build/assets/alert-banner-BkVt_8Or.js +1 -0
  14. package/build/assets/{api-error-message-Ve79qDcN.js → api-error-message-BXo0lCI5.js} +1 -1
  15. package/build/assets/api-key-entry-screen-fAIld-n7.js +1 -0
  16. package/build/assets/app-settings-DITbrM2u.js +1 -0
  17. package/build/assets/automation-detail-DawHmaOl.js +3 -0
  18. package/build/assets/automation-disabled-reason-DtgC8Ppt.js +1 -0
  19. package/build/assets/{automation-export-DxY47m3T.js → automation-export-Kv6e0bHx.js} +1 -1
  20. package/build/assets/automation-git-sync-Dd98G_Oz.js +1 -0
  21. package/build/assets/automation-setup-route-Cdc109ZD.js +3 -0
  22. package/build/assets/{automation-templates-4C4PM8K7.js → automation-templates-Xhr-FDAB.js} +1 -1
  23. package/build/assets/automations-list-BeP1LuzQ.js +1 -0
  24. package/build/assets/{back-nav-button-B8tH0BEp.js → back-nav-button-DWNiN277.js} +1 -1
  25. package/build/assets/backend-form-modal-DeaUJIik.js +1 -0
  26. package/build/assets/backend-synced-settings-badge-Br85iWIY.js +1 -0
  27. package/build/assets/badge-Cdnei7Ly.js +1 -0
  28. package/build/assets/{base-modal-DTULovNc.js → base-modal-Ds7grR67.js} +1 -1
  29. package/build/assets/{bot-CLMxGFaq.js → bot-ByolqPEr.js} +1 -1
  30. package/build/assets/{brand-button-DxPO4Lf8.js → brand-button-mQq2Ld1I.js} +1 -1
  31. package/build/assets/browser-Bfum8fkE.js +46 -0
  32. package/build/assets/browser-tab-CmYT4j_r.js +1 -0
  33. package/build/assets/canvas-extension-page-CokKSVRG.js +1 -0
  34. package/build/assets/canvas-extensions-BoQEYG-2.js +1 -0
  35. package/build/assets/{canvas-extensions-runtime-DdnkgJXV.js → canvas-extensions-runtime-Du2M5iEX.js} +1 -1
  36. package/build/assets/{chat-send-button-B3SFSIg5.js → chat-send-button-DM3JgMc2.js} +1 -1
  37. package/build/assets/check-Bo8s5Ylg.js +1 -0
  38. package/build/assets/chevron-down-Cr6Fn_wS.js +1 -0
  39. package/build/assets/chevron-right-CfdfRn-8.js +1 -0
  40. package/build/assets/circle-check-5pH3qCji.js +1 -0
  41. package/build/assets/circle-plus-check-toggle-BbYBiUwv.js +1 -0
  42. package/build/assets/{cloud-funnel-analytics-BQanUEEC.js → cloud-funnel-analytics-D02kgq1s.js} +1 -1
  43. package/build/assets/{cog-VBlRo6iw.js → cog-Dy1eWbSZ.js} +1 -1
  44. package/build/assets/command-menu-mehU3Evb.js +1 -0
  45. package/build/assets/commits-tab-DhQW8CXi.js +1 -0
  46. package/build/assets/{condenser-settings-eVKFMvap.js → condenser-settings-eRk6Fkx6.js} +1 -1
  47. package/build/assets/confirmation-modal-BmVt_c0I.js +1 -0
  48. package/build/assets/{context-menu-list-item-DDROe12Z.js → context-menu-list-item-lx2zk9G7.js} +1 -1
  49. package/build/assets/{conversation-BnjJSFf5.js → conversation-BOj0ENI6.js} +8 -8
  50. package/build/assets/conversation-SouWlGvs.js +1 -0
  51. package/build/assets/conversation-panel-SouWlGvs.js +1 -0
  52. package/build/assets/conversation-state-store-C6YncQNo.js +1 -0
  53. package/build/assets/conversation-store-DC-JlJuq.js +6 -0
  54. package/build/assets/{conversation-tab-empty-state-DKlzAz5c.js → conversation-tab-empty-state-DIhwCC2J.js} +1 -1
  55. package/build/assets/copy-to-clipboard-button-DVNXLylP.js +1 -0
  56. package/build/assets/device-verify-LP-x7nra.js +1 -0
  57. package/build/assets/{dropdown-classes-B9kJVDIk.js → dropdown-classes-goDldkiu.js} +1 -1
  58. package/build/assets/edit-automation-modal-BJJR9wSW.js +1 -0
  59. package/build/assets/empty-state-Cd-TFe7i.js +1 -0
  60. package/build/assets/{entry.client-Ds8LVp-E.js → entry.client-CJdjIvMZ.js} +2 -2
  61. package/build/assets/enum-filter-dropdown-pocrz9AL.js +1 -0
  62. package/build/assets/environment-switch-overlay-Dwe78wRl.js +1 -0
  63. package/build/assets/error-state-SuWwztxV.js +1 -0
  64. package/build/assets/extensions-hub-O0esK3Er.js +1 -0
  65. package/build/assets/extensions-navigation-CgCVSXje.js +1 -0
  66. package/build/assets/{external-link-DQtv7eTh.js → external-link-Bd7Tga-1.js} +1 -1
  67. package/build/assets/files-tab-B_6t8ljw.js +1 -0
  68. package/build/assets/{form-control-classes-B-z85REi.js → form-control-classes-lQfLxzH4.js} +1 -1
  69. package/build/assets/format-model-name-Dfh9Udrx.js +1 -0
  70. package/build/assets/get-git-path-Bp5rsUkl.js +1 -0
  71. package/build/assets/git-repo-dropdown-CbD0_LPN.js +1 -0
  72. package/build/assets/git-repo-dropdown-DPQVumZg.js +1 -0
  73. package/build/assets/highlighted-source-view-Bp0sgK-f.js +1 -0
  74. package/build/assets/home-BghzJd-D.js +1 -0
  75. package/build/assets/home-CXZ8zawS.js +1 -0
  76. package/build/assets/home-automation-run-tooltip-BdpCckKO.js +1 -0
  77. package/build/assets/home-store-DNQpfIEO.js +1 -0
  78. package/build/assets/hooks-service-DkxnQa4N.js +1 -0
  79. package/build/assets/index-home-Ca6Q5CtT.js +1 -0
  80. package/build/assets/info-BcajzHn1.js +1 -0
  81. package/build/assets/install-server-modal-DvxxYKia.js +1 -0
  82. package/build/assets/{key-CfXbJa8w.js → key-CwTE6XJj.js} +1 -1
  83. package/build/assets/launch-jsRGPRBx.js +1 -0
  84. package/build/assets/link-external-B4gx3EeM.js +1 -0
  85. package/build/assets/list-filter-B8PQk3RA.js +1 -0
  86. package/build/assets/{list-todo-ByXMdY4W.js → list-todo-Caa8E1X_.js} +1 -1
  87. package/build/assets/llm-not-configured-banner-DMdL1vaX.js +27 -0
  88. package/build/assets/llm-settings-dkQ5Oq7W.js +1 -0
  89. package/build/assets/llm-settings-lNDerSlu.js +1 -0
  90. package/build/assets/{llm-subscription-service-aDNBQ7Gq.js → llm-subscription-service-BiQaZbvv.js} +1 -1
  91. package/build/assets/loader-circle-BxJgk73D.js +1 -0
  92. package/build/assets/{loading-spinner-DfHI77BA.js → loading-spinner-DK1sAwVg.js} +1 -1
  93. package/build/assets/manage-backends-modal-DqCJohsX.js +1 -0
  94. package/build/assets/manifest-ff93dc8c.js +1 -0
  95. package/build/assets/manifest-subpage-layout-sxuPqfUr.js +1 -0
  96. package/build/assets/{map-provider-DXvssjw9.js → map-provider-iRs80q_6.js} +1 -1
  97. package/build/assets/markdown-renderer-BH5pqDuJ.js +8 -0
  98. package/build/assets/mcp-BtaJDXnP.js +1 -0
  99. package/build/assets/mcp-installed-servers-Br4ZLsjr.js +1 -0
  100. package/build/assets/mcp-marketplace-utils-BwEnDYqm.js +1 -0
  101. package/build/assets/mcp-page-DwbnQpVs.js +9 -0
  102. package/build/assets/messages-DmFJlW6c.js +41 -0
  103. package/build/assets/{modal-body-DJw4xBki.js → modal-body-DBweM2EN.js} +1 -1
  104. package/build/assets/{modal-classes-Dg0ngBPO.js → modal-classes-jvG85nkU.js} +1 -1
  105. package/build/assets/modal-close-button-BrM5fNnn.js +1 -0
  106. package/build/assets/{mouse-pointer-click-DEhsSUpu.js → mouse-pointer-click-BXlRN92t.js} +1 -1
  107. package/build/assets/onboarding-CQ8T6T0U.js +1 -0
  108. package/build/assets/onboarding-modal-CnliiaSN.js +1 -0
  109. package/build/assets/{organization-service.api-Cyg1Tn03.js → organization-service.api-DmFUEmu5.js} +1 -1
  110. package/build/assets/parse-git-remote-url-BG8pcKL8.js +1 -0
  111. package/build/assets/{pencil-D5c47kyN.js → pencil-q-ihqDYv.js} +1 -1
  112. package/build/assets/{pin-C7vMagvf.js → pin-BdroVQdN.js} +1 -1
  113. package/build/assets/planner-tab-CXph_C9F.js +1 -0
  114. package/build/assets/{plug-CeaqeF6u.js → plug-7ACOEbMs.js} +1 -1
  115. package/build/assets/{plugins-management-service-Bh_pBuf-.js → plugins-management-service-CcEpfNKz.js} +1 -1
  116. package/build/assets/plus-C2e21TFC.js +1 -0
  117. package/build/assets/{profiles-service.api-BbOK0EkF.js → profiles-service.api-DdF_4Zhe.js} +1 -1
  118. package/build/assets/providers-BWRnl31s.js +1 -0
  119. package/build/assets/proxy-DbEf3Nqr.js +1 -0
  120. package/build/assets/{puzzle-DdAqbUrb.js → puzzle-LJYnuxKe.js} +1 -1
  121. package/build/assets/react-BX7IV650.js +1 -0
  122. package/build/assets/recommended-automations-launcher-r0oI91HN.js +1 -0
  123. package/build/assets/{refresh-cw-DEQdPCpH.js → refresh-cw-BtdC3T8P.js} +1 -1
  124. package/build/assets/root-j2fFRlWS.js +2 -0
  125. package/build/assets/root-layout-CfGgm2lo.js +2 -0
  126. package/build/assets/root-sEWn3ha1.css +1 -0
  127. package/build/assets/schema-field-c3Ncgm4x.js +1 -0
  128. package/build/assets/sdk-section-page-DFEiuiFQ.js +1 -0
  129. package/build/assets/secret-form-WXXNFqTM.js +1 -0
  130. package/build/assets/{secrets-service-Cr8g8xuv.js → secrets-service-DU6wh3wX.js} +1 -1
  131. package/build/assets/secrets-settings-BEqbPvjy.js +1 -0
  132. package/build/assets/settings-DjaSQwFh.js +1 -0
  133. package/build/assets/settings-dropdown-input-BRR2OZwi.js +1 -0
  134. package/build/assets/settings-index-DHv4XFcM.js +1 -0
  135. package/build/assets/settings-input-Cm4VxUon.js +1 -0
  136. package/build/assets/{settings-list-classes-Ds4jPBHK.js → settings-list-classes-CaUfKZ33.js} +1 -1
  137. package/build/assets/settings-modal-djXuAnBO.js +1 -0
  138. package/build/assets/settings-nav-DFb3hBMr.js +16 -0
  139. package/build/assets/{settings-service.api-9_xdv_mh.js → settings-service.api-Q9lKW1TX.js} +1 -1
  140. package/build/assets/settings-switch-seaSJc7t.js +1 -0
  141. package/build/assets/{settings-utils-8GIIgOQL.js → settings-utils-BTaaRhLZ.js} +1 -1
  142. package/build/assets/shared-conversation-x3rxVCon.js +1 -0
  143. package/build/assets/{shield-check-DO7wa_eu.js → shield-check-C3Rl0NF8.js} +1 -1
  144. package/build/assets/{sidebar-layout-EPfWxk11.js → sidebar-layout-B6mcvvdV.js} +1 -1
  145. package/build/assets/sidebar-mobile-menu-toggle-CEqS8KnA.js +1 -0
  146. package/build/assets/{sidebar-nav-link-_t4_Ef-9.js → sidebar-nav-link-BTHX11LG.js} +1 -1
  147. package/build/assets/{sidebar-onboarding-checklist-storage-DbDX-Wvj.js → sidebar-onboarding-checklist-storage-BP1kzn6p.js} +1 -1
  148. package/build/assets/sidebar-store-DAZVH-DR.js +1 -0
  149. package/build/assets/skill-card-pill-row-TD39nfP3.js +1 -0
  150. package/build/assets/{skill-filter-BBeZ4JEP.js → skill-filter-CdBbbwjm.js} +2 -2
  151. package/build/assets/{skill-icon-badge-BDhQ-tQ0.js → skill-icon-badge-vUEp7gsC.js} +1 -1
  152. package/build/assets/skill-scope-BTRBQt0-.js +1 -0
  153. package/build/assets/skills-plugins-k1tUNAw4.js +1 -0
  154. package/build/assets/skills-settings-CENviPT7.js +1 -0
  155. package/build/assets/{styled-tooltip-Pd22BIvr.js → styled-tooltip-DVAlWYnL.js} +1 -1
  156. package/build/assets/task-list-tab-BLKF_x9f.js +1 -0
  157. package/build/assets/{terminal-onXx0_Qz.js → terminal-Ckbnqb23.js} +1 -1
  158. package/build/assets/{toggle-switch-lwQtxDcm.js → toggle-switch-DeiG9Mf6.js} +1 -1
  159. package/build/assets/{trash-2-ZhKuTLiz.js → trash-2-Bb40zQzs.js} +1 -1
  160. package/build/assets/trash-CXN2R54L.js +1 -0
  161. package/build/assets/{triangle-alert-BajLPpQg.js → triangle-alert-DEdu-b_L.js} +1 -1
  162. package/build/assets/{typography-HLLg_FAn.js → typography-NarNyelM.js} +1 -1
  163. package/build/assets/usage-tab-Drd3O006.js +1 -0
  164. package/build/assets/use-acp-credential-form-CJ5wPsQm.js +1 -0
  165. package/build/assets/{use-activate-agent-profile-BLKML6vQ.js → use-activate-agent-profile-iQZ0U3qV.js} +1 -1
  166. package/build/assets/use-agent-profiles-LCvb5OXT.js +1 -0
  167. package/build/assets/{use-automation-health-BZkQNEA8.js → use-automation-health-DdgSa8sA.js} +1 -1
  168. package/build/assets/{use-automation-permissions-B_RpvL-s.js → use-automation-permissions-D3kEeieu.js} +1 -1
  169. package/build/assets/{use-automations-Cg0PDIZr.js → use-automations-CEubcYTD.js} +1 -1
  170. package/build/assets/{use-can-manage-org-profiles-BbN6KfDr.js → use-can-manage-org-profiles-CB8ybxFK.js} +1 -1
  171. package/build/assets/{use-canvas-extensions-CFUpHat6.js → use-canvas-extensions-BMacwi2v.js} +1 -1
  172. package/build/assets/{use-cloud-current-user-id-Dk0rjr63.js → use-cloud-current-user-id-BEKRd6XZ.js} +1 -1
  173. package/build/assets/{use-config-DpafU_QP.js → use-config-3WYcJsgC.js} +1 -1
  174. package/build/assets/{use-create-conversation-1YgmAHt-.js → use-create-conversation-Be-eVpFp.js} +1 -1
  175. package/build/assets/use-create-secret-Bk9ZxTjl.js +1 -0
  176. package/build/assets/use-free-models-Ci3FkKEm.js +1 -0
  177. package/build/assets/{use-get-secrets-CQ4b0qK8.js → use-get-secrets-fqS0kxt6.js} +1 -1
  178. package/build/assets/use-is-authed-A2Yinlt0.js +1 -0
  179. package/build/assets/{use-launch-skill-in-chat-DeYa1tFM.js → use-launch-skill-in-chat-QbXDhiHr.js} +1 -1
  180. package/build/assets/use-llm-configured-B71ZEVg2.js +1 -0
  181. package/build/assets/use-llm-profiles-xtCBMbef.js +1 -0
  182. package/build/assets/{use-manifest-capabilities-AOWGcu0j.js → use-manifest-capabilities-Bso6A_-A.js} +1 -1
  183. package/build/assets/{use-onboarding-completion-CoINurkm.js → use-onboarding-completion-CEgjL02y.js} +1 -1
  184. package/build/assets/{use-pinned-home-route-D6krwXjy.js → use-pinned-home-route-pK4IlJ-p.js} +1 -1
  185. package/build/assets/{use-plugins-marketplace-mLsIDhBv.js → use-plugins-marketplace-B8-ZEB34.js} +1 -1
  186. package/build/assets/use-save-agent-profile-D7hJuW_u.js +1 -0
  187. package/build/assets/{use-save-settings-3U5K9-8x.js → use-save-settings-CikTu5xV.js} +1 -1
  188. package/build/assets/{use-settings-Dn00UISA.js → use-settings-BWIH5EuN.js} +1 -1
  189. package/build/assets/use-settings-nav-items-DSKwLDnd.js +1 -0
  190. package/build/assets/use-tracking-ChnSFChv.js +3 -0
  191. package/build/assets/{use-user-conversation-ZWlXtbZb.js → use-user-conversation-C_l65DqW.js} +1 -1
  192. package/build/assets/{useInfiniteQuery-BzrRqjLg.js → useInfiniteQuery-CtSRG7uW.js} +1 -1
  193. package/build/assets/{useMutation-CJuK90oc.js → useMutation-Cszf8mKy.js} +1 -1
  194. package/build/assets/useQueries-pUohFMSP.js +1 -0
  195. package/build/assets/useQuery-DAAacS7O.js +1 -0
  196. package/build/assets/useTranslation-CCgUjPJF.js +1 -0
  197. package/build/assets/utils-D-gOfhmN.js +1 -0
  198. package/build/assets/{vendor~conversation-panel~conversation-4eJ0tml3.js → vendor~conversation-panel~conversation-C22sJuWZ.js} +1 -1
  199. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~b0x1wrp2-D6owle9R.js +2 -0
  200. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-BXXGJNDU.js +185 -0
  201. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-40UR63Gt.js +1 -0
  202. package/build/assets/{vendor~index-home~home~conversation-panel~conversation-CSFLzmTN.js → vendor~index-home~home~conversation-panel~conversation-UTl7gUo9.js} +1 -1
  203. package/build/assets/{vendor~root-layout-n8XCEXG8.js → vendor~root-layout-CcdIqs-C.js} +1 -1
  204. package/build/assets/{vendor~root-layout~conversation-panel~conversation-3Idg1_U3.js → vendor~root-layout~conversation-panel~conversation-C3EBV3kp.js} +1 -1
  205. package/build/assets/vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-BCe0JJVy.js +1 -0
  206. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation-DgQ564nC.js → vendor~root-layout~index-home~home~conversation-panel~conversation-BGGbPcYh.js} +1 -1
  207. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~inibp4qe-BFRW8wt-.js +48 -0
  208. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-BPjI-atc.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-3eJ4ExtV.js} +1 -1
  209. package/build/assets/{verification-settings-u2POabjH.js → verification-settings-PKtoIUUY.js} +1 -1
  210. package/build/assets/{wrench-CkgI4rxA.js → wrench-B8Wg_nbp.js} +1 -1
  211. package/build/assets/{x-By6qCmtY.js → x-CX8lVGwm.js} +1 -1
  212. package/build/index.html +4 -4
  213. package/build/locales/ar/openhands.json +16 -2
  214. package/build/locales/ca/openhands.json +16 -2
  215. package/build/locales/de/openhands.json +16 -2
  216. package/build/locales/en/openhands.json +16 -2
  217. package/build/locales/es/openhands.json +16 -2
  218. package/build/locales/fr/openhands.json +16 -2
  219. package/build/locales/it/openhands.json +16 -2
  220. package/build/locales/ja/openhands.json +16 -2
  221. package/build/locales/ko-KR/openhands.json +16 -2
  222. package/build/locales/no/openhands.json +16 -2
  223. package/build/locales/pt/openhands.json +16 -2
  224. package/build/locales/tr/openhands.json +16 -2
  225. package/build/locales/uk/openhands.json +16 -2
  226. package/build/locales/zh-CN/openhands.json +16 -2
  227. package/build/locales/zh-TW/openhands.json +16 -2
  228. package/config/defaults.json +3 -3
  229. package/dist/api/cloud/organization-service.api.cjs.map +1 -1
  230. package/dist/api/cloud/organization-service.api.d.ts +9 -1
  231. package/dist/api/cloud/organization-service.api.js.map +1 -1
  232. package/dist/api/config-service/config-service.api.cjs +1 -1
  233. package/dist/api/config-service/config-service.api.cjs.map +1 -1
  234. package/dist/api/config-service/config-service.api.js +6 -2
  235. package/dist/api/config-service/config-service.api.js.map +1 -1
  236. package/dist/api/config-service/config-service.types.d.ts +13 -0
  237. package/dist/components/conversation-events/chat/event-content-helpers/get-skill-ready-content.cjs +3 -3
  238. package/dist/components/conversation-events/chat/event-content-helpers/get-skill-ready-content.cjs.map +1 -1
  239. package/dist/components/conversation-events/chat/event-content-helpers/get-skill-ready-content.js +12 -16
  240. package/dist/components/conversation-events/chat/event-content-helpers/get-skill-ready-content.js.map +1 -1
  241. package/dist/components/features/automations/automation-card.cjs +1 -1
  242. package/dist/components/features/automations/automation-card.cjs.map +1 -1
  243. package/dist/components/features/automations/automation-card.js +39 -38
  244. package/dist/components/features/automations/automation-card.js.map +1 -1
  245. package/dist/components/features/automations/automation-list-row.cjs +1 -1
  246. package/dist/components/features/automations/automation-list-row.cjs.map +1 -1
  247. package/dist/components/features/automations/automation-list-row.js +73 -72
  248. package/dist/components/features/automations/automation-list-row.js.map +1 -1
  249. package/dist/components/features/automations/build-automation-menu-items.cjs +1 -1
  250. package/dist/components/features/automations/build-automation-menu-items.cjs.map +1 -1
  251. package/dist/components/features/automations/build-automation-menu-items.d.ts +3 -1
  252. package/dist/components/features/automations/build-automation-menu-items.js +12 -11
  253. package/dist/components/features/automations/build-automation-menu-items.js.map +1 -1
  254. package/dist/components/features/automations/detail/configuration-section.d.ts +7 -1
  255. package/dist/components/features/automations/detail/detail-header.d.ts +8 -5
  256. package/dist/components/features/automations/detail/disabled-reason-banner.d.ts +16 -0
  257. package/dist/components/features/backends/backend-selector.cjs +1 -1
  258. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  259. package/dist/components/features/backends/backend-selector.js +112 -112
  260. package/dist/components/features/backends/backend-selector.js.map +1 -1
  261. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs +1 -1
  262. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs.map +1 -1
  263. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js +62 -61
  264. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js.map +1 -1
  265. package/dist/components/features/chat/model-messages.cjs +1 -1
  266. package/dist/components/features/chat/model-messages.cjs.map +1 -1
  267. package/dist/components/features/chat/model-messages.js +27 -26
  268. package/dist/components/features/chat/model-messages.js.map +1 -1
  269. package/dist/components/features/conversation-panel/budget-display.cjs +1 -1
  270. package/dist/components/features/conversation-panel/budget-display.cjs.map +1 -1
  271. package/dist/components/features/conversation-panel/budget-display.d.ts +1 -2
  272. package/dist/components/features/conversation-panel/budget-display.js +12 -19
  273. package/dist/components/features/conversation-panel/budget-display.js.map +1 -1
  274. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -1
  275. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
  276. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +45 -44
  277. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
  278. package/dist/components/features/home/featured-automations/build-home-automation-menu-items.d.ts +1 -2
  279. package/dist/components/features/home/featured-automations/build-pinned-automation-menu-items.d.ts +0 -1
  280. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs +1 -1
  281. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs.map +1 -1
  282. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js +38 -29
  283. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js.map +1 -1
  284. package/dist/components/features/images/image-lightbox.cjs +2 -0
  285. package/dist/components/features/images/image-lightbox.cjs.map +1 -0
  286. package/dist/components/features/images/image-lightbox.d.ts +11 -0
  287. package/dist/components/features/images/image-lightbox.js +37 -0
  288. package/dist/components/features/images/image-lightbox.js.map +1 -0
  289. package/dist/components/features/images/image-preview.cjs +1 -1
  290. package/dist/components/features/images/image-preview.cjs.map +1 -1
  291. package/dist/components/features/images/image-preview.d.ts +2 -1
  292. package/dist/components/features/images/image-preview.js +33 -13
  293. package/dist/components/features/images/image-preview.js.map +1 -1
  294. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +3 -4
  295. package/dist/components/features/settings/agent-profiles/profile-scope-list.d.ts +20 -0
  296. package/dist/components/features/settings/cloud-settings-link.cjs +1 -1
  297. package/dist/components/features/settings/cloud-settings-link.cjs.map +1 -1
  298. package/dist/components/features/settings/cloud-settings-link.d.ts +3 -1
  299. package/dist/components/features/settings/cloud-settings-link.js +22 -21
  300. package/dist/components/features/settings/cloud-settings-link.js.map +1 -1
  301. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  302. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +2 -1
  303. package/dist/components/features/settings/llm-profiles/profile-row.cjs +1 -1
  304. package/dist/components/features/settings/llm-profiles/profile-row.js +1 -1
  305. package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
  306. package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
  307. package/dist/components/features/sidebar/sidebar-rail-body.d.ts +2 -1
  308. package/dist/components/features/sidebar/sidebar-rail-body.js +143 -142
  309. package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
  310. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  311. package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
  312. package/dist/components/features/sidebar/sidebar.js +28 -27
  313. package/dist/components/features/sidebar/sidebar.js.map +1 -1
  314. package/dist/components/providers/agent-server-ui-providers.cjs +1 -1
  315. package/dist/components/providers/agent-server-ui-providers.cjs.map +1 -1
  316. package/dist/components/providers/agent-server-ui-providers.js +33 -29
  317. package/dist/components/providers/agent-server-ui-providers.js.map +1 -1
  318. package/dist/components/shared/buttons/remove-button.cjs +1 -1
  319. package/dist/components/shared/buttons/remove-button.cjs.map +1 -1
  320. package/dist/components/shared/buttons/remove-button.d.ts +2 -1
  321. package/dist/components/shared/buttons/remove-button.js +2 -1
  322. package/dist/components/shared/buttons/remove-button.js.map +1 -1
  323. package/dist/components/shared/free-models-note.cjs +2 -0
  324. package/dist/components/shared/free-models-note.cjs.map +1 -0
  325. package/dist/components/shared/free-models-note.d.ts +3 -0
  326. package/dist/components/shared/free-models-note.js +20 -0
  327. package/dist/components/shared/free-models-note.js.map +1 -0
  328. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  329. package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
  330. package/dist/components/shared/modals/settings/model-selector.js +102 -112
  331. package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
  332. package/dist/config/defaults.cjs +1 -1
  333. package/dist/config/defaults.cjs.map +1 -1
  334. package/dist/config/defaults.js +3 -3
  335. package/dist/config/defaults.js.map +1 -1
  336. package/dist/constants/acp-providers.cjs +1 -1
  337. package/dist/constants/acp-providers.cjs.map +1 -1
  338. package/dist/constants/acp-providers.d.ts +7 -0
  339. package/dist/constants/acp-providers.js +20 -16
  340. package/dist/constants/acp-providers.js.map +1 -1
  341. package/dist/constants/profile-scope.d.ts +21 -0
  342. package/dist/hooks/chat/use-slash-command.cjs +1 -1
  343. package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
  344. package/dist/hooks/chat/use-slash-command.js +63 -62
  345. package/dist/hooks/chat/use-slash-command.js.map +1 -1
  346. package/dist/hooks/query/use-automation-sdk-version.cjs +1 -1
  347. package/dist/hooks/query/use-automation-sdk-version.cjs.map +1 -1
  348. package/dist/hooks/query/use-automation-sdk-version.js +3 -1
  349. package/dist/hooks/query/use-automation-sdk-version.js.map +1 -1
  350. package/dist/hooks/query/use-free-models.cjs +2 -0
  351. package/dist/hooks/query/use-free-models.cjs.map +1 -0
  352. package/dist/hooks/query/use-free-models.d.ts +23 -0
  353. package/dist/hooks/query/use-free-models.js +67 -0
  354. package/dist/hooks/query/use-free-models.js.map +1 -0
  355. package/dist/hooks/query/use-local-git-info.cjs.map +1 -1
  356. package/dist/hooks/query/use-local-git-info.js.map +1 -1
  357. package/dist/hooks/query/use-provider-models.cjs +1 -1
  358. package/dist/hooks/query/use-provider-models.cjs.map +1 -1
  359. package/dist/hooks/query/use-provider-models.js +34 -25
  360. package/dist/hooks/query/use-provider-models.js.map +1 -1
  361. package/dist/hooks/query/use-search-providers.cjs +1 -1
  362. package/dist/hooks/query/use-search-providers.cjs.map +1 -1
  363. package/dist/hooks/query/use-search-providers.js +31 -19
  364. package/dist/hooks/query/use-search-providers.js.map +1 -1
  365. package/dist/hooks/use-drag-resize.cjs +1 -1
  366. package/dist/hooks/use-drag-resize.cjs.map +1 -1
  367. package/dist/hooks/use-drag-resize.js +8 -9
  368. package/dist/hooks/use-drag-resize.js.map +1 -1
  369. package/dist/hooks/use-home-automation-actions.cjs +1 -1
  370. package/dist/hooks/use-home-automation-actions.cjs.map +1 -1
  371. package/dist/hooks/use-home-automation-actions.d.ts +1 -2
  372. package/dist/hooks/use-home-automation-actions.js +0 -1
  373. package/dist/hooks/use-home-automation-actions.js.map +1 -1
  374. package/dist/i18n/declaration.cjs +1 -1
  375. package/dist/i18n/declaration.cjs.map +1 -1
  376. package/dist/i18n/declaration.d.ts +16 -2
  377. package/dist/i18n/declaration.js +1 -1
  378. package/dist/i18n/declaration.js.map +1 -1
  379. package/dist/i18n/translation.cjs +3 -3
  380. package/dist/i18n/translation.cjs.map +1 -1
  381. package/dist/i18n/translation.js +255 -17
  382. package/dist/i18n/translation.js.map +1 -1
  383. package/dist/locales/ar/openhands.json +16 -2
  384. package/dist/locales/ca/openhands.json +16 -2
  385. package/dist/locales/de/openhands.json +16 -2
  386. package/dist/locales/en/openhands.json +16 -2
  387. package/dist/locales/es/openhands.json +16 -2
  388. package/dist/locales/fr/openhands.json +16 -2
  389. package/dist/locales/it/openhands.json +16 -2
  390. package/dist/locales/ja/openhands.json +16 -2
  391. package/dist/locales/ko-KR/openhands.json +16 -2
  392. package/dist/locales/no/openhands.json +16 -2
  393. package/dist/locales/pt/openhands.json +16 -2
  394. package/dist/locales/tr/openhands.json +16 -2
  395. package/dist/locales/uk/openhands.json +16 -2
  396. package/dist/locales/zh-CN/openhands.json +16 -2
  397. package/dist/locales/zh-TW/openhands.json +16 -2
  398. package/dist/manifests/manifest-validation.cjs +1 -1
  399. package/dist/manifests/manifest-validation.cjs.map +1 -1
  400. package/dist/manifests/manifest-validation.js +2 -2
  401. package/dist/manifests/manifest-validation.js.map +1 -1
  402. package/dist/node_modules/@openhands/typescript-client/dist/client/agent-server-compatibility.cjs +1 -1
  403. package/dist/node_modules/@openhands/typescript-client/dist/client/agent-server-compatibility.cjs.map +1 -1
  404. package/dist/node_modules/@openhands/typescript-client/dist/client/agent-server-compatibility.js +15 -8
  405. package/dist/node_modules/@openhands/typescript-client/dist/client/agent-server-compatibility.js.map +1 -1
  406. package/dist/node_modules/@openhands/typescript-client/dist/client/bash-client.cjs +1 -1
  407. package/dist/node_modules/@openhands/typescript-client/dist/client/bash-client.cjs.map +1 -1
  408. package/dist/node_modules/@openhands/typescript-client/dist/client/bash-client.js +11 -13
  409. package/dist/node_modules/@openhands/typescript-client/dist/client/bash-client.js.map +1 -1
  410. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.cjs +1 -1
  411. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.cjs.map +1 -1
  412. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.js +6 -0
  413. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.js.map +1 -1
  414. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs +1 -1
  415. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs.map +1 -1
  416. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js +3 -6
  417. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js.map +1 -1
  418. package/dist/node_modules/@openhands/typescript-client/dist/client/file-client.cjs +1 -1
  419. package/dist/node_modules/@openhands/typescript-client/dist/client/file-client.cjs.map +1 -1
  420. package/dist/node_modules/@openhands/typescript-client/dist/client/file-client.js +9 -12
  421. package/dist/node_modules/@openhands/typescript-client/dist/client/file-client.js.map +1 -1
  422. package/dist/node_modules/@openhands/typescript-client/dist/client/runtime-transport.cjs +2 -0
  423. package/dist/node_modules/@openhands/typescript-client/dist/client/runtime-transport.cjs.map +1 -0
  424. package/dist/node_modules/@openhands/typescript-client/dist/client/runtime-transport.js +41 -0
  425. package/dist/node_modules/@openhands/typescript-client/dist/client/runtime-transport.js.map +1 -0
  426. package/dist/node_modules/@openhands/typescript-client/dist/client/vscode-client.cjs +1 -1
  427. package/dist/node_modules/@openhands/typescript-client/dist/client/vscode-client.cjs.map +1 -1
  428. package/dist/node_modules/@openhands/typescript-client/dist/client/vscode-client.js +3 -6
  429. package/dist/node_modules/@openhands/typescript-client/dist/client/vscode-client.js.map +1 -1
  430. package/dist/node_modules/@openhands/typescript-client/dist/clients.cjs +1 -1
  431. package/dist/node_modules/@openhands/typescript-client/dist/clients.js +1 -1
  432. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  433. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  434. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +199 -61
  435. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  436. package/dist/node_modules/@openhands/typescript-client/dist/workspace/remote-workspace.cjs +1 -1
  437. package/dist/node_modules/@openhands/typescript-client/dist/workspace/remote-workspace.cjs.map +1 -1
  438. package/dist/node_modules/@openhands/typescript-client/dist/workspace/remote-workspace.js +9 -8
  439. package/dist/node_modules/@openhands/typescript-client/dist/workspace/remote-workspace.js.map +1 -1
  440. package/dist/package.cjs +1 -1
  441. package/dist/package.cjs.map +1 -1
  442. package/dist/package.js +2 -2
  443. package/dist/package.js.map +1 -1
  444. package/dist/routes/agent-settings.d.ts +5 -0
  445. package/dist/routes/app-settings.cjs +1 -1
  446. package/dist/routes/app-settings.cjs.map +1 -1
  447. package/dist/routes/app-settings.js +52 -47
  448. package/dist/routes/app-settings.js.map +1 -1
  449. package/dist/routes/llm-settings.cjs +1 -1
  450. package/dist/routes/llm-settings.cjs.map +1 -1
  451. package/dist/routes/llm-settings.js +137 -145
  452. package/dist/routes/llm-settings.js.map +1 -1
  453. package/dist/stores/free-models-store.cjs +2 -0
  454. package/dist/stores/free-models-store.cjs.map +1 -0
  455. package/dist/stores/free-models-store.d.ts +30 -0
  456. package/dist/stores/free-models-store.js +22 -0
  457. package/dist/stores/free-models-store.js.map +1 -0
  458. package/dist/types/automation.cjs.map +1 -1
  459. package/dist/types/automation.d.ts +23 -1
  460. package/dist/types/automation.js.map +1 -1
  461. package/dist/utils/automation-disabled-reason.cjs +2 -0
  462. package/dist/utils/automation-disabled-reason.cjs.map +1 -0
  463. package/dist/utils/automation-disabled-reason.d.ts +32 -0
  464. package/dist/utils/automation-disabled-reason.js +19 -0
  465. package/dist/utils/automation-disabled-reason.js.map +1 -0
  466. package/dist/utils/format-model-name.cjs +1 -1
  467. package/dist/utils/format-model-name.cjs.map +1 -1
  468. package/dist/utils/format-model-name.d.ts +27 -6
  469. package/dist/utils/format-model-name.js +14 -6
  470. package/dist/utils/format-model-name.js.map +1 -1
  471. package/dist/utils/parse-git-remote-url.cjs +1 -1
  472. package/dist/utils/parse-git-remote-url.cjs.map +1 -1
  473. package/dist/utils/parse-git-remote-url.d.ts +5 -1
  474. package/dist/utils/parse-git-remote-url.js +20 -13
  475. package/dist/utils/parse-git-remote-url.js.map +1 -1
  476. package/package.json +2 -2
  477. package/scripts/check-sdk-version-sync.mjs +79 -8
  478. package/scripts/dev-safe.mjs +1 -1
  479. package/scripts/static-server.mjs +24 -7
  480. package/build/assets/acp-providers-DrjNsPOH.js +0 -1
  481. package/build/assets/active-backend-context-rZiLJRUc.js +0 -2
  482. package/build/assets/agent-profiles-settings-x6RvPsq7.js +0 -1
  483. package/build/assets/agent-server-client-options-BikiNwTa.js +0 -1
  484. package/build/assets/agent-server-conversation-service.api-DYFsNHKf.js +0 -88
  485. package/build/assets/agent-settings-BJyovpJ_.js +0 -1
  486. package/build/assets/agent-settings-CpY3zObk.js +0 -2
  487. package/build/assets/alert-banner-vARz1idS.js +0 -1
  488. package/build/assets/api-key-entry-screen-CsMuBhoP.js +0 -1
  489. package/build/assets/app-settings-BaJ7nfHt.js +0 -1
  490. package/build/assets/automation-detail-CVoGDrKq.js +0 -3
  491. package/build/assets/automation-git-sync-CRMP9Bbm.js +0 -1
  492. package/build/assets/automation-setup-route-072beLz1.js +0 -3
  493. package/build/assets/automations-list-xuqTSQL4.js +0 -1
  494. package/build/assets/backend-form-modal-0aJmWdsa.js +0 -1
  495. package/build/assets/backend-synced-settings-badge-B7rKWBir.js +0 -1
  496. package/build/assets/badge-CcGx2TaV.js +0 -1
  497. package/build/assets/browser-C-_qBasq.js +0 -46
  498. package/build/assets/browser-tab-DZanIw5m.js +0 -1
  499. package/build/assets/canvas-extension-page-o_cWgE9B.js +0 -1
  500. package/build/assets/canvas-extensions-DSgNlCha.js +0 -1
  501. package/build/assets/check-fDEfgSzj.js +0 -1
  502. package/build/assets/chevron-down-CFrWHqu8.js +0 -1
  503. package/build/assets/chevron-right-C5roOrJu.js +0 -1
  504. package/build/assets/circle-check-ClTzJTdb.js +0 -1
  505. package/build/assets/circle-plus-check-toggle-Fr-h-mbL.js +0 -1
  506. package/build/assets/command-menu-BT3Ia5Ru.js +0 -1
  507. package/build/assets/commits-tab-2jhtaohl.js +0 -1
  508. package/build/assets/confirmation-modal-gIFHeZt6.js +0 -1
  509. package/build/assets/conversation-DZ578rOX.js +0 -1
  510. package/build/assets/conversation-panel-DZ578rOX.js +0 -1
  511. package/build/assets/conversation-state-store-D1_G_9I-.js +0 -1
  512. package/build/assets/conversation-store-DVVfkCsk.js +0 -6
  513. package/build/assets/copy-to-clipboard-button-B3w0X7WH.js +0 -1
  514. package/build/assets/device-verify-BfkCzyFB.js +0 -1
  515. package/build/assets/edit-automation-modal-BwhJ39JT.js +0 -1
  516. package/build/assets/empty-state-RZz0OfnY.js +0 -1
  517. package/build/assets/enum-filter-dropdown-C4pW0ORd.js +0 -1
  518. package/build/assets/environment-switch-overlay-P2ZAuzov.js +0 -1
  519. package/build/assets/error-state-B_5cgNPJ.js +0 -1
  520. package/build/assets/extensions-hub-DsaUr4ca.js +0 -1
  521. package/build/assets/extensions-navigation-CRMZd-FE.js +0 -1
  522. package/build/assets/files-tab-CCgAcVdc.js +0 -1
  523. package/build/assets/format-model-name-CasAP3nd.js +0 -1
  524. package/build/assets/get-git-path-EHrSDxTM.js +0 -1
  525. package/build/assets/git-repo-dropdown-D2WEQ97X.js +0 -1
  526. package/build/assets/git-repo-dropdown-DVeCUUXG.js +0 -1
  527. package/build/assets/highlighted-source-view-DjHO3SdW.js +0 -1
  528. package/build/assets/home-BorS_vdS.js +0 -1
  529. package/build/assets/home-aIMbHw5M.js +0 -1
  530. package/build/assets/home-automation-run-tooltip-CpHNFiFl.js +0 -1
  531. package/build/assets/home-store-DyXz0w6d.js +0 -1
  532. package/build/assets/hooks-service-C6dpRtZM.js +0 -1
  533. package/build/assets/index-home-zKFuKK2B.js +0 -1
  534. package/build/assets/info-CTIdjttz.js +0 -1
  535. package/build/assets/install-server-modal-DUiZMc8u.js +0 -1
  536. package/build/assets/kebab-menu-item-content-mm04SBD3.js +0 -1
  537. package/build/assets/launch-BSIf9KEg.js +0 -1
  538. package/build/assets/link-external-BNZzCM1I.js +0 -1
  539. package/build/assets/list-filter-CNpqTWVY.js +0 -1
  540. package/build/assets/llm-not-configured-banner-z_QRSeV0.js +0 -27
  541. package/build/assets/llm-settings-B-5AHMiX.js +0 -1
  542. package/build/assets/llm-settings-B3SjHDmA.js +0 -1
  543. package/build/assets/loader-circle-Dex47vVW.js +0 -1
  544. package/build/assets/manage-backends-modal-q1NdFisr.js +0 -1
  545. package/build/assets/manifest-8920b615.js +0 -1
  546. package/build/assets/manifest-subpage-layout-BQQy5ipR.js +0 -1
  547. package/build/assets/markdown-renderer-rivLvYh-.js +0 -8
  548. package/build/assets/mcp-CEXDhQWn.js +0 -1
  549. package/build/assets/mcp-marketplace-utils-Cn_NIMWM.js +0 -1
  550. package/build/assets/mcp-page-LGYIICMG.js +0 -9
  551. package/build/assets/messages-DUSeANAh.js +0 -41
  552. package/build/assets/modal-close-button-B9j4nasm.js +0 -1
  553. package/build/assets/onboarding-BjFebfs5.js +0 -1
  554. package/build/assets/onboarding-modal-DDTjOpyd.js +0 -1
  555. package/build/assets/parse-git-remote-url-DpDM9bK6.js +0 -1
  556. package/build/assets/planner-tab-D8f2Y3rn.js +0 -1
  557. package/build/assets/plus-B3wvU06-.js +0 -1
  558. package/build/assets/providers-2gIIkz2N.js +0 -1
  559. package/build/assets/proxy-CBgMd2zf.js +0 -1
  560. package/build/assets/recommended-automations-launcher-YLAQTp4s.js +0 -1
  561. package/build/assets/root-Bye_XmEL.js +0 -2
  562. package/build/assets/root-Cnwwzgo0.css +0 -1
  563. package/build/assets/root-layout-CSAwxBGM.js +0 -2
  564. package/build/assets/schema-field-CK0xYNbO.js +0 -1
  565. package/build/assets/sdk-section-page-Da73Rs_o.js +0 -1
  566. package/build/assets/secret-form-CB2VwYYg.js +0 -1
  567. package/build/assets/secrets-settings-Bh0udeKv.js +0 -1
  568. package/build/assets/settings-DmyIgRxO.js +0 -1
  569. package/build/assets/settings-dropdown-input-DVpyD87C.js +0 -1
  570. package/build/assets/settings-index-BSJUFmjE.js +0 -1
  571. package/build/assets/settings-input-C0dd8Utu.js +0 -1
  572. package/build/assets/settings-modal-yStgEh2o.js +0 -1
  573. package/build/assets/settings-switch-B78Khmyz.js +0 -1
  574. package/build/assets/shared-conversation-DM96X4KV.js +0 -1
  575. package/build/assets/sidebar-mobile-menu-toggle-D5GR5xT9.js +0 -1
  576. package/build/assets/sidebar-store-CSDFK0W5.js +0 -1
  577. package/build/assets/skill-card-pill-row-989y15jV.js +0 -1
  578. package/build/assets/skill-scope-Dz3kPH3P.js +0 -1
  579. package/build/assets/skills-plugins-e3CYGdUJ.js +0 -1
  580. package/build/assets/skills-settings-3e-FuHw_.js +0 -1
  581. package/build/assets/task-list-tab-3Jg8ywch.js +0 -1
  582. package/build/assets/trash-B6OIe4yX.js +0 -1
  583. package/build/assets/usage-tab-Cm-9j3UK.js +0 -1
  584. package/build/assets/use-acp-credential-form-BARcvjYE.js +0 -1
  585. package/build/assets/use-agent-profiles-1P91KSXz.js +0 -1
  586. package/build/assets/use-create-secret-D8p9ybQj.js +0 -1
  587. package/build/assets/use-is-authed-BRPjCRKM.js +0 -1
  588. package/build/assets/use-llm-configured-DGm8FiRR.js +0 -1
  589. package/build/assets/use-llm-profiles-BTTtpW4z.js +0 -1
  590. package/build/assets/use-save-agent-profile-CxyO9YG-.js +0 -1
  591. package/build/assets/use-settings-nav-items-vrjxTqcu.js +0 -16
  592. package/build/assets/use-tracking-B9prBsrq.js +0 -3
  593. package/build/assets/useQueries-CykmpT-a.js +0 -1
  594. package/build/assets/useQuery-S6j94er1.js +0 -1
  595. package/build/assets/utils-BOco8mxR.js +0 -1
  596. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~b0x1wrp2-Z0lly3FE.js +0 -2
  597. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-BfkNGovw.js +0 -185
  598. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-DnDoaYNi.js +0 -1
  599. package/build/assets/vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-DkPlXWl9.js +0 -1
  600. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~inibp4qe-CnsDDzeE.js +0 -48
  601. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iyia17eu-CV850oio.js +0 -1
@@ -1,20 +1,21 @@
1
1
  import { BUILT_IN_COMMANDS as e, MODEL_COMMAND as t } from "../../utils/constants.js";
2
2
  import { useActiveBackend as n } from "../../contexts/active-backend-context.js";
3
- import { formatModelNameForDisplay as r } from "../../utils/format-model-name.js";
4
- import { useConversationSkills as i } from "../query/use-conversation-skills.js";
5
- import { useLlmProfiles as a } from "../query/use-llm-profiles.js";
6
- import { useCallback as o, useEffect as s, useMemo as c, useRef as l, useState as u } from "react";
3
+ import { useFreeModels as r } from "../query/use-free-models.js";
4
+ import { formatModelNameForDisplay as i } from "../../utils/format-model-name.js";
5
+ import { useConversationSkills as a } from "../query/use-conversation-skills.js";
6
+ import { useLlmProfiles as o } from "../query/use-llm-profiles.js";
7
+ import { useCallback as s, useEffect as c, useMemo as l, useRef as u, useState as d } from "react";
7
8
  //#region src/hooks/chat/use-slash-command.ts
8
- function d(e) {
9
+ function f(e) {
9
10
  let t = window.getSelection();
10
11
  if (!t || t.rangeCount === 0) return -1;
11
12
  let n = t.getRangeAt(0), r = n.cloneRange();
12
13
  return r.selectNodeContents(e), r.setEnd(n.startContainer, n.startOffset), r.toString().length;
13
14
  }
14
- var f = (f) => {
15
- let { data: p, isLoading: m } = i(), h = n().backend.kind === "cloud", { data: g, isLoading: _ } = a(), [v, y] = u(!1), [b, x] = u(""), [S, C] = u("command"), [w, T] = u(0), E = c(() => {
16
- let t = e.filter((e) => e.command === "/new" ? h : !0);
17
- return m || !p || p.forEach((e) => {
15
+ var p = (p) => {
16
+ let { data: m, isLoading: h } = a(), g = n().backend.kind === "cloud", { data: _, isLoading: v } = o(), y = r(), [b, x] = d(!1), [S, C] = d(""), [w, T] = d("command"), [E, D] = d(0), O = l(() => {
17
+ let t = e.filter((e) => e.command === "/new" ? g : !0);
18
+ return h || !m || m.forEach((e) => {
18
19
  let n = (e.triggers || []).filter((e) => e.startsWith("/"));
19
20
  n.length > 0 ? n.forEach((n) => {
20
21
  t.push({
@@ -27,10 +28,10 @@ var f = (f) => {
27
28
  });
28
29
  }), t;
29
30
  }, [
30
- p,
31
31
  m,
32
- h
33
- ]), D = c(() => (g?.profiles ?? []).map((e) => {
32
+ h,
33
+ g
34
+ ]), k = l(() => (_?.profiles ?? []).map((e) => {
34
35
  let n = `${t} ${e.name}`;
35
36
  return {
36
37
  command: n,
@@ -38,32 +39,32 @@ var f = (f) => {
38
39
  name: e.name,
39
40
  type: "agentskills",
40
41
  source: null,
41
- content: e.model ? `Switch to ${r(e.model)}` : "Switch to this LLM profile",
42
+ content: e.model ? `Switch to ${i(e.model, y)}` : "Switch to this LLM profile",
42
43
  triggers: [n]
43
44
  }
44
45
  };
45
- }), [g?.profiles]), O = c(() => {
46
- let e = S === "model-profile" ? D : E;
47
- if (!b) return e;
48
- let t = b.toLowerCase();
46
+ }), [_?.profiles, y]), A = l(() => {
47
+ let e = w === "model-profile" ? k : O;
48
+ if (!S) return e;
49
+ let t = S.toLowerCase();
49
50
  return e.filter((e) => e.command.toLowerCase().includes(t) || e.skill.name.toLowerCase().includes(t) || e.skill.content?.toLowerCase().includes(t));
50
51
  }, [
51
- S,
52
- D,
53
- E,
54
- b
55
- ]), k = l(v);
56
- k.current = v;
57
- let A = l(O);
58
- A.current = O;
59
- let j = l(w);
60
- j.current = w, s(() => {
61
- T(0);
62
- }, [b]);
63
- let M = l(null), N = o(() => {
64
- let e = f.current;
52
+ w,
53
+ k,
54
+ O,
55
+ S
56
+ ]), j = u(b);
57
+ j.current = b;
58
+ let M = u(A);
59
+ M.current = A;
60
+ let N = u(E);
61
+ N.current = E, c(() => {
62
+ D(0);
63
+ }, [S]);
64
+ let P = u(null), F = s(() => {
65
+ let e = p.current;
65
66
  if (!e) return null;
66
- let t = (e.innerText || "").replace(/[\n\r]+$/, ""), n = d(e);
67
+ let t = (e.innerText || "").replace(/[\n\r]+$/, ""), n = f(e);
67
68
  if (n < 0) return null;
68
69
  let r = t.slice(0, n), i = r.match(/(^|\s)(\/model(?:\s+\S*)?)$/);
69
70
  if (i) {
@@ -84,21 +85,21 @@ var f = (f) => {
84
85
  start: s,
85
86
  end: l
86
87
  };
87
- }, [f]), P = o(() => {
88
- let e = N(), t = e?.kind === "model-profile" ? D.length > 0 || _ : E.length > 0;
89
- e !== null && t ? (C(e.kind), x(e.text), M.current = {
88
+ }, [p]), I = s(() => {
89
+ let e = F(), t = e?.kind === "model-profile" ? k.length > 0 || v : O.length > 0;
90
+ e !== null && t ? (T(e.kind), C(e.text), P.current = {
90
91
  start: e.start,
91
92
  end: e.end
92
- }, y(!0)) : (y(!1), x(""), C("command"), M.current = null);
93
+ }, x(!0)) : (x(!1), C(""), T("command"), P.current = null);
93
94
  }, [
94
- N,
95
- _,
96
- D.length,
97
- E.length
98
- ]), F = o((e) => {
99
- let t = f.current;
95
+ F,
96
+ v,
97
+ k.length,
98
+ O.length
99
+ ]), L = s((e) => {
100
+ let t = p.current;
100
101
  if (!t) return;
101
- let n = M.current, r = (t.innerText || "").replace(/[\n\r]+$/, ""), i = `${e.command} `;
102
+ let n = P.current, r = (t.innerText || "").replace(/[\n\r]+$/, ""), i = `${e.command} `;
102
103
  if (n) {
103
104
  t.textContent = r.slice(0, n.start) + i + r.slice(n.end);
104
105
  let e = n.start + i.length, a = t.firstChild;
@@ -111,37 +112,37 @@ var f = (f) => {
111
112
  let e = document.createRange(), n = window.getSelection();
112
113
  e.selectNodeContents(t), e.collapse(!1), n?.removeAllRanges(), n?.addRange(e);
113
114
  }
114
- y(!1), x(""), C("command"), T(0), M.current = null, t.dispatchEvent(new InputEvent("input", { bubbles: !0 })), t.focus();
115
- }, [f]);
115
+ x(!1), C(""), T("command"), D(0), P.current = null, t.dispatchEvent(new InputEvent("input", { bubbles: !0 })), t.focus();
116
+ }, [p]);
116
117
  return {
117
- isMenuOpen: v,
118
- filteredItems: O,
119
- selectedIndex: w,
120
- updateSlashMenu: P,
121
- selectItem: F,
122
- handleSlashKeyDown: o((e) => {
123
- let t = A.current;
124
- if (!k.current || t.length === 0) return !1;
118
+ isMenuOpen: b,
119
+ filteredItems: A,
120
+ selectedIndex: E,
121
+ updateSlashMenu: I,
122
+ selectItem: L,
123
+ handleSlashKeyDown: s((e) => {
124
+ let t = M.current;
125
+ if (!j.current || t.length === 0) return !1;
125
126
  switch (e.key) {
126
- case "ArrowDown": return e.preventDefault(), T((e) => e < t.length - 1 ? e + 1 : 0), !0;
127
- case "ArrowUp": return e.preventDefault(), T((e) => e > 0 ? e - 1 : t.length - 1), !0;
127
+ case "ArrowDown": return e.preventDefault(), D((e) => e < t.length - 1 ? e + 1 : 0), !0;
128
+ case "ArrowUp": return e.preventDefault(), D((e) => e > 0 ? e - 1 : t.length - 1), !0;
128
129
  case "Enter":
129
130
  case "Tab": {
130
- let n = t[j.current];
131
- return n ? (e.preventDefault(), F(n), !0) : !1;
131
+ let n = t[N.current];
132
+ return n ? (e.preventDefault(), L(n), !0) : !1;
132
133
  }
133
- case "Escape": return e.preventDefault(), y(!1), !0;
134
+ case "Escape": return e.preventDefault(), x(!1), !0;
134
135
  case "ArrowLeft":
135
136
  case "ArrowRight":
136
137
  case "Home":
137
- case "End": return y(!1), !1;
138
+ case "End": return x(!1), !1;
138
139
  default: return !1;
139
140
  }
140
- }, [F]),
141
- closeMenu: o(() => y(!1), [])
141
+ }, [L]),
142
+ closeMenu: s(() => x(!1), [])
142
143
  };
143
144
  };
144
145
  //#endregion
145
- export { f as useSlashCommand };
146
+ export { p as useSlashCommand };
146
147
 
147
148
  //# sourceMappingURL=use-slash-command.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-slash-command.js","names":[],"sources":["../../../src/hooks/chat/use-slash-command.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useConversationSkills } from \"#/hooks/query/use-conversation-skills\";\nimport { SkillInfo } from \"#/types/settings\";\nimport { BUILT_IN_COMMANDS, MODEL_COMMAND } from \"#/utils/constants\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useLlmProfiles } from \"#/hooks/query/use-llm-profiles\";\nimport { formatModelNameForDisplay } from \"#/utils/format-model-name\";\n\nexport type SlashCommandSkill = SkillInfo;\n\nexport interface SlashCommandItem {\n skill: SlashCommandSkill;\n /** The slash command string, e.g. \"/random-number\" */\n command: string;\n}\n\ntype SlashCompletionKind = \"command\" | \"model-profile\";\n\n/** Get the cursor's character offset within a contentEditable element. */\nfunction getCursorOffset(element: HTMLElement): number {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0) return -1;\n const range = selection.getRangeAt(0);\n const preRange = range.cloneRange();\n preRange.selectNodeContents(element);\n preRange.setEnd(range.startContainer, range.startOffset);\n return preRange.toString().length;\n}\n\n/**\n * Hook for managing slash command autocomplete in the chat input.\n * Detects when user types \"/\" and provides filtered skill suggestions.\n * Only skills with explicit \"/\" triggers (TaskTrigger) appear in the menu.\n */\nexport const useSlashCommand = (\n chatInputRef: React.RefObject<HTMLDivElement | null>,\n) => {\n // Scope the skill catalog to this conversation's attached workspace so the\n // slash menu lists the same project skills that were loaded into it.\n const { data: skills, isLoading: isSkillsLoading } = useConversationSkills();\n const isCloud = useActiveBackend().backend.kind === \"cloud\";\n const { data: profilesData, isLoading: isProfilesLoading } = useLlmProfiles();\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [filterText, setFilterText] = useState(\"\");\n const [completionKind, setCompletionKind] =\n useState<SlashCompletionKind>(\"command\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // Build slash command items from built-in commands + skills:\n // - Built-in commands (like /new) are included for V1 conversations\n // - /new is cloud-only — local backends don't surface it\n // - /model lists/switches LLM profiles; both local and cloud support them\n // - Skills with explicit \"/\" triggers use those triggers\n // - AgentSkills without \"/\" triggers get a derived \"/<name>\" command\n const slashItems = useMemo(() => {\n const items: SlashCommandItem[] = BUILT_IN_COMMANDS.filter((cmd) => {\n if (cmd.command === \"/new\") return isCloud;\n return true;\n });\n\n // Wait for skills to finish initial load so all commands appear together\n if (isSkillsLoading) return items;\n\n if (!skills) return items;\n skills.forEach((skill) => {\n const triggers = skill.triggers || [];\n const slashTriggers = triggers.filter((t) => t.startsWith(\"/\"));\n\n if (slashTriggers.length > 0) {\n // Skill has explicit slash triggers\n slashTriggers.forEach((trigger) => {\n items.push({ skill, command: trigger });\n });\n } else if (skill.type === \"agentskills\") {\n // AgentSkills without slash triggers get a derived command\n items.push({ skill, command: `/${skill.name}` });\n }\n });\n return items;\n }, [skills, isSkillsLoading, isCloud]);\n\n const modelProfileItems = useMemo<SlashCommandItem[]>(() => {\n return (profilesData?.profiles ?? []).map((profile) => {\n const command = `${MODEL_COMMAND} ${profile.name}`;\n return {\n command,\n skill: {\n name: profile.name,\n type: \"agentskills\",\n source: null,\n content: profile.model\n ? `Switch to ${formatModelNameForDisplay(profile.model)}`\n : \"Switch to this LLM profile\",\n triggers: [command],\n },\n };\n });\n }, [profilesData?.profiles]);\n\n // Filter items based on user input after \"/\"\n const filteredItems = useMemo(() => {\n const sourceItems =\n completionKind === \"model-profile\" ? modelProfileItems : slashItems;\n if (!filterText) return sourceItems;\n const lower = filterText.toLowerCase();\n return sourceItems.filter(\n (item) =>\n item.command.toLowerCase().includes(lower) ||\n item.skill.name.toLowerCase().includes(lower) ||\n item.skill.content?.toLowerCase().includes(lower),\n );\n }, [completionKind, modelProfileItems, slashItems, filterText]);\n\n // Keep refs in sync so handleSlashKeyDown always reads the latest values,\n // avoiding stale closures from React's batched state updates.\n const isMenuOpenRef = useRef(isMenuOpen);\n isMenuOpenRef.current = isMenuOpen;\n const filteredItemsRef = useRef(filteredItems);\n filteredItemsRef.current = filteredItems;\n const selectedIndexRef = useRef(selectedIndex);\n selectedIndexRef.current = selectedIndex;\n\n // Reset selected index when the filter text changes\n useEffect(() => {\n setSelectedIndex(0);\n }, [filterText]);\n\n // Track the character range of the current slash word so selectItem can\n // replace only that portion instead of wiping the entire input.\n const slashRangeRef = useRef<{ start: number; end: number } | null>(null);\n\n // Detect a slash word at the cursor position.\n // Returns the filter text (characters after \"/\") and the range of the\n // slash word within the full input text, or null if no slash word found.\n const getSlashText = useCallback((): {\n kind: SlashCompletionKind;\n text: string;\n start: number;\n end: number;\n } | null => {\n const element = chatInputRef.current;\n if (!element) return null;\n\n // Strip trailing newlines that contentEditable can produce, but preserve\n // spaces so \"/command \" (after selection) won't re-trigger the menu.\n const text = (element.innerText || \"\").replace(/[\\n\\r]+$/, \"\");\n const cursor = getCursorOffset(element);\n if (cursor < 0) return null;\n\n const textBeforeCursor = text.slice(0, cursor);\n\n const modelMatch = textBeforeCursor.match(/(^|\\s)(\\/model(?:\\s+\\S*)?)$/);\n if (modelMatch) {\n const modelCommand = modelMatch[2];\n const start = textBeforeCursor.length - modelCommand.length;\n const afterCursor = text.slice(cursor);\n const trailing = afterCursor.match(/^\\S*/);\n const end = cursor + (trailing ? trailing[0].length : 0);\n\n return {\n kind: \"model-profile\",\n text: modelCommand.replace(/^\\/model(?:\\s+)?/, \"\"),\n start,\n end,\n };\n }\n\n // Match a \"/\" preceded by whitespace or at position 0, followed by\n // non-whitespace characters, ending right at the cursor.\n const match = textBeforeCursor.match(/(^|\\s)(\\/\\S*)$/);\n if (!match) return null;\n\n const slashWord = match[2]; // e.g. \"/hel\"\n const start = textBeforeCursor.length - slashWord.length;\n // The end of the slash word extends past the cursor to include any\n // contiguous non-whitespace characters (covers the case where the\n // cursor sits in the middle of a word).\n const afterCursor = text.slice(cursor);\n const trailing = afterCursor.match(/^\\S*/);\n const end = cursor + (trailing ? trailing[0].length : 0);\n\n return { kind: \"command\", text: slashWord.slice(1), start, end }; // strip leading \"/\"\n }, [chatInputRef]);\n\n // Update the menu state based on current input\n const updateSlashMenu = useCallback(() => {\n const result = getSlashText();\n const hasItems =\n result?.kind === \"model-profile\"\n ? modelProfileItems.length > 0 || isProfilesLoading\n : slashItems.length > 0;\n\n if (result !== null && hasItems) {\n setCompletionKind(result.kind);\n setFilterText(result.text);\n slashRangeRef.current = { start: result.start, end: result.end };\n setIsMenuOpen(true);\n } else {\n setIsMenuOpen(false);\n setFilterText(\"\");\n setCompletionKind(\"command\");\n slashRangeRef.current = null;\n }\n }, [\n getSlashText,\n isProfilesLoading,\n modelProfileItems.length,\n slashItems.length,\n ]);\n\n // Select an item and replace only the slash word with the command\n const selectItem = useCallback(\n (item: SlashCommandItem) => {\n const element = chatInputRef.current;\n if (!element) return;\n\n const slashRange = slashRangeRef.current;\n const currentText = (element.innerText || \"\").replace(/[\\n\\r]+$/, \"\");\n const replacement = `${item.command} `;\n\n if (slashRange) {\n // Splice the command into the text, replacing only the slash word\n element.textContent =\n currentText.slice(0, slashRange.start) +\n replacement +\n currentText.slice(slashRange.end);\n\n // Position cursor right after the inserted command + space\n const cursorPos = slashRange.start + replacement.length;\n const textNode = element.firstChild;\n if (textNode) {\n const range = document.createRange();\n const sel = window.getSelection();\n const offset = Math.min(cursorPos, textNode.textContent!.length);\n range.setStart(textNode, offset);\n range.collapse(true);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n } else {\n // Fallback: replace everything (e.g. if range tracking failed)\n element.textContent = replacement;\n const range = document.createRange();\n const sel = window.getSelection();\n range.selectNodeContents(element);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n setIsMenuOpen(false);\n setFilterText(\"\");\n setCompletionKind(\"command\");\n setSelectedIndex(0);\n slashRangeRef.current = null;\n\n // Trigger a native InputEvent so React's onInput fires (for smartResize etc.)\n element.dispatchEvent(new InputEvent(\"input\", { bubbles: true }));\n\n // Restore focus so keyboard events (Enter to submit) work after selection\n element.focus();\n },\n [chatInputRef],\n );\n\n // Handle keyboard navigation in the menu.\n // Uses refs to always read the latest state, avoiding stale closures.\n const handleSlashKeyDown = useCallback(\n (e: React.KeyboardEvent): boolean => {\n const items = filteredItemsRef.current;\n if (!isMenuOpenRef.current || items.length === 0) return false;\n\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));\n return true;\n case \"ArrowUp\":\n e.preventDefault();\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));\n return true;\n case \"Enter\":\n case \"Tab\": {\n const item = items[selectedIndexRef.current];\n if (!item) return false;\n e.preventDefault();\n selectItem(item);\n return true;\n }\n case \"Escape\":\n e.preventDefault();\n setIsMenuOpen(false);\n return true;\n // Cursor-movement keys: close the menu to avoid acting on a stale\n // slash-word range, but don't consume the event so the cursor moves.\n case \"ArrowLeft\":\n case \"ArrowRight\":\n case \"Home\":\n case \"End\":\n setIsMenuOpen(false);\n return false;\n default:\n return false;\n }\n },\n [selectItem],\n );\n\n const closeMenu = useCallback(() => setIsMenuOpen(false), []);\n\n return {\n isMenuOpen,\n filteredItems,\n selectedIndex,\n updateSlashMenu,\n selectItem,\n handleSlashKeyDown,\n closeMenu,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-slash-command.js","names":[],"sources":["../../../src/hooks/chat/use-slash-command.ts"],"sourcesContent":["import { useState, useCallback, useEffect, useMemo, useRef } from \"react\";\nimport { useConversationSkills } from \"#/hooks/query/use-conversation-skills\";\nimport { SkillInfo } from \"#/types/settings\";\nimport { BUILT_IN_COMMANDS, MODEL_COMMAND } from \"#/utils/constants\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useLlmProfiles } from \"#/hooks/query/use-llm-profiles\";\nimport { useFreeModels } from \"#/hooks/query/use-free-models\";\nimport { formatModelNameForDisplay } from \"#/utils/format-model-name\";\n\nexport type SlashCommandSkill = SkillInfo;\n\nexport interface SlashCommandItem {\n skill: SlashCommandSkill;\n /** The slash command string, e.g. \"/random-number\" */\n command: string;\n}\n\ntype SlashCompletionKind = \"command\" | \"model-profile\";\n\n/** Get the cursor's character offset within a contentEditable element. */\nfunction getCursorOffset(element: HTMLElement): number {\n const selection = window.getSelection();\n if (!selection || selection.rangeCount === 0) return -1;\n const range = selection.getRangeAt(0);\n const preRange = range.cloneRange();\n preRange.selectNodeContents(element);\n preRange.setEnd(range.startContainer, range.startOffset);\n return preRange.toString().length;\n}\n\n/**\n * Hook for managing slash command autocomplete in the chat input.\n * Detects when user types \"/\" and provides filtered skill suggestions.\n * Only skills with explicit \"/\" triggers (TaskTrigger) appear in the menu.\n */\nexport const useSlashCommand = (\n chatInputRef: React.RefObject<HTMLDivElement | null>,\n) => {\n // Scope the skill catalog to this conversation's attached workspace so the\n // slash menu lists the same project skills that were loaded into it.\n const { data: skills, isLoading: isSkillsLoading } = useConversationSkills();\n const isCloud = useActiveBackend().backend.kind === \"cloud\";\n const { data: profilesData, isLoading: isProfilesLoading } = useLlmProfiles();\n const freeModels = useFreeModels();\n const [isMenuOpen, setIsMenuOpen] = useState(false);\n const [filterText, setFilterText] = useState(\"\");\n const [completionKind, setCompletionKind] =\n useState<SlashCompletionKind>(\"command\");\n const [selectedIndex, setSelectedIndex] = useState(0);\n\n // Build slash command items from built-in commands + skills:\n // - Built-in commands (like /new) are included for V1 conversations\n // - /new is cloud-only — local backends don't surface it\n // - /model lists/switches LLM profiles; both local and cloud support them\n // - Skills with explicit \"/\" triggers use those triggers\n // - AgentSkills without \"/\" triggers get a derived \"/<name>\" command\n const slashItems = useMemo(() => {\n const items: SlashCommandItem[] = BUILT_IN_COMMANDS.filter((cmd) => {\n if (cmd.command === \"/new\") return isCloud;\n return true;\n });\n\n // Wait for skills to finish initial load so all commands appear together\n if (isSkillsLoading) return items;\n\n if (!skills) return items;\n skills.forEach((skill) => {\n const triggers = skill.triggers || [];\n const slashTriggers = triggers.filter((t) => t.startsWith(\"/\"));\n\n if (slashTriggers.length > 0) {\n // Skill has explicit slash triggers\n slashTriggers.forEach((trigger) => {\n items.push({ skill, command: trigger });\n });\n } else if (skill.type === \"agentskills\") {\n // AgentSkills without slash triggers get a derived command\n items.push({ skill, command: `/${skill.name}` });\n }\n });\n return items;\n }, [skills, isSkillsLoading, isCloud]);\n\n const modelProfileItems = useMemo<SlashCommandItem[]>(() => {\n return (profilesData?.profiles ?? []).map((profile) => {\n const command = `${MODEL_COMMAND} ${profile.name}`;\n return {\n command,\n skill: {\n name: profile.name,\n type: \"agentskills\",\n source: null,\n content: profile.model\n ? `Switch to ${formatModelNameForDisplay(profile.model, freeModels)}`\n : \"Switch to this LLM profile\",\n triggers: [command],\n },\n };\n });\n }, [profilesData?.profiles, freeModels]);\n\n // Filter items based on user input after \"/\"\n const filteredItems = useMemo(() => {\n const sourceItems =\n completionKind === \"model-profile\" ? modelProfileItems : slashItems;\n if (!filterText) return sourceItems;\n const lower = filterText.toLowerCase();\n return sourceItems.filter(\n (item) =>\n item.command.toLowerCase().includes(lower) ||\n item.skill.name.toLowerCase().includes(lower) ||\n item.skill.content?.toLowerCase().includes(lower),\n );\n }, [completionKind, modelProfileItems, slashItems, filterText]);\n\n // Keep refs in sync so handleSlashKeyDown always reads the latest values,\n // avoiding stale closures from React's batched state updates.\n const isMenuOpenRef = useRef(isMenuOpen);\n isMenuOpenRef.current = isMenuOpen;\n const filteredItemsRef = useRef(filteredItems);\n filteredItemsRef.current = filteredItems;\n const selectedIndexRef = useRef(selectedIndex);\n selectedIndexRef.current = selectedIndex;\n\n // Reset selected index when the filter text changes\n useEffect(() => {\n setSelectedIndex(0);\n }, [filterText]);\n\n // Track the character range of the current slash word so selectItem can\n // replace only that portion instead of wiping the entire input.\n const slashRangeRef = useRef<{ start: number; end: number } | null>(null);\n\n // Detect a slash word at the cursor position.\n // Returns the filter text (characters after \"/\") and the range of the\n // slash word within the full input text, or null if no slash word found.\n const getSlashText = useCallback((): {\n kind: SlashCompletionKind;\n text: string;\n start: number;\n end: number;\n } | null => {\n const element = chatInputRef.current;\n if (!element) return null;\n\n // Strip trailing newlines that contentEditable can produce, but preserve\n // spaces so \"/command \" (after selection) won't re-trigger the menu.\n const text = (element.innerText || \"\").replace(/[\\n\\r]+$/, \"\");\n const cursor = getCursorOffset(element);\n if (cursor < 0) return null;\n\n const textBeforeCursor = text.slice(0, cursor);\n\n const modelMatch = textBeforeCursor.match(/(^|\\s)(\\/model(?:\\s+\\S*)?)$/);\n if (modelMatch) {\n const modelCommand = modelMatch[2];\n const start = textBeforeCursor.length - modelCommand.length;\n const afterCursor = text.slice(cursor);\n const trailing = afterCursor.match(/^\\S*/);\n const end = cursor + (trailing ? trailing[0].length : 0);\n\n return {\n kind: \"model-profile\",\n text: modelCommand.replace(/^\\/model(?:\\s+)?/, \"\"),\n start,\n end,\n };\n }\n\n // Match a \"/\" preceded by whitespace or at position 0, followed by\n // non-whitespace characters, ending right at the cursor.\n const match = textBeforeCursor.match(/(^|\\s)(\\/\\S*)$/);\n if (!match) return null;\n\n const slashWord = match[2]; // e.g. \"/hel\"\n const start = textBeforeCursor.length - slashWord.length;\n // The end of the slash word extends past the cursor to include any\n // contiguous non-whitespace characters (covers the case where the\n // cursor sits in the middle of a word).\n const afterCursor = text.slice(cursor);\n const trailing = afterCursor.match(/^\\S*/);\n const end = cursor + (trailing ? trailing[0].length : 0);\n\n return { kind: \"command\", text: slashWord.slice(1), start, end }; // strip leading \"/\"\n }, [chatInputRef]);\n\n // Update the menu state based on current input\n const updateSlashMenu = useCallback(() => {\n const result = getSlashText();\n const hasItems =\n result?.kind === \"model-profile\"\n ? modelProfileItems.length > 0 || isProfilesLoading\n : slashItems.length > 0;\n\n if (result !== null && hasItems) {\n setCompletionKind(result.kind);\n setFilterText(result.text);\n slashRangeRef.current = { start: result.start, end: result.end };\n setIsMenuOpen(true);\n } else {\n setIsMenuOpen(false);\n setFilterText(\"\");\n setCompletionKind(\"command\");\n slashRangeRef.current = null;\n }\n }, [\n getSlashText,\n isProfilesLoading,\n modelProfileItems.length,\n slashItems.length,\n ]);\n\n // Select an item and replace only the slash word with the command\n const selectItem = useCallback(\n (item: SlashCommandItem) => {\n const element = chatInputRef.current;\n if (!element) return;\n\n const slashRange = slashRangeRef.current;\n const currentText = (element.innerText || \"\").replace(/[\\n\\r]+$/, \"\");\n const replacement = `${item.command} `;\n\n if (slashRange) {\n // Splice the command into the text, replacing only the slash word\n element.textContent =\n currentText.slice(0, slashRange.start) +\n replacement +\n currentText.slice(slashRange.end);\n\n // Position cursor right after the inserted command + space\n const cursorPos = slashRange.start + replacement.length;\n const textNode = element.firstChild;\n if (textNode) {\n const range = document.createRange();\n const sel = window.getSelection();\n const offset = Math.min(cursorPos, textNode.textContent!.length);\n range.setStart(textNode, offset);\n range.collapse(true);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n } else {\n // Fallback: replace everything (e.g. if range tracking failed)\n element.textContent = replacement;\n const range = document.createRange();\n const sel = window.getSelection();\n range.selectNodeContents(element);\n range.collapse(false);\n sel?.removeAllRanges();\n sel?.addRange(range);\n }\n\n setIsMenuOpen(false);\n setFilterText(\"\");\n setCompletionKind(\"command\");\n setSelectedIndex(0);\n slashRangeRef.current = null;\n\n // Trigger a native InputEvent so React's onInput fires (for smartResize etc.)\n element.dispatchEvent(new InputEvent(\"input\", { bubbles: true }));\n\n // Restore focus so keyboard events (Enter to submit) work after selection\n element.focus();\n },\n [chatInputRef],\n );\n\n // Handle keyboard navigation in the menu.\n // Uses refs to always read the latest state, avoiding stale closures.\n const handleSlashKeyDown = useCallback(\n (e: React.KeyboardEvent): boolean => {\n const items = filteredItemsRef.current;\n if (!isMenuOpenRef.current || items.length === 0) return false;\n\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setSelectedIndex((prev) => (prev < items.length - 1 ? prev + 1 : 0));\n return true;\n case \"ArrowUp\":\n e.preventDefault();\n setSelectedIndex((prev) => (prev > 0 ? prev - 1 : items.length - 1));\n return true;\n case \"Enter\":\n case \"Tab\": {\n const item = items[selectedIndexRef.current];\n if (!item) return false;\n e.preventDefault();\n selectItem(item);\n return true;\n }\n case \"Escape\":\n e.preventDefault();\n setIsMenuOpen(false);\n return true;\n // Cursor-movement keys: close the menu to avoid acting on a stale\n // slash-word range, but don't consume the event so the cursor moves.\n case \"ArrowLeft\":\n case \"ArrowRight\":\n case \"Home\":\n case \"End\":\n setIsMenuOpen(false);\n return false;\n default:\n return false;\n }\n },\n [selectItem],\n );\n\n const closeMenu = useCallback(() => setIsMenuOpen(false), []);\n\n return {\n isMenuOpen,\n filteredItems,\n selectedIndex,\n updateSlashMenu,\n selectItem,\n handleSlashKeyDown,\n closeMenu,\n };\n};\n"],"mappings":";;;;;;;;AAoBA,SAAS,EAAgB,GAA8B;CACrD,IAAM,IAAY,OAAO,aAAa;CACtC,IAAI,CAAC,KAAa,EAAU,eAAe,GAAG,OAAO;CACrD,IAAM,IAAQ,EAAU,WAAW,CAAC,GAC9B,IAAW,EAAM,WAAW;CAGlC,OAFA,EAAS,mBAAmB,CAAO,GACnC,EAAS,OAAO,EAAM,gBAAgB,EAAM,WAAW,GAChD,EAAS,SAAS,EAAE;AAC7B;AAOA,IAAa,KACX,MACG;CAGH,IAAM,EAAE,MAAM,GAAQ,WAAW,MAAoB,EAAsB,GACrE,IAAU,EAAiB,EAAE,QAAQ,SAAS,SAC9C,EAAE,MAAM,GAAc,WAAW,MAAsB,EAAe,GACtE,IAAa,EAAc,GAC3B,CAAC,GAAY,KAAiB,EAAS,EAAK,GAC5C,CAAC,GAAY,KAAiB,EAAS,EAAE,GACzC,CAAC,GAAgB,KACrB,EAA8B,SAAS,GACnC,CAAC,GAAe,KAAoB,EAAS,CAAC,GAQ9C,IAAa,QAAc;EAC/B,IAAM,IAA4B,EAAkB,QAAQ,MACtD,EAAI,YAAY,SAAe,IAC5B,EACR;EAoBD,OAjBI,KAEA,CAAC,KACL,EAAO,SAAS,MAAU;GAExB,IAAM,KADW,EAAM,YAAY,CAAC,GACL,QAAQ,MAAM,EAAE,WAAW,GAAG,CAAC;GAE9D,AAAI,EAAc,SAAS,IAEzB,EAAc,SAAS,MAAY;IACjC,EAAM,KAAK;KAAE;KAAO,SAAS;IAAQ,CAAC;GACxC,CAAC,IACQ,EAAM,SAAS,iBAExB,EAAM,KAAK;IAAE;IAAO,SAAS,IAAI,EAAM;GAAO,CAAC;EAEnD,CAAC,GAdmB;CAgBtB,GAAG;EAAC;EAAQ;EAAiB;CAAO,CAAC,GAE/B,IAAoB,SAChB,GAAc,YAAY,CAAC,GAAG,KAAK,MAAY;EACrD,IAAM,IAAU,GAAG,EAAc,GAAG,EAAQ;EAC5C,OAAO;GACL;GACA,OAAO;IACL,MAAM,EAAQ;IACd,MAAM;IACN,QAAQ;IACR,SAAS,EAAQ,QACb,aAAa,EAA0B,EAAQ,OAAO,CAAU,MAChE;IACJ,UAAU,CAAC,CAAO;GACpB;EACF;CACF,CAAC,GACA,CAAC,GAAc,UAAU,CAAU,CAAC,GAGjC,IAAgB,QAAc;EAClC,IAAM,IACJ,MAAmB,kBAAkB,IAAoB;EAC3D,IAAI,CAAC,GAAY,OAAO;EACxB,IAAM,IAAQ,EAAW,YAAY;EACrC,OAAO,EAAY,QAChB,MACC,EAAK,QAAQ,YAAY,EAAE,SAAS,CAAK,KACzC,EAAK,MAAM,KAAK,YAAY,EAAE,SAAS,CAAK,KAC5C,EAAK,MAAM,SAAS,YAAY,EAAE,SAAS,CAAK,CACpD;CACF,GAAG;EAAC;EAAgB;EAAmB;EAAY;CAAU,CAAC,GAIxD,IAAgB,EAAO,CAAU;CACvC,EAAc,UAAU;CACxB,IAAM,IAAmB,EAAO,CAAa;CAC7C,EAAiB,UAAU;CAC3B,IAAM,IAAmB,EAAO,CAAa;CAI7C,AAHA,EAAiB,UAAU,GAG3B,QAAgB;EACd,EAAiB,CAAC;CACpB,GAAG,CAAC,CAAU,CAAC;CAIf,IAAM,IAAgB,EAA8C,IAAI,GAKlE,IAAe,QAKT;EACV,IAAM,IAAU,EAAa;EAC7B,IAAI,CAAC,GAAS,OAAO;EAIrB,IAAM,KAAQ,EAAQ,aAAa,IAAI,QAAQ,YAAY,EAAE,GACvD,IAAS,EAAgB,CAAO;EACtC,IAAI,IAAS,GAAG,OAAO;EAEvB,IAAM,IAAmB,EAAK,MAAM,GAAG,CAAM,GAEvC,IAAa,EAAiB,MAAM,6BAA6B;EACvE,IAAI,GAAY;GACd,IAAM,IAAe,EAAW,IAC1B,IAAQ,EAAiB,SAAS,EAAa,QAE/C,IADc,EAAK,MAAM,CACd,EAAY,MAAM,MAAM,GACnC,IAAM,KAAU,IAAW,EAAS,GAAG,SAAS;GAEtD,OAAO;IACL,MAAM;IACN,MAAM,EAAa,QAAQ,oBAAoB,EAAE;IACjD;IACA;GACF;EACF;EAIA,IAAM,IAAQ,EAAiB,MAAM,gBAAgB;EACrD,IAAI,CAAC,GAAO,OAAO;EAEnB,IAAM,IAAY,EAAM,IAClB,IAAQ,EAAiB,SAAS,EAAU,QAK5C,IADc,EAAK,MAAM,CACd,EAAY,MAAM,MAAM,GACnC,IAAM,KAAU,IAAW,EAAS,GAAG,SAAS;EAEtD,OAAO;GAAE,MAAM;GAAW,MAAM,EAAU,MAAM,CAAC;GAAG;GAAO;EAAI;CACjE,GAAG,CAAC,CAAY,CAAC,GAGX,IAAkB,QAAkB;EACxC,IAAM,IAAS,EAAa,GACtB,IACJ,GAAQ,SAAS,kBACb,EAAkB,SAAS,KAAK,IAChC,EAAW,SAAS;EAE1B,AAAI,MAAW,QAAQ,KACrB,EAAkB,EAAO,IAAI,GAC7B,EAAc,EAAO,IAAI,GACzB,EAAc,UAAU;GAAE,OAAO,EAAO;GAAO,KAAK,EAAO;EAAI,GAC/D,EAAc,EAAI,MAElB,EAAc,EAAK,GACnB,EAAc,EAAE,GAChB,EAAkB,SAAS,GAC3B,EAAc,UAAU;CAE5B,GAAG;EACD;EACA;EACA,EAAkB;EAClB,EAAW;CACb,CAAC,GAGK,IAAa,GAChB,MAA2B;EAC1B,IAAM,IAAU,EAAa;EAC7B,IAAI,CAAC,GAAS;EAEd,IAAM,IAAa,EAAc,SAC3B,KAAe,EAAQ,aAAa,IAAI,QAAQ,YAAY,EAAE,GAC9D,IAAc,GAAG,EAAK,QAAQ;EAEpC,IAAI,GAAY;GAEd,EAAQ,cACN,EAAY,MAAM,GAAG,EAAW,KAAK,IACrC,IACA,EAAY,MAAM,EAAW,GAAG;GAGlC,IAAM,IAAY,EAAW,QAAQ,EAAY,QAC3C,IAAW,EAAQ;GACzB,IAAI,GAAU;IACZ,IAAM,IAAQ,SAAS,YAAY,GAC7B,IAAM,OAAO,aAAa,GAC1B,IAAS,KAAK,IAAI,GAAW,EAAS,YAAa,MAAM;IAI/D,AAHA,EAAM,SAAS,GAAU,CAAM,GAC/B,EAAM,SAAS,EAAI,GACnB,GAAK,gBAAgB,GACrB,GAAK,SAAS,CAAK;GACrB;EACF,OAAO;GAEL,EAAQ,cAAc;GACtB,IAAM,IAAQ,SAAS,YAAY,GAC7B,IAAM,OAAO,aAAa;GAIhC,AAHA,EAAM,mBAAmB,CAAO,GAChC,EAAM,SAAS,EAAK,GACpB,GAAK,gBAAgB,GACrB,GAAK,SAAS,CAAK;EACrB;EAYA,AAVA,EAAc,EAAK,GACnB,EAAc,EAAE,GAChB,EAAkB,SAAS,GAC3B,EAAiB,CAAC,GAClB,EAAc,UAAU,MAGxB,EAAQ,cAAc,IAAI,WAAW,SAAS,EAAE,SAAS,GAAK,CAAC,CAAC,GAGhE,EAAQ,MAAM;CAChB,GACA,CAAC,CAAY,CACf;CA+CA,OAAO;EACL;EACA;EACA;EACA;EACA;EACA,oBAjDyB,GACxB,MAAoC;GACnC,IAAM,IAAQ,EAAiB;GAC/B,IAAI,CAAC,EAAc,WAAW,EAAM,WAAW,GAAG,OAAO;GAEzD,QAAQ,EAAE,KAAV;IACE,KAAK,aAGH,OAFA,EAAE,eAAe,GACjB,GAAkB,MAAU,IAAO,EAAM,SAAS,IAAI,IAAO,IAAI,CAAE,GAC5D;IACT,KAAK,WAGH,OAFA,EAAE,eAAe,GACjB,GAAkB,MAAU,IAAO,IAAI,IAAO,IAAI,EAAM,SAAS,CAAE,GAC5D;IACT,KAAK;IACL,KAAK,OAAO;KACV,IAAM,IAAO,EAAM,EAAiB;KAIpC,OAHK,KACL,EAAE,eAAe,GACjB,EAAW,CAAI,GACR,MAHW;IAIpB;IACA,KAAK,UAGH,OAFA,EAAE,eAAe,GACjB,EAAc,EAAK,GACZ;IAGT,KAAK;IACL,KAAK;IACL,KAAK;IACL,KAAK,OAEH,OADA,EAAc,EAAK,GACZ;IACT,SACE,OAAO;GACX;EACF,GACA,CAAC,CAAU,CAWX;EACA,WATgB,QAAkB,EAAc,EAAK,GAAG,CAAC,CASzD;CACF;AACF"}
@@ -1,2 +1,2 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../api/backend-registry/active-store.cjs"),n=require("../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs"),r=require("../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs"),i=require("../../query-client-config.cjs"),a=require("../../contexts/active-backend-context.cjs"),o=require("../../api/automation-service/automation-service.api.cjs");let s=require("react");s=e.__toESM(s,1);var c=`automation-sdk-version`,l=3600*1e3;async function u(){if(typeof o.default.getSdkVersion!=`function`)return null;try{return await o.default.getSdkVersion()}catch{return null}}function d(){let e=a.useActiveBackend(),{backend:d}=e,f=s.default.useContext(n.QueryClientContext),p=!t.isNoBackend(d)&&typeof o.default.getSdkVersion==`function`,{data:m}=r.useQuery({queryKey:[c,d.id,d.kind,d.host,e.orgId??``],queryFn:u,enabled:p,initialData:p?void 0:null,staleTime:l,gcTime:l},f??i.getQueryClient());return m??null}exports.useAutomationSdkVersion=d;
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../api/backend-registry/active-store.cjs"),n=require("../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs"),r=require("../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs"),i=require("../../query-client-config.cjs"),a=require("../../contexts/active-backend-context.cjs"),o=require("../../api/automation-service/automation-service.api.cjs");let s=require("react");s=e.__toESM(s,1);var c=`automation-sdk-version`,l=3600*1e3;async function u(){if(typeof o.default.getSdkVersion!=`function`)return null;try{return await o.default.getSdkVersion()}catch{return null}}function d(){let e=a.useActiveBackend(),{backend:d}=e,f=s.default.useContext(n.QueryClientContext),p=!t.isNoBackend(d)&&typeof o.default.getSdkVersion==`function`,{data:m}=r.useQuery({queryKey:[c,d.id,d.kind,d.host,e.orgId??``],queryFn:u,enabled:p,initialData:p?void 0:null,staleTime:l,gcTime:l,refetchOnWindowFocus:!1,refetchOnReconnect:!1},f??i.getQueryClient());return m??null}exports.useAutomationSdkVersion=d;
2
2
  //# sourceMappingURL=use-automation-sdk-version.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-automation-sdk-version.cjs","names":[],"sources":["../../../src/hooks/query/use-automation-sdk-version.ts"],"sourcesContent":["import React from \"react\";\nimport { QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport AutomationService from \"#/api/automation-service/automation-service.api\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { getQueryClient } from \"#/query-client-config\";\n\nexport const AUTOMATION_SDK_VERSION_CACHE_NAMESPACE = \"automation-sdk-version\";\n\nconst AUTOMATION_SDK_VERSION_CACHE_TIME_MS = 60 * 60 * 1000;\nasync function fetchAutomationSdkVersion(): Promise<string | null> {\n if (typeof AutomationService.getSdkVersion !== \"function\") {\n return null;\n }\n\n try {\n return await AutomationService.getSdkVersion();\n } catch {\n return null;\n }\n}\n\nexport function useAutomationSdkVersion() {\n const active = useActiveBackend();\n const { backend } = active;\n const providerQueryClient = React.useContext(QueryClientContext);\n const canFetchSdkVersion =\n !isNoBackend(backend) &&\n typeof AutomationService.getSdkVersion === \"function\";\n const { data } = useQuery(\n {\n queryKey: [\n AUTOMATION_SDK_VERSION_CACHE_NAMESPACE,\n backend.id,\n backend.kind,\n backend.host,\n active.orgId ?? \"\",\n ],\n queryFn: fetchAutomationSdkVersion,\n enabled: canFetchSdkVersion,\n initialData: canFetchSdkVersion ? undefined : null,\n staleTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n gcTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n },\n providerQueryClient ?? getQueryClient(),\n );\n\n return data ?? null;\n}\n"],"mappings":"2eAOA,IAAa,EAAyC,yBAEhD,EAAuC,KAAU,IACvD,eAAe,GAAoD,CACjE,GAAI,OAAO,EAAA,QAAkB,eAAkB,WAC7C,OAAO,KAGT,GAAI,CACF,OAAO,MAAM,EAAA,QAAkB,cAAc,CAC/C,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAgB,GAA0B,CACxC,IAAM,EAAS,EAAA,iBAAiB,EAC1B,CAAE,WAAY,EACd,EAAsB,EAAA,QAAM,WAAW,EAAA,kBAAkB,EACzD,EACJ,CAAC,EAAA,YAAY,CAAO,GACpB,OAAO,EAAA,QAAkB,eAAkB,WACvC,CAAE,QAAS,EAAA,SACf,CACE,SAAU,CACR,EACA,EAAQ,GACR,EAAQ,KACR,EAAQ,KACR,EAAO,OAAS,EAClB,EACA,QAAS,EACT,QAAS,EACT,YAAa,EAAqB,IAAA,GAAY,KAC9C,UAAW,EACX,OAAQ,CACV,EACA,GAAuB,EAAA,eAAe,CACxC,EAEA,OAAO,GAAQ,IACjB"}
1
+ {"version":3,"file":"use-automation-sdk-version.cjs","names":[],"sources":["../../../src/hooks/query/use-automation-sdk-version.ts"],"sourcesContent":["import React from \"react\";\nimport { QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport AutomationService from \"#/api/automation-service/automation-service.api\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { getQueryClient } from \"#/query-client-config\";\n\nexport const AUTOMATION_SDK_VERSION_CACHE_NAMESPACE = \"automation-sdk-version\";\n\nconst AUTOMATION_SDK_VERSION_CACHE_TIME_MS = 60 * 60 * 1000;\nasync function fetchAutomationSdkVersion(): Promise<string | null> {\n if (typeof AutomationService.getSdkVersion !== \"function\") {\n return null;\n }\n\n try {\n return await AutomationService.getSdkVersion();\n } catch {\n return null;\n }\n}\n\nexport function useAutomationSdkVersion() {\n const active = useActiveBackend();\n const { backend } = active;\n const providerQueryClient = React.useContext(QueryClientContext);\n const canFetchSdkVersion =\n !isNoBackend(backend) &&\n typeof AutomationService.getSdkVersion === \"function\";\n const { data } = useQuery(\n {\n queryKey: [\n AUTOMATION_SDK_VERSION_CACHE_NAMESPACE,\n backend.id,\n backend.kind,\n backend.host,\n active.orgId ?? \"\",\n ],\n queryFn: fetchAutomationSdkVersion,\n enabled: canFetchSdkVersion,\n initialData: canFetchSdkVersion ? undefined : null,\n staleTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n gcTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n providerQueryClient ?? getQueryClient(),\n );\n\n return data ?? null;\n}\n"],"mappings":"2eAOA,IAAa,EAAyC,yBAEhD,EAAuC,KAAU,IACvD,eAAe,GAAoD,CACjE,GAAI,OAAO,EAAA,QAAkB,eAAkB,WAC7C,OAAO,KAGT,GAAI,CACF,OAAO,MAAM,EAAA,QAAkB,cAAc,CAC/C,MAAQ,CACN,OAAO,IACT,CACF,CAEA,SAAgB,GAA0B,CACxC,IAAM,EAAS,EAAA,iBAAiB,EAC1B,CAAE,WAAY,EACd,EAAsB,EAAA,QAAM,WAAW,EAAA,kBAAkB,EACzD,EACJ,CAAC,EAAA,YAAY,CAAO,GACpB,OAAO,EAAA,QAAkB,eAAkB,WACvC,CAAE,QAAS,EAAA,SACf,CACE,SAAU,CACR,EACA,EAAQ,GACR,EAAQ,KACR,EAAQ,KACR,EAAO,OAAS,EAClB,EACA,QAAS,EACT,QAAS,EACT,YAAa,EAAqB,IAAA,GAAY,KAC9C,UAAW,EACX,OAAQ,EACR,qBAAsB,GACtB,mBAAoB,EACtB,EACA,GAAuB,EAAA,eAAe,CACxC,EAEA,OAAO,GAAQ,IACjB"}
@@ -28,7 +28,9 @@ function u() {
28
28
  enabled: p,
29
29
  initialData: p ? void 0 : null,
30
30
  staleTime: c,
31
- gcTime: c
31
+ gcTime: c,
32
+ refetchOnWindowFocus: !1,
33
+ refetchOnReconnect: !1
32
34
  }, f ?? r());
33
35
  return m ?? null;
34
36
  }
@@ -1 +1 @@
1
- {"version":3,"file":"use-automation-sdk-version.js","names":[],"sources":["../../../src/hooks/query/use-automation-sdk-version.ts"],"sourcesContent":["import React from \"react\";\nimport { QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport AutomationService from \"#/api/automation-service/automation-service.api\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { getQueryClient } from \"#/query-client-config\";\n\nexport const AUTOMATION_SDK_VERSION_CACHE_NAMESPACE = \"automation-sdk-version\";\n\nconst AUTOMATION_SDK_VERSION_CACHE_TIME_MS = 60 * 60 * 1000;\nasync function fetchAutomationSdkVersion(): Promise<string | null> {\n if (typeof AutomationService.getSdkVersion !== \"function\") {\n return null;\n }\n\n try {\n return await AutomationService.getSdkVersion();\n } catch {\n return null;\n }\n}\n\nexport function useAutomationSdkVersion() {\n const active = useActiveBackend();\n const { backend } = active;\n const providerQueryClient = React.useContext(QueryClientContext);\n const canFetchSdkVersion =\n !isNoBackend(backend) &&\n typeof AutomationService.getSdkVersion === \"function\";\n const { data } = useQuery(\n {\n queryKey: [\n AUTOMATION_SDK_VERSION_CACHE_NAMESPACE,\n backend.id,\n backend.kind,\n backend.host,\n active.orgId ?? \"\",\n ],\n queryFn: fetchAutomationSdkVersion,\n enabled: canFetchSdkVersion,\n initialData: canFetchSdkVersion ? undefined : null,\n staleTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n gcTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n },\n providerQueryClient ?? getQueryClient(),\n );\n\n return data ?? null;\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,IAAyC,0BAEhD,IAAuC,OAAU;AACvD,eAAe,IAAoD;CACjE,IAAI,OAAO,EAAkB,iBAAkB,YAC7C,OAAO;CAGT,IAAI;EACF,OAAO,MAAM,EAAkB,cAAc;CAC/C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,IAA0B;CACxC,IAAM,IAAS,EAAiB,GAC1B,EAAE,eAAY,GACd,IAAsB,EAAM,WAAW,CAAkB,GACzD,IACJ,CAAC,EAAY,CAAO,KACpB,OAAO,EAAkB,iBAAkB,YACvC,EAAE,YAAS,EACf;EACE,UAAU;GACR;GACA,EAAQ;GACR,EAAQ;GACR,EAAQ;GACR,EAAO,SAAS;EAClB;EACA,SAAS;EACT,SAAS;EACT,aAAa,IAAqB,KAAA,IAAY;EAC9C,WAAW;EACX,QAAQ;CACV,GACA,KAAuB,EAAe,CACxC;CAEA,OAAO,KAAQ;AACjB"}
1
+ {"version":3,"file":"use-automation-sdk-version.js","names":[],"sources":["../../../src/hooks/query/use-automation-sdk-version.ts"],"sourcesContent":["import React from \"react\";\nimport { QueryClientContext, useQuery } from \"@tanstack/react-query\";\nimport AutomationService from \"#/api/automation-service/automation-service.api\";\nimport { isNoBackend } from \"#/api/backend-registry/active-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { getQueryClient } from \"#/query-client-config\";\n\nexport const AUTOMATION_SDK_VERSION_CACHE_NAMESPACE = \"automation-sdk-version\";\n\nconst AUTOMATION_SDK_VERSION_CACHE_TIME_MS = 60 * 60 * 1000;\nasync function fetchAutomationSdkVersion(): Promise<string | null> {\n if (typeof AutomationService.getSdkVersion !== \"function\") {\n return null;\n }\n\n try {\n return await AutomationService.getSdkVersion();\n } catch {\n return null;\n }\n}\n\nexport function useAutomationSdkVersion() {\n const active = useActiveBackend();\n const { backend } = active;\n const providerQueryClient = React.useContext(QueryClientContext);\n const canFetchSdkVersion =\n !isNoBackend(backend) &&\n typeof AutomationService.getSdkVersion === \"function\";\n const { data } = useQuery(\n {\n queryKey: [\n AUTOMATION_SDK_VERSION_CACHE_NAMESPACE,\n backend.id,\n backend.kind,\n backend.host,\n active.orgId ?? \"\",\n ],\n queryFn: fetchAutomationSdkVersion,\n enabled: canFetchSdkVersion,\n initialData: canFetchSdkVersion ? undefined : null,\n staleTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n gcTime: AUTOMATION_SDK_VERSION_CACHE_TIME_MS,\n refetchOnWindowFocus: false,\n refetchOnReconnect: false,\n },\n providerQueryClient ?? getQueryClient(),\n );\n\n return data ?? null;\n}\n"],"mappings":";;;;;;;;AAOA,IAAa,IAAyC,0BAEhD,IAAuC,OAAU;AACvD,eAAe,IAAoD;CACjE,IAAI,OAAO,EAAkB,iBAAkB,YAC7C,OAAO;CAGT,IAAI;EACF,OAAO,MAAM,EAAkB,cAAc;CAC/C,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,IAA0B;CACxC,IAAM,IAAS,EAAiB,GAC1B,EAAE,eAAY,GACd,IAAsB,EAAM,WAAW,CAAkB,GACzD,IACJ,CAAC,EAAY,CAAO,KACpB,OAAO,EAAkB,iBAAkB,YACvC,EAAE,YAAS,EACf;EACE,UAAU;GACR;GACA,EAAQ;GACR,EAAQ;GACR,EAAQ;GACR,EAAO,SAAS;EAClB;EACA,SAAS;EACT,SAAS;EACT,aAAa,IAAqB,KAAA,IAAY;EAC9C,WAAW;EACX,QAAQ;EACR,sBAAsB;EACtB,oBAAoB;CACtB,GACA,KAAuB,EAAe,CACxC;CAEA,OAAO,KAAQ;AACjB"}
@@ -0,0 +1,2 @@
1
+ const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs"),n=require("../../contexts/active-backend-context.cjs"),r=require("../../api/config-service/config-service.api.cjs"),i=require("../../stores/free-models-store.cjs"),a=require("./use-verified-models.cjs");let o=require("react");o=e.__toESM(o,1);var s=`openhands`,c=100,l=10;async function u(e,t,n,i){if(i>=l)throw Error(`Too many pagination requests while fetching OpenHands models (depth=${i})`);let a=await r.default.searchModels({provider__eq:s,limit:c,...t?{page_id:t}:{}},e);if(!a.next_page_id)return a.items;if(n.has(a.next_page_id))throw Error(`Repeated page id while fetching OpenHands models: ${a.next_page_id}`);n.add(a.next_page_id);let o=await u(e,a.next_page_id,n,i+1);return[...a.items,...o]}var d=()=>{let{backend:e,orgId:r}=n.useActiveBackend(),i=[e.id,e.connectionRevision??0,r];return t.useQuery({queryKey:[`config`,`models`,s,`flags`,...i],queryFn:async({client:e})=>u(await e.fetchQuery({queryKey:[...a.VERIFIED_MODELS_QUERY_KEY,...i],queryFn:a.fetchVerifiedModelsByProvider,staleTime:a.VERIFIED_MODELS_STALE_TIME}),null,new Set,0),staleTime:a.VERIFIED_MODELS_STALE_TIME,gcTime:a.VERIFIED_MODELS_GC_TIME})},f=()=>{let{backend:e,orgId:t}=n.useActiveBackend(),{data:r,isError:a}=d(),c=i.useFreeModelsStore(e=>e.setFlags),l=i.useFreeModelsStore(e=>e.markDefaultModelReady),u=i.useFreeModelsStore(e=>e.resetFlags);o.default.useEffect(()=>{u()},[e.id,e.connectionRevision,t,u]),o.default.useEffect(()=>{if(!r)return;let e=new Set(r.filter(e=>e.free).map(e=>`${s}/${e.name}`)),t=r.find(e=>e.default);c({freeModels:e,defaultModel:t?`${s}/${t.name}`:null})},[r,c]),o.default.useEffect(()=>{a&&l()},[a,l])},p=()=>i.useFreeModelsStore(e=>e.freeModels),m=()=>i.useFreeModelsStore(e=>e.defaultModel);exports.useDefaultModel=m,exports.useFreeModels=p,exports.useHydrateFreeModels=f;
2
+ //# sourceMappingURL=use-free-models.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-free-models.cjs","names":[],"sources":["../../../src/hooks/query/use-free-models.ts"],"sourcesContent":["import React from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport ConfigService from \"#/api/config-service/config-service.api\";\nimport type { LLMModel } from \"#/api/config-service/config-service.types\";\nimport type { FreeModelSet } from \"#/utils/format-model-name\";\nimport { useFreeModelsStore } from \"#/stores/free-models-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n VERIFIED_MODELS_GC_TIME,\n VERIFIED_MODELS_QUERY_KEY,\n VERIFIED_MODELS_STALE_TIME,\n fetchVerifiedModelsByProvider,\n} from \"./use-verified-models\";\n\n/**\n * Provider whose models carry free / default metadata. Both are\n * OpenHands-managed concepts, so they are scoped to the `openhands` provider.\n */\nconst OPENHANDS_PROVIDER = \"openhands\";\nconst CLOUD_MODEL_SEARCH_PAGE_LIMIT = 100;\nconst MAX_PAGINATION_DEPTH = 10;\n\nasync function fetchAllOpenHandsModels(\n verifiedByProvider: Record<string, string[]>,\n pageId: string | null | undefined,\n seenPageIds: Set<string>,\n depth: number,\n): Promise<LLMModel[]> {\n if (depth >= MAX_PAGINATION_DEPTH) {\n throw new Error(\n `Too many pagination requests while fetching OpenHands models (depth=${depth})`,\n );\n }\n\n const page = await ConfigService.searchModels(\n {\n provider__eq: OPENHANDS_PROVIDER,\n limit: CLOUD_MODEL_SEARCH_PAGE_LIMIT,\n ...(pageId ? { page_id: pageId } : {}),\n },\n verifiedByProvider,\n );\n\n if (!page.next_page_id) {\n return page.items;\n }\n\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n `Repeated page id while fetching OpenHands models: ${page.next_page_id}`,\n );\n }\n seenPageIds.add(page.next_page_id);\n\n const rest = await fetchAllOpenHandsModels(\n verifiedByProvider,\n page.next_page_id,\n seenPageIds,\n depth + 1,\n );\n return [...page.items, ...rest];\n}\n\n/**\n * Fetches the `openhands` provider's models with their DB-driven `free` /\n * `default` flags (the same channel that carries `verified`).\n */\nconst useOpenHandsModels = () => {\n const { backend, orgId } = useActiveBackend();\n const backendScope = [\n backend.id,\n backend.connectionRevision ?? 0,\n orgId,\n ] as const;\n\n return useQuery({\n queryKey: [\n \"config\",\n \"models\",\n OPENHANDS_PROVIDER,\n \"flags\",\n ...backendScope,\n ],\n queryFn: async ({ client }): Promise<LLMModel[]> => {\n const verifiedByProvider = await client.fetchQuery({\n queryKey: [...VERIFIED_MODELS_QUERY_KEY, ...backendScope],\n queryFn: fetchVerifiedModelsByProvider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n });\n return fetchAllOpenHandsModels(verifiedByProvider, null, new Set(), 0);\n },\n staleTime: VERIFIED_MODELS_STALE_TIME,\n gcTime: VERIFIED_MODELS_GC_TIME,\n });\n};\n\n/**\n * Fetches the DB-driven free / default flags once and mirrors them into the\n * {@link useFreeModelsStore}. Mount this high in the tree (inside the query\n * provider). Leaf display components then read the flags synchronously via the\n * {@link useFreeModels} / {@link useDefaultModel} zustand selectors, so they\n * stay renderable in isolation without a QueryClientProvider in scope.\n */\nexport const useHydrateFreeModels = (): void => {\n const { backend, orgId } = useActiveBackend();\n const { data, isError } = useOpenHandsModels();\n const setFlags = useFreeModelsStore((state) => state.setFlags);\n const markDefaultModelReady = useFreeModelsStore(\n (state) => state.markDefaultModelReady,\n );\n const resetFlags = useFreeModelsStore((state) => state.resetFlags);\n\n React.useEffect(() => {\n resetFlags();\n }, [backend.id, backend.connectionRevision, orgId, resetFlags]);\n\n React.useEffect(() => {\n if (!data) return;\n const freeModels = new Set(\n data\n .filter((model) => model.free)\n .map((model) => `${OPENHANDS_PROVIDER}/${model.name}`),\n );\n const defaultEntry = data.find((model) => model.default);\n setFlags({\n freeModels,\n defaultModel: defaultEntry\n ? `${OPENHANDS_PROVIDER}/${defaultEntry.name}`\n : null,\n });\n }, [data, setFlags]);\n\n React.useEffect(() => {\n if (isError) markDefaultModelReady();\n }, [isError, markDefaultModelReady]);\n};\n\n/**\n * Set of free ``openhands/<model>`` ids, sourced from the backend model list\n * (DB-driven on cloud via the `free` flag). Returns an empty set on backends\n * without free metadata (e.g. the local agent-server), so callers uniformly\n * treat \"not in set\" as paid and the frontend keeps no hardcoded free list.\n */\nexport const useFreeModels = (): FreeModelSet =>\n useFreeModelsStore((state) => state.freeModels);\n\n/**\n * DB-driven default OpenHands model id (``openhands/<model>``), used to\n * preselect a model on onboarding and when creating a new OpenHands model.\n * Returns `null` on backends without default metadata.\n */\nexport const useDefaultModel = (): string | null =>\n useFreeModelsStore((state) => state.defaultModel);\n\nexport const useDefaultModelReady = (): boolean =>\n useFreeModelsStore((state) => state.defaultModelReady);\n"],"mappings":"2XAkBA,IAAM,EAAqB,YACrB,EAAgC,IAChC,EAAuB,GAE7B,eAAe,EACb,EACA,EACA,EACA,EACqB,CACrB,GAAI,GAAS,EACX,MAAU,MACR,uEAAuE,EAAM,EAC/E,EAGF,IAAM,EAAO,MAAM,EAAA,QAAc,aAC/B,CACE,aAAc,EACd,MAAO,EACP,GAAI,EAAS,CAAE,QAAS,CAAO,EAAI,CAAC,CACtC,EACA,CACF,EAEA,GAAI,CAAC,EAAK,aACR,OAAO,EAAK,MAGd,GAAI,EAAY,IAAI,EAAK,YAAY,EACnC,MAAU,MACR,qDAAqD,EAAK,cAC5D,EAEF,EAAY,IAAI,EAAK,YAAY,EAEjC,IAAM,EAAO,MAAM,EACjB,EACA,EAAK,aACL,EACA,EAAQ,CACV,EACA,MAAO,CAAC,GAAG,EAAK,MAAO,GAAG,CAAI,CAChC,CAMA,IAAM,MAA2B,CAC/B,GAAM,CAAE,UAAS,SAAU,EAAA,iBAAiB,EACtC,EAAe,CACnB,EAAQ,GACR,EAAQ,oBAAsB,EAC9B,CACF,EAEA,OAAO,EAAA,SAAS,CACd,SAAU,CACR,SACA,SACA,EACA,QACA,GAAG,CACL,EACA,QAAS,MAAO,CAAE,YAMT,EAAwB,MALE,EAAO,WAAW,CACjD,SAAU,CAAC,GAAG,EAAA,0BAA2B,GAAG,CAAY,EACxD,QAAS,EAAA,8BACT,UAAW,EAAA,0BACb,CAAC,EACkD,KAAM,IAAI,IAAO,CAAC,EAEvE,UAAW,EAAA,2BACX,OAAQ,EAAA,uBACV,CAAC,CACH,EASa,MAAmC,CAC9C,GAAM,CAAE,UAAS,SAAU,EAAA,iBAAiB,EACtC,CAAE,OAAM,WAAY,EAAmB,EACvC,EAAW,EAAA,mBAAoB,GAAU,EAAM,QAAQ,EACvD,EAAwB,EAAA,mBAC3B,GAAU,EAAM,qBACnB,EACM,EAAa,EAAA,mBAAoB,GAAU,EAAM,UAAU,EAEjE,EAAA,QAAM,cAAgB,CACpB,EAAW,CACb,EAAG,CAAC,EAAQ,GAAI,EAAQ,mBAAoB,EAAO,CAAU,CAAC,EAE9D,EAAA,QAAM,cAAgB,CACpB,GAAI,CAAC,EAAM,OACX,IAAM,EAAa,IAAI,IACrB,EACG,OAAQ,GAAU,EAAM,IAAI,EAC5B,IAAK,GAAU,GAAG,EAAmB,GAAG,EAAM,MAAM,CACzD,EACM,EAAe,EAAK,KAAM,GAAU,EAAM,OAAO,EACvD,EAAS,CACP,aACA,aAAc,EACV,GAAG,EAAmB,GAAG,EAAa,OACtC,IACN,CAAC,CACH,EAAG,CAAC,EAAM,CAAQ,CAAC,EAEnB,EAAA,QAAM,cAAgB,CAChB,GAAS,EAAsB,CACrC,EAAG,CAAC,EAAS,CAAqB,CAAC,CACrC,EAQa,MACX,EAAA,mBAAoB,GAAU,EAAM,UAAU,EAOnC,MACX,EAAA,mBAAoB,GAAU,EAAM,YAAY"}
@@ -0,0 +1,23 @@
1
+ import type { FreeModelSet } from "#/utils/format-model-name";
2
+ /**
3
+ * Fetches the DB-driven free / default flags once and mirrors them into the
4
+ * {@link useFreeModelsStore}. Mount this high in the tree (inside the query
5
+ * provider). Leaf display components then read the flags synchronously via the
6
+ * {@link useFreeModels} / {@link useDefaultModel} zustand selectors, so they
7
+ * stay renderable in isolation without a QueryClientProvider in scope.
8
+ */
9
+ export declare const useHydrateFreeModels: () => void;
10
+ /**
11
+ * Set of free ``openhands/<model>`` ids, sourced from the backend model list
12
+ * (DB-driven on cloud via the `free` flag). Returns an empty set on backends
13
+ * without free metadata (e.g. the local agent-server), so callers uniformly
14
+ * treat "not in set" as paid and the frontend keeps no hardcoded free list.
15
+ */
16
+ export declare const useFreeModels: () => FreeModelSet;
17
+ /**
18
+ * DB-driven default OpenHands model id (``openhands/<model>``), used to
19
+ * preselect a model on onboarding and when creating a new OpenHands model.
20
+ * Returns `null` on backends without default metadata.
21
+ */
22
+ export declare const useDefaultModel: () => string | null;
23
+ export declare const useDefaultModelReady: () => boolean;
@@ -0,0 +1,67 @@
1
+ import { useQuery as e } from "../../node_modules/@tanstack/react-query/build/modern/useQuery.js";
2
+ import { useActiveBackend as t } from "../../contexts/active-backend-context.js";
3
+ import n from "../../api/config-service/config-service.api.js";
4
+ import { useFreeModelsStore as r } from "../../stores/free-models-store.js";
5
+ import { VERIFIED_MODELS_GC_TIME as i, VERIFIED_MODELS_QUERY_KEY as a, VERIFIED_MODELS_STALE_TIME as o, fetchVerifiedModelsByProvider as s } from "./use-verified-models.js";
6
+ import c from "react";
7
+ //#region src/hooks/query/use-free-models.ts
8
+ var l = "openhands", u = 100, d = 10;
9
+ async function f(e, t, r, i) {
10
+ if (i >= d) throw Error(`Too many pagination requests while fetching OpenHands models (depth=${i})`);
11
+ let a = await n.searchModels({
12
+ provider__eq: l,
13
+ limit: u,
14
+ ...t ? { page_id: t } : {}
15
+ }, e);
16
+ if (!a.next_page_id) return a.items;
17
+ if (r.has(a.next_page_id)) throw Error(`Repeated page id while fetching OpenHands models: ${a.next_page_id}`);
18
+ r.add(a.next_page_id);
19
+ let o = await f(e, a.next_page_id, r, i + 1);
20
+ return [...a.items, ...o];
21
+ }
22
+ var p = () => {
23
+ let { backend: n, orgId: r } = t(), c = [
24
+ n.id,
25
+ n.connectionRevision ?? 0,
26
+ r
27
+ ];
28
+ return e({
29
+ queryKey: [
30
+ "config",
31
+ "models",
32
+ l,
33
+ "flags",
34
+ ...c
35
+ ],
36
+ queryFn: async ({ client: e }) => f(await e.fetchQuery({
37
+ queryKey: [...a, ...c],
38
+ queryFn: s,
39
+ staleTime: o
40
+ }), null, /* @__PURE__ */ new Set(), 0),
41
+ staleTime: o,
42
+ gcTime: i
43
+ });
44
+ }, m = () => {
45
+ let { backend: e, orgId: n } = t(), { data: i, isError: a } = p(), o = r((e) => e.setFlags), s = r((e) => e.markDefaultModelReady), u = r((e) => e.resetFlags);
46
+ c.useEffect(() => {
47
+ u();
48
+ }, [
49
+ e.id,
50
+ e.connectionRevision,
51
+ n,
52
+ u
53
+ ]), c.useEffect(() => {
54
+ if (!i) return;
55
+ let e = new Set(i.filter((e) => e.free).map((e) => `${l}/${e.name}`)), t = i.find((e) => e.default);
56
+ o({
57
+ freeModels: e,
58
+ defaultModel: t ? `${l}/${t.name}` : null
59
+ });
60
+ }, [i, o]), c.useEffect(() => {
61
+ a && s();
62
+ }, [a, s]);
63
+ }, h = () => r((e) => e.freeModels), g = () => r((e) => e.defaultModel);
64
+ //#endregion
65
+ export { g as useDefaultModel, h as useFreeModels, m as useHydrateFreeModels };
66
+
67
+ //# sourceMappingURL=use-free-models.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-free-models.js","names":[],"sources":["../../../src/hooks/query/use-free-models.ts"],"sourcesContent":["import React from \"react\";\nimport { useQuery } from \"@tanstack/react-query\";\nimport ConfigService from \"#/api/config-service/config-service.api\";\nimport type { LLMModel } from \"#/api/config-service/config-service.types\";\nimport type { FreeModelSet } from \"#/utils/format-model-name\";\nimport { useFreeModelsStore } from \"#/stores/free-models-store\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n VERIFIED_MODELS_GC_TIME,\n VERIFIED_MODELS_QUERY_KEY,\n VERIFIED_MODELS_STALE_TIME,\n fetchVerifiedModelsByProvider,\n} from \"./use-verified-models\";\n\n/**\n * Provider whose models carry free / default metadata. Both are\n * OpenHands-managed concepts, so they are scoped to the `openhands` provider.\n */\nconst OPENHANDS_PROVIDER = \"openhands\";\nconst CLOUD_MODEL_SEARCH_PAGE_LIMIT = 100;\nconst MAX_PAGINATION_DEPTH = 10;\n\nasync function fetchAllOpenHandsModels(\n verifiedByProvider: Record<string, string[]>,\n pageId: string | null | undefined,\n seenPageIds: Set<string>,\n depth: number,\n): Promise<LLMModel[]> {\n if (depth >= MAX_PAGINATION_DEPTH) {\n throw new Error(\n `Too many pagination requests while fetching OpenHands models (depth=${depth})`,\n );\n }\n\n const page = await ConfigService.searchModels(\n {\n provider__eq: OPENHANDS_PROVIDER,\n limit: CLOUD_MODEL_SEARCH_PAGE_LIMIT,\n ...(pageId ? { page_id: pageId } : {}),\n },\n verifiedByProvider,\n );\n\n if (!page.next_page_id) {\n return page.items;\n }\n\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n `Repeated page id while fetching OpenHands models: ${page.next_page_id}`,\n );\n }\n seenPageIds.add(page.next_page_id);\n\n const rest = await fetchAllOpenHandsModels(\n verifiedByProvider,\n page.next_page_id,\n seenPageIds,\n depth + 1,\n );\n return [...page.items, ...rest];\n}\n\n/**\n * Fetches the `openhands` provider's models with their DB-driven `free` /\n * `default` flags (the same channel that carries `verified`).\n */\nconst useOpenHandsModels = () => {\n const { backend, orgId } = useActiveBackend();\n const backendScope = [\n backend.id,\n backend.connectionRevision ?? 0,\n orgId,\n ] as const;\n\n return useQuery({\n queryKey: [\n \"config\",\n \"models\",\n OPENHANDS_PROVIDER,\n \"flags\",\n ...backendScope,\n ],\n queryFn: async ({ client }): Promise<LLMModel[]> => {\n const verifiedByProvider = await client.fetchQuery({\n queryKey: [...VERIFIED_MODELS_QUERY_KEY, ...backendScope],\n queryFn: fetchVerifiedModelsByProvider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n });\n return fetchAllOpenHandsModels(verifiedByProvider, null, new Set(), 0);\n },\n staleTime: VERIFIED_MODELS_STALE_TIME,\n gcTime: VERIFIED_MODELS_GC_TIME,\n });\n};\n\n/**\n * Fetches the DB-driven free / default flags once and mirrors them into the\n * {@link useFreeModelsStore}. Mount this high in the tree (inside the query\n * provider). Leaf display components then read the flags synchronously via the\n * {@link useFreeModels} / {@link useDefaultModel} zustand selectors, so they\n * stay renderable in isolation without a QueryClientProvider in scope.\n */\nexport const useHydrateFreeModels = (): void => {\n const { backend, orgId } = useActiveBackend();\n const { data, isError } = useOpenHandsModels();\n const setFlags = useFreeModelsStore((state) => state.setFlags);\n const markDefaultModelReady = useFreeModelsStore(\n (state) => state.markDefaultModelReady,\n );\n const resetFlags = useFreeModelsStore((state) => state.resetFlags);\n\n React.useEffect(() => {\n resetFlags();\n }, [backend.id, backend.connectionRevision, orgId, resetFlags]);\n\n React.useEffect(() => {\n if (!data) return;\n const freeModels = new Set(\n data\n .filter((model) => model.free)\n .map((model) => `${OPENHANDS_PROVIDER}/${model.name}`),\n );\n const defaultEntry = data.find((model) => model.default);\n setFlags({\n freeModels,\n defaultModel: defaultEntry\n ? `${OPENHANDS_PROVIDER}/${defaultEntry.name}`\n : null,\n });\n }, [data, setFlags]);\n\n React.useEffect(() => {\n if (isError) markDefaultModelReady();\n }, [isError, markDefaultModelReady]);\n};\n\n/**\n * Set of free ``openhands/<model>`` ids, sourced from the backend model list\n * (DB-driven on cloud via the `free` flag). Returns an empty set on backends\n * without free metadata (e.g. the local agent-server), so callers uniformly\n * treat \"not in set\" as paid and the frontend keeps no hardcoded free list.\n */\nexport const useFreeModels = (): FreeModelSet =>\n useFreeModelsStore((state) => state.freeModels);\n\n/**\n * DB-driven default OpenHands model id (``openhands/<model>``), used to\n * preselect a model on onboarding and when creating a new OpenHands model.\n * Returns `null` on backends without default metadata.\n */\nexport const useDefaultModel = (): string | null =>\n useFreeModelsStore((state) => state.defaultModel);\n\nexport const useDefaultModelReady = (): boolean =>\n useFreeModelsStore((state) => state.defaultModelReady);\n"],"mappings":";;;;;;;AAkBA,IAAM,IAAqB,aACrB,IAAgC,KAChC,IAAuB;AAE7B,eAAe,EACb,GACA,GACA,GACA,GACqB;CACrB,IAAI,KAAS,GACX,MAAU,MACR,uEAAuE,EAAM,EAC/E;CAGF,IAAM,IAAO,MAAM,EAAc,aAC/B;EACE,cAAc;EACd,OAAO;EACP,GAAI,IAAS,EAAE,SAAS,EAAO,IAAI,CAAC;CACtC,GACA,CACF;CAEA,IAAI,CAAC,EAAK,cACR,OAAO,EAAK;CAGd,IAAI,EAAY,IAAI,EAAK,YAAY,GACnC,MAAU,MACR,qDAAqD,EAAK,cAC5D;CAEF,EAAY,IAAI,EAAK,YAAY;CAEjC,IAAM,IAAO,MAAM,EACjB,GACA,EAAK,cACL,GACA,IAAQ,CACV;CACA,OAAO,CAAC,GAAG,EAAK,OAAO,GAAG,CAAI;AAChC;AAMA,IAAM,UAA2B;CAC/B,IAAM,EAAE,YAAS,aAAU,EAAiB,GACtC,IAAe;EACnB,EAAQ;EACR,EAAQ,sBAAsB;EAC9B;CACF;CAEA,OAAO,EAAS;EACd,UAAU;GACR;GACA;GACA;GACA;GACA,GAAG;EACL;EACA,SAAS,OAAO,EAAE,gBAMT,EAAwB,MALE,EAAO,WAAW;GACjD,UAAU,CAAC,GAAG,GAA2B,GAAG,CAAY;GACxD,SAAS;GACT,WAAW;EACb,CAAC,GACkD,sBAAM,IAAI,IAAI,GAAG,CAAC;EAEvE,WAAW;EACX,QAAQ;CACV,CAAC;AACH,GASa,UAAmC;CAC9C,IAAM,EAAE,YAAS,aAAU,EAAiB,GACtC,EAAE,SAAM,eAAY,EAAmB,GACvC,IAAW,GAAoB,MAAU,EAAM,QAAQ,GACvD,IAAwB,GAC3B,MAAU,EAAM,qBACnB,GACM,IAAa,GAAoB,MAAU,EAAM,UAAU;CAsBjE,AApBA,EAAM,gBAAgB;EACpB,EAAW;CACb,GAAG;EAAC,EAAQ;EAAI,EAAQ;EAAoB;EAAO;CAAU,CAAC,GAE9D,EAAM,gBAAgB;EACpB,IAAI,CAAC,GAAM;EACX,IAAM,IAAa,IAAI,IACrB,EACG,QAAQ,MAAU,EAAM,IAAI,EAC5B,KAAK,MAAU,GAAG,EAAmB,GAAG,EAAM,MAAM,CACzD,GACM,IAAe,EAAK,MAAM,MAAU,EAAM,OAAO;EACvD,EAAS;GACP;GACA,cAAc,IACV,GAAG,EAAmB,GAAG,EAAa,SACtC;EACN,CAAC;CACH,GAAG,CAAC,GAAM,CAAQ,CAAC,GAEnB,EAAM,gBAAgB;EACpB,AAAI,KAAS,EAAsB;CACrC,GAAG,CAAC,GAAS,CAAqB,CAAC;AACrC,GAQa,UACX,GAAoB,MAAU,EAAM,UAAU,GAOnC,UACX,GAAoB,MAAU,EAAM,YAAY"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-local-git-info.cjs","names":[],"sources":["../../../src/hooks/query/use-local-git-info.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport { useRef } from \"react\";\n\nimport type { CommandResult } from \"#/api/runtime-service/agent-server-runtime-service\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useRuntimeIsReady } from \"#/hooks/use-runtime-is-ready\";\nimport { useBashCommandRunner } from \"#/hooks/use-bash-command-runner\";\nimport { Provider } from \"#/types/settings\";\nimport { parseGitRemoteUrl } from \"#/utils/parse-git-remote-url\";\n\nexport interface LocalGitInfo {\n repository: string | null;\n branch: string | null;\n provider: Provider | null;\n remoteUrl: string | null;\n}\n\nconst EMPTY_LOCAL_GIT_INFO: LocalGitInfo = {\n repository: null,\n branch: null,\n provider: null,\n remoteUrl: null,\n};\n\ntype RunCommand = (\n command: string,\n cwd: string,\n timeout: number,\n) => Promise<CommandResult>;\n\n// Single shell script that replaces the former probeGitInfoAtDir +\n// probeNestedRepoInDir pair. It runs as one bash WebSocket round-trip:\n// 1. Read the origin remote URL and current branch at the workspace root.\n// 2. If neither is set, search for exactly one nested git repo up to 4\n// levels deep and repeat the probe there.\n// Output: two lines — <remote-url>\\n<branch> — either may be empty.\nconst GIT_INFO_COMMAND = [\n \"r=$(git remote get-url origin 2>/dev/null)\",\n \"b=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)\",\n 'if [ -z \"$r$b\" ]; then',\n \"n=$(find . -mindepth 2 -maxdepth 4 -name .git 2>/dev/null | cut -c3- | sed 's|/.git$||' | sort -u)\",\n \"c=$(printf '%s\\\\n' \\\"$n\\\" | grep -c '[^[:space:]]')\",\n 'if [ \"$c\" = \"1\" ] && [ -n \"$n\" ]; then',\n 'r=$(git -C \"$n\" remote get-url origin 2>/dev/null)',\n 'b=$(git -C \"$n\" rev-parse --abbrev-ref HEAD 2>/dev/null)',\n \"fi\",\n \"fi\",\n 'printf \\'%s\\\\n%s\\' \"$r\" \"$b\"',\n].join(\"\\n\");\n\nasync function probeGitInfo(\n run: RunCommand,\n directory: string,\n): Promise<LocalGitInfo> {\n const result = await run(GIT_INFO_COMMAND, directory, 10);\n if (result.exit_code !== 0) return EMPTY_LOCAL_GIT_INFO;\n\n const nl = result.stdout.indexOf(\"\\n\");\n const remoteUrl = (\n nl >= 0 ? result.stdout.slice(0, nl) : result.stdout\n ).trim();\n const rawBranch = (nl >= 0 ? result.stdout.slice(nl + 1) : \"\").trim();\n const branch = rawBranch && rawBranch !== \"HEAD\" ? rawBranch : null;\n\n if (!remoteUrl && !branch) return EMPTY_LOCAL_GIT_INFO;\n\n const parsedRemote = parseGitRemoteUrl(remoteUrl);\n return {\n repository: parsedRemote?.repository ?? null,\n provider: parsedRemote?.provider ?? null,\n remoteUrl: remoteUrl || null,\n branch,\n };\n}\n\n/**\n * Probe git metadata for a **local** backend's workspace checkout by\n * shelling out via the agent server using a single consolidated bash\n * script (see `GIT_INFO_COMMAND`).\n *\n * Local-only by design. On cloud backends the conversation metadata\n * (`selected_repository`, `git_provider`, `selected_branch`) is the\n * source of truth, and probing via `/api/bash/execute_bash_command`\n * would (a) leak the user's local `getAgentServerWorkingDir()` path to\n * the cloud runtime when `workspace.working_dir` is missing, and\n * (b) hit a bash endpoint we don't want the frontend driving on cloud.\n *\n * On local, we keep the probe enabled until the active conversation\n * has a complete repo tuple so the control bar can recover from\n * partial metadata hydration after connect/clone flows.\n *\n * Returns `null` fields when the working dir is not a git checkout —\n * callers should treat that the same as \"no repo detected\".\n */\nexport const useLocalGitInfo = () => {\n const { data: conversation } = useActiveConversation();\n const runtimeIsReady = useRuntimeIsReady();\n const { backend } = useActiveBackend();\n const isLocalBackend = backend.kind === \"local\";\n\n const conversationId = conversation?.id;\n const conversationUrl = conversation?.conversation_url;\n const sessionApiKey = conversation?.session_api_key;\n const workingDir = conversation?.workspace?.working_dir?.trim();\n const hasConversationRepo = !!conversation?.selected_repository;\n const hasConversationProvider = !!conversation?.git_provider;\n const hasConversationBranch = !!conversation?.selected_branch;\n\n const queryEnabled =\n isLocalBackend &&\n runtimeIsReady &&\n !!conversationId &&\n !!workingDir &&\n (!hasConversationRepo ||\n !hasConversationProvider ||\n !hasConversationBranch);\n\n // Persistent WebSocket connection to the bash-events endpoint. The\n // connection is opened when the query is enabled and closed on unmount or\n // when the conversation changes.\n const runCommand = useBashCommandRunner(\n conversationUrl,\n sessionApiKey,\n queryEnabled,\n );\n\n // Keep a ref so queryFn can call the latest runner without capturing it\n // as a queryKey dependency (runCommand is stable but the linter can't\n // infer that).\n const runCommandRef = useRef(runCommand);\n runCommandRef.current = runCommand;\n\n // runCommandRef is a ref (always stable); the linter cannot infer this so\n // we disable the exhaustive-deps check here.\n // eslint-disable-next-line @tanstack/query/exhaustive-deps\n return useQuery<LocalGitInfo>({\n queryKey: [\n \"local-git-info\",\n conversationId,\n conversationUrl,\n sessionApiKey,\n workingDir,\n ],\n queryFn: async () => {\n const run: RunCommand = (command, cwd, timeout) =>\n runCommandRef.current(command, cwd, timeout);\n // workingDir is guaranteed non-empty by the queryEnabled guard above.\n return probeGitInfo(run, workingDir!);\n },\n enabled: queryEnabled,\n retry: false,\n // Re-probe the workspace every 10s so the UI reflects branch/repo\n // changes (e.g. `git checkout`, adding a remote) without requiring a\n // manual refresh when there is no `selected_repository` recorded on\n // the conversation. Commands now run over the persistent WebSocket\n // connection rather than individual REST calls.\n staleTime: 10_000,\n refetchInterval: 10_000,\n gcTime: 1000 * 60 * 5,\n meta: { disableToast: true },\n });\n};\n"],"mappings":"sYAkBA,IAAM,EAAqC,CACzC,WAAY,KACZ,OAAQ,KACR,SAAU,KACV,UAAW,IACb,EAcM,EAAmB,CACvB,6CACA,mDACA,yBACA,qGACA,oDACA,yCACA,qDACA,2DACA,KACA,KACA,4BACF,EAAE,KAAK;CAAI,EAEX,eAAe,EACb,EACA,EACuB,CACvB,IAAM,EAAS,MAAM,EAAI,EAAkB,EAAW,EAAE,EACxD,GAAI,EAAO,YAAc,EAAG,OAAO,EAEnC,IAAM,EAAK,EAAO,OAAO,QAAQ;CAAI,EAC/B,GACJ,GAAM,EAAI,EAAO,OAAO,MAAM,EAAG,CAAE,EAAI,EAAO,QAC9C,KAAK,EACD,GAAa,GAAM,EAAI,EAAO,OAAO,MAAM,EAAK,CAAC,EAAI,IAAI,KAAK,EAC9D,EAAS,GAAa,IAAc,OAAS,EAAY,KAE/D,GAAI,CAAC,GAAa,CAAC,EAAQ,OAAO,EAElC,IAAM,EAAe,EAAA,kBAAkB,CAAS,EAChD,MAAO,CACL,WAAY,GAAc,YAAc,KACxC,SAAU,GAAc,UAAY,KACpC,UAAW,GAAa,KACxB,QACF,CACF,CAqBA,IAAa,MAAwB,CACnC,GAAM,CAAE,KAAM,GAAiB,EAAA,sBAAsB,EAC/C,EAAiB,EAAA,kBAAkB,EACnC,CAAE,WAAY,EAAA,iBAAiB,EAC/B,EAAiB,EAAQ,OAAS,QAElC,EAAiB,GAAc,GAC/B,EAAkB,GAAc,iBAChC,EAAgB,GAAc,gBAC9B,EAAa,GAAc,WAAW,aAAa,KAAK,EACxD,EAAsB,CAAC,CAAC,GAAc,oBACtC,EAA0B,CAAC,CAAC,GAAc,aAC1C,EAAwB,CAAC,CAAC,GAAc,gBAExC,EACJ,GACA,GACA,CAAC,CAAC,GACF,CAAC,CAAC,IACD,CAAC,GACA,CAAC,GACD,CAAC,GAKC,EAAa,EAAA,qBACjB,EACA,EACA,CACF,EAKM,GAAA,EAAA,EAAA,QAAuB,CAAU,EAMvC,MALA,GAAc,QAAU,EAKjB,EAAA,SAAuB,CAC5B,SAAU,CACR,iBACA,EACA,EACA,EACA,CACF,EACA,QAAS,SAIA,GAHkB,EAAS,EAAK,IACrC,EAAc,QAAQ,EAAS,EAAK,CAAO,EAEpB,CAAW,EAEtC,QAAS,EACT,MAAO,GAMP,UAAW,IACX,gBAAiB,IACjB,OAAQ,IAAO,GAAK,EACpB,KAAM,CAAE,aAAc,EAAK,CAC7B,CAAC,CACH"}
1
+ {"version":3,"file":"use-local-git-info.cjs","names":[],"sources":["../../../src/hooks/query/use-local-git-info.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport { useRef } from \"react\";\n\nimport type { CommandResult } from \"#/api/runtime-service/agent-server-runtime-service\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useRuntimeIsReady } from \"#/hooks/use-runtime-is-ready\";\nimport { useBashCommandRunner } from \"#/hooks/use-bash-command-runner\";\nimport { Provider } from \"#/types/settings\";\nimport { parseGitRemoteUrl } from \"#/utils/parse-git-remote-url\";\n\nexport interface LocalGitInfo {\n repository: string | null;\n branch: string | null;\n provider: Provider | null;\n remoteUrl: string | null;\n}\n\nconst EMPTY_LOCAL_GIT_INFO: LocalGitInfo = {\n repository: null,\n branch: null,\n provider: null,\n remoteUrl: null,\n};\n\ntype RunCommand = (\n command: string,\n cwd: string,\n timeout: number,\n) => Promise<CommandResult>;\n\n// Single shell script that replaces the former probeGitInfoAtDir +\n// probeNestedRepoInDir pair. It runs as one bash WebSocket round-trip:\n// 1. Read the origin remote URL and current branch at the workspace root.\n// 2. If neither is set, search for exactly one nested git repo up to 4\n// levels deep and repeat the probe there.\n// Output: two lines — <remote-url>\\n<branch> — either may be empty.\nconst GIT_INFO_COMMAND = [\n \"r=$(git remote get-url origin 2>/dev/null)\",\n \"b=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)\",\n 'if [ -z \"$r$b\" ]; then',\n \"n=$(find . -mindepth 2 -maxdepth 4 -name .git 2>/dev/null | cut -c3- | sed 's|/.git$||' | sort -u)\",\n \"c=$(printf '%s\\\\n' \\\"$n\\\" | grep -c '[^[:space:]]')\",\n 'if [ \"$c\" = \"1\" ] && [ -n \"$n\" ]; then',\n 'r=$(git -C \"$n\" remote get-url origin 2>/dev/null)',\n 'b=$(git -C \"$n\" rev-parse --abbrev-ref HEAD 2>/dev/null)',\n \"fi\",\n \"fi\",\n 'printf \\'%s\\\\n%s\\' \"$r\" \"$b\"',\n].join(\"\\n\");\n\nasync function probeGitInfo(\n run: RunCommand,\n directory: string,\n): Promise<LocalGitInfo> {\n const result = await run(GIT_INFO_COMMAND, directory, 10);\n if (result.exit_code !== 0) return EMPTY_LOCAL_GIT_INFO;\n\n const nl = result.stdout.indexOf(\"\\n\");\n const remoteUrl = (\n nl >= 0 ? result.stdout.slice(0, nl) : result.stdout\n ).trim();\n const rawBranch = (nl >= 0 ? result.stdout.slice(nl + 1) : \"\").trim();\n const branch = rawBranch && rawBranch !== \"HEAD\" ? rawBranch : null;\n\n if (!remoteUrl && !branch) return EMPTY_LOCAL_GIT_INFO;\n\n const parsedRemote = parseGitRemoteUrl(remoteUrl);\n return {\n repository: parsedRemote?.repository ?? null,\n provider: parsedRemote?.provider ?? null,\n remoteUrl: remoteUrl || null,\n branch,\n };\n}\n\n/**\n * Probe git metadata for a **local** backend's workspace checkout by\n * shelling out via the agent server using a single consolidated bash\n * script (see `GIT_INFO_COMMAND`).\n *\n * Local-only by design. On cloud backends the conversation metadata\n * (`selected_repository`, `git_provider`, `selected_branch`) is the\n * source of truth, and probing via `/api/bash/execute_bash_command`\n * would (a) leak the user's local `getAgentServerWorkingDir()` path to\n * the cloud runtime when `workspace.working_dir` is missing, and\n * (b) hit a bash endpoint we don't want the frontend driving on cloud.\n *\n * On local, we keep the probe enabled until the active conversation\n * has a complete repo tuple so the control bar can recover from\n * partial metadata hydration after connect/clone flows.\n *\n * Returns `null` fields when the working dir is not a git checkout —\n * callers should treat that the same as \"no repo detected\".\n */\nexport const useLocalGitInfo = () => {\n const { data: conversation } = useActiveConversation();\n const runtimeIsReady = useRuntimeIsReady();\n const { backend } = useActiveBackend();\n const isLocalBackend = backend.kind === \"local\";\n\n const conversationId = conversation?.id;\n const conversationUrl = conversation?.conversation_url;\n const sessionApiKey = conversation?.session_api_key;\n const workingDir = conversation?.workspace?.working_dir?.trim();\n const hasConversationRepo = !!conversation?.selected_repository;\n const hasConversationProvider = !!conversation?.git_provider;\n const hasConversationBranch = !!conversation?.selected_branch;\n\n const queryEnabled =\n isLocalBackend &&\n runtimeIsReady &&\n !!conversationId &&\n !!workingDir &&\n (!hasConversationRepo ||\n !hasConversationProvider ||\n !hasConversationBranch);\n\n // Persistent WebSocket connection to the bash-events endpoint. The\n // connection is opened when the query is enabled and closed on unmount or\n // when the conversation changes.\n const runCommand = useBashCommandRunner(\n conversationUrl,\n sessionApiKey,\n queryEnabled,\n );\n\n // Keep a ref so queryFn can call the latest runner without capturing it\n // as a queryKey dependency (runCommand is stable but the linter can't\n // infer that).\n const runCommandRef = useRef(runCommand);\n runCommandRef.current = runCommand;\n\n // runCommandRef is a ref (always stable); the linter cannot infer this so\n // we disable the exhaustive-deps check here.\n\n return useQuery<LocalGitInfo>({\n queryKey: [\n \"local-git-info\",\n conversationId,\n conversationUrl,\n sessionApiKey,\n workingDir,\n ],\n queryFn: async () => {\n const run: RunCommand = (command, cwd, timeout) =>\n runCommandRef.current(command, cwd, timeout);\n // workingDir is guaranteed non-empty by the queryEnabled guard above.\n return probeGitInfo(run, workingDir!);\n },\n enabled: queryEnabled,\n retry: false,\n // Re-probe the workspace every 10s so the UI reflects branch/repo\n // changes (e.g. `git checkout`, adding a remote) without requiring a\n // manual refresh when there is no `selected_repository` recorded on\n // the conversation. Commands now run over the persistent WebSocket\n // connection rather than individual REST calls.\n staleTime: 10_000,\n refetchInterval: 10_000,\n gcTime: 1000 * 60 * 5,\n meta: { disableToast: true },\n });\n};\n"],"mappings":"sYAkBA,IAAM,EAAqC,CACzC,WAAY,KACZ,OAAQ,KACR,SAAU,KACV,UAAW,IACb,EAcM,EAAmB,CACvB,6CACA,mDACA,yBACA,qGACA,oDACA,yCACA,qDACA,2DACA,KACA,KACA,4BACF,EAAE,KAAK;CAAI,EAEX,eAAe,EACb,EACA,EACuB,CACvB,IAAM,EAAS,MAAM,EAAI,EAAkB,EAAW,EAAE,EACxD,GAAI,EAAO,YAAc,EAAG,OAAO,EAEnC,IAAM,EAAK,EAAO,OAAO,QAAQ;CAAI,EAC/B,GACJ,GAAM,EAAI,EAAO,OAAO,MAAM,EAAG,CAAE,EAAI,EAAO,QAC9C,KAAK,EACD,GAAa,GAAM,EAAI,EAAO,OAAO,MAAM,EAAK,CAAC,EAAI,IAAI,KAAK,EAC9D,EAAS,GAAa,IAAc,OAAS,EAAY,KAE/D,GAAI,CAAC,GAAa,CAAC,EAAQ,OAAO,EAElC,IAAM,EAAe,EAAA,kBAAkB,CAAS,EAChD,MAAO,CACL,WAAY,GAAc,YAAc,KACxC,SAAU,GAAc,UAAY,KACpC,UAAW,GAAa,KACxB,QACF,CACF,CAqBA,IAAa,MAAwB,CACnC,GAAM,CAAE,KAAM,GAAiB,EAAA,sBAAsB,EAC/C,EAAiB,EAAA,kBAAkB,EACnC,CAAE,WAAY,EAAA,iBAAiB,EAC/B,EAAiB,EAAQ,OAAS,QAElC,EAAiB,GAAc,GAC/B,EAAkB,GAAc,iBAChC,EAAgB,GAAc,gBAC9B,EAAa,GAAc,WAAW,aAAa,KAAK,EACxD,EAAsB,CAAC,CAAC,GAAc,oBACtC,EAA0B,CAAC,CAAC,GAAc,aAC1C,EAAwB,CAAC,CAAC,GAAc,gBAExC,EACJ,GACA,GACA,CAAC,CAAC,GACF,CAAC,CAAC,IACD,CAAC,GACA,CAAC,GACD,CAAC,GAKC,EAAa,EAAA,qBACjB,EACA,EACA,CACF,EAKM,GAAA,EAAA,EAAA,QAAuB,CAAU,EAMvC,MALA,GAAc,QAAU,EAKjB,EAAA,SAAuB,CAC5B,SAAU,CACR,iBACA,EACA,EACA,EACA,CACF,EACA,QAAS,SAIA,GAHkB,EAAS,EAAK,IACrC,EAAc,QAAQ,EAAS,EAAK,CAAO,EAEpB,CAAW,EAEtC,QAAS,EACT,MAAO,GAMP,UAAW,IACX,gBAAiB,IACjB,OAAQ,IAAO,GAAK,EACpB,KAAM,CAAE,aAAc,EAAK,CAC7B,CAAC,CACH"}
@@ -1 +1 @@
1
- {"version":3,"file":"use-local-git-info.js","names":[],"sources":["../../../src/hooks/query/use-local-git-info.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport { useRef } from \"react\";\n\nimport type { CommandResult } from \"#/api/runtime-service/agent-server-runtime-service\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useRuntimeIsReady } from \"#/hooks/use-runtime-is-ready\";\nimport { useBashCommandRunner } from \"#/hooks/use-bash-command-runner\";\nimport { Provider } from \"#/types/settings\";\nimport { parseGitRemoteUrl } from \"#/utils/parse-git-remote-url\";\n\nexport interface LocalGitInfo {\n repository: string | null;\n branch: string | null;\n provider: Provider | null;\n remoteUrl: string | null;\n}\n\nconst EMPTY_LOCAL_GIT_INFO: LocalGitInfo = {\n repository: null,\n branch: null,\n provider: null,\n remoteUrl: null,\n};\n\ntype RunCommand = (\n command: string,\n cwd: string,\n timeout: number,\n) => Promise<CommandResult>;\n\n// Single shell script that replaces the former probeGitInfoAtDir +\n// probeNestedRepoInDir pair. It runs as one bash WebSocket round-trip:\n// 1. Read the origin remote URL and current branch at the workspace root.\n// 2. If neither is set, search for exactly one nested git repo up to 4\n// levels deep and repeat the probe there.\n// Output: two lines — <remote-url>\\n<branch> — either may be empty.\nconst GIT_INFO_COMMAND = [\n \"r=$(git remote get-url origin 2>/dev/null)\",\n \"b=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)\",\n 'if [ -z \"$r$b\" ]; then',\n \"n=$(find . -mindepth 2 -maxdepth 4 -name .git 2>/dev/null | cut -c3- | sed 's|/.git$||' | sort -u)\",\n \"c=$(printf '%s\\\\n' \\\"$n\\\" | grep -c '[^[:space:]]')\",\n 'if [ \"$c\" = \"1\" ] && [ -n \"$n\" ]; then',\n 'r=$(git -C \"$n\" remote get-url origin 2>/dev/null)',\n 'b=$(git -C \"$n\" rev-parse --abbrev-ref HEAD 2>/dev/null)',\n \"fi\",\n \"fi\",\n 'printf \\'%s\\\\n%s\\' \"$r\" \"$b\"',\n].join(\"\\n\");\n\nasync function probeGitInfo(\n run: RunCommand,\n directory: string,\n): Promise<LocalGitInfo> {\n const result = await run(GIT_INFO_COMMAND, directory, 10);\n if (result.exit_code !== 0) return EMPTY_LOCAL_GIT_INFO;\n\n const nl = result.stdout.indexOf(\"\\n\");\n const remoteUrl = (\n nl >= 0 ? result.stdout.slice(0, nl) : result.stdout\n ).trim();\n const rawBranch = (nl >= 0 ? result.stdout.slice(nl + 1) : \"\").trim();\n const branch = rawBranch && rawBranch !== \"HEAD\" ? rawBranch : null;\n\n if (!remoteUrl && !branch) return EMPTY_LOCAL_GIT_INFO;\n\n const parsedRemote = parseGitRemoteUrl(remoteUrl);\n return {\n repository: parsedRemote?.repository ?? null,\n provider: parsedRemote?.provider ?? null,\n remoteUrl: remoteUrl || null,\n branch,\n };\n}\n\n/**\n * Probe git metadata for a **local** backend's workspace checkout by\n * shelling out via the agent server using a single consolidated bash\n * script (see `GIT_INFO_COMMAND`).\n *\n * Local-only by design. On cloud backends the conversation metadata\n * (`selected_repository`, `git_provider`, `selected_branch`) is the\n * source of truth, and probing via `/api/bash/execute_bash_command`\n * would (a) leak the user's local `getAgentServerWorkingDir()` path to\n * the cloud runtime when `workspace.working_dir` is missing, and\n * (b) hit a bash endpoint we don't want the frontend driving on cloud.\n *\n * On local, we keep the probe enabled until the active conversation\n * has a complete repo tuple so the control bar can recover from\n * partial metadata hydration after connect/clone flows.\n *\n * Returns `null` fields when the working dir is not a git checkout —\n * callers should treat that the same as \"no repo detected\".\n */\nexport const useLocalGitInfo = () => {\n const { data: conversation } = useActiveConversation();\n const runtimeIsReady = useRuntimeIsReady();\n const { backend } = useActiveBackend();\n const isLocalBackend = backend.kind === \"local\";\n\n const conversationId = conversation?.id;\n const conversationUrl = conversation?.conversation_url;\n const sessionApiKey = conversation?.session_api_key;\n const workingDir = conversation?.workspace?.working_dir?.trim();\n const hasConversationRepo = !!conversation?.selected_repository;\n const hasConversationProvider = !!conversation?.git_provider;\n const hasConversationBranch = !!conversation?.selected_branch;\n\n const queryEnabled =\n isLocalBackend &&\n runtimeIsReady &&\n !!conversationId &&\n !!workingDir &&\n (!hasConversationRepo ||\n !hasConversationProvider ||\n !hasConversationBranch);\n\n // Persistent WebSocket connection to the bash-events endpoint. The\n // connection is opened when the query is enabled and closed on unmount or\n // when the conversation changes.\n const runCommand = useBashCommandRunner(\n conversationUrl,\n sessionApiKey,\n queryEnabled,\n );\n\n // Keep a ref so queryFn can call the latest runner without capturing it\n // as a queryKey dependency (runCommand is stable but the linter can't\n // infer that).\n const runCommandRef = useRef(runCommand);\n runCommandRef.current = runCommand;\n\n // runCommandRef is a ref (always stable); the linter cannot infer this so\n // we disable the exhaustive-deps check here.\n // eslint-disable-next-line @tanstack/query/exhaustive-deps\n return useQuery<LocalGitInfo>({\n queryKey: [\n \"local-git-info\",\n conversationId,\n conversationUrl,\n sessionApiKey,\n workingDir,\n ],\n queryFn: async () => {\n const run: RunCommand = (command, cwd, timeout) =>\n runCommandRef.current(command, cwd, timeout);\n // workingDir is guaranteed non-empty by the queryEnabled guard above.\n return probeGitInfo(run, workingDir!);\n },\n enabled: queryEnabled,\n retry: false,\n // Re-probe the workspace every 10s so the UI reflects branch/repo\n // changes (e.g. `git checkout`, adding a remote) without requiring a\n // manual refresh when there is no `selected_repository` recorded on\n // the conversation. Commands now run over the persistent WebSocket\n // connection rather than individual REST calls.\n staleTime: 10_000,\n refetchInterval: 10_000,\n gcTime: 1000 * 60 * 5,\n meta: { disableToast: true },\n });\n};\n"],"mappings":";;;;;;;;AAkBA,IAAM,IAAqC;CACzC,YAAY;CACZ,QAAQ;CACR,UAAU;CACV,WAAW;AACb,GAcM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,EAAE,KAAK,IAAI;AAEX,eAAe,EACb,GACA,GACuB;CACvB,IAAM,IAAS,MAAM,EAAI,GAAkB,GAAW,EAAE;CACxD,IAAI,EAAO,cAAc,GAAG,OAAO;CAEnC,IAAM,IAAK,EAAO,OAAO,QAAQ,IAAI,GAC/B,KACJ,KAAM,IAAI,EAAO,OAAO,MAAM,GAAG,CAAE,IAAI,EAAO,QAC9C,KAAK,GACD,KAAa,KAAM,IAAI,EAAO,OAAO,MAAM,IAAK,CAAC,IAAI,IAAI,KAAK,GAC9D,IAAS,KAAa,MAAc,SAAS,IAAY;CAE/D,IAAI,CAAC,KAAa,CAAC,GAAQ,OAAO;CAElC,IAAM,IAAe,EAAkB,CAAS;CAChD,OAAO;EACL,YAAY,GAAc,cAAc;EACxC,UAAU,GAAc,YAAY;EACpC,WAAW,KAAa;EACxB;CACF;AACF;AAqBA,IAAa,UAAwB;CACnC,IAAM,EAAE,MAAM,MAAiB,EAAsB,GAC/C,IAAiB,EAAkB,GACnC,EAAE,eAAY,EAAiB,GAC/B,IAAiB,EAAQ,SAAS,SAElC,IAAiB,GAAc,IAC/B,IAAkB,GAAc,kBAChC,IAAgB,GAAc,iBAC9B,IAAa,GAAc,WAAW,aAAa,KAAK,GACxD,IAAsB,CAAC,CAAC,GAAc,qBACtC,IAA0B,CAAC,CAAC,GAAc,cAC1C,IAAwB,CAAC,CAAC,GAAc,iBAExC,IACJ,KACA,KACA,CAAC,CAAC,KACF,CAAC,CAAC,MACD,CAAC,KACA,CAAC,KACD,CAAC,IAKC,IAAa,EACjB,GACA,GACA,CACF,GAKM,IAAgB,EAAO,CAAU;CAMvC,OALA,EAAc,UAAU,GAKjB,EAAuB;EAC5B,UAAU;GACR;GACA;GACA;GACA;GACA;EACF;EACA,SAAS,YAIA,GAHkB,GAAS,GAAK,MACrC,EAAc,QAAQ,GAAS,GAAK,CAAO,GAEpB,CAAW;EAEtC,SAAS;EACT,OAAO;EAMP,WAAW;EACX,iBAAiB;EACjB,QAAQ,MAAO,KAAK;EACpB,MAAM,EAAE,cAAc,GAAK;CAC7B,CAAC;AACH"}
1
+ {"version":3,"file":"use-local-git-info.js","names":[],"sources":["../../../src/hooks/query/use-local-git-info.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport { useRef } from \"react\";\n\nimport type { CommandResult } from \"#/api/runtime-service/agent-server-runtime-service\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useRuntimeIsReady } from \"#/hooks/use-runtime-is-ready\";\nimport { useBashCommandRunner } from \"#/hooks/use-bash-command-runner\";\nimport { Provider } from \"#/types/settings\";\nimport { parseGitRemoteUrl } from \"#/utils/parse-git-remote-url\";\n\nexport interface LocalGitInfo {\n repository: string | null;\n branch: string | null;\n provider: Provider | null;\n remoteUrl: string | null;\n}\n\nconst EMPTY_LOCAL_GIT_INFO: LocalGitInfo = {\n repository: null,\n branch: null,\n provider: null,\n remoteUrl: null,\n};\n\ntype RunCommand = (\n command: string,\n cwd: string,\n timeout: number,\n) => Promise<CommandResult>;\n\n// Single shell script that replaces the former probeGitInfoAtDir +\n// probeNestedRepoInDir pair. It runs as one bash WebSocket round-trip:\n// 1. Read the origin remote URL and current branch at the workspace root.\n// 2. If neither is set, search for exactly one nested git repo up to 4\n// levels deep and repeat the probe there.\n// Output: two lines — <remote-url>\\n<branch> — either may be empty.\nconst GIT_INFO_COMMAND = [\n \"r=$(git remote get-url origin 2>/dev/null)\",\n \"b=$(git rev-parse --abbrev-ref HEAD 2>/dev/null)\",\n 'if [ -z \"$r$b\" ]; then',\n \"n=$(find . -mindepth 2 -maxdepth 4 -name .git 2>/dev/null | cut -c3- | sed 's|/.git$||' | sort -u)\",\n \"c=$(printf '%s\\\\n' \\\"$n\\\" | grep -c '[^[:space:]]')\",\n 'if [ \"$c\" = \"1\" ] && [ -n \"$n\" ]; then',\n 'r=$(git -C \"$n\" remote get-url origin 2>/dev/null)',\n 'b=$(git -C \"$n\" rev-parse --abbrev-ref HEAD 2>/dev/null)',\n \"fi\",\n \"fi\",\n 'printf \\'%s\\\\n%s\\' \"$r\" \"$b\"',\n].join(\"\\n\");\n\nasync function probeGitInfo(\n run: RunCommand,\n directory: string,\n): Promise<LocalGitInfo> {\n const result = await run(GIT_INFO_COMMAND, directory, 10);\n if (result.exit_code !== 0) return EMPTY_LOCAL_GIT_INFO;\n\n const nl = result.stdout.indexOf(\"\\n\");\n const remoteUrl = (\n nl >= 0 ? result.stdout.slice(0, nl) : result.stdout\n ).trim();\n const rawBranch = (nl >= 0 ? result.stdout.slice(nl + 1) : \"\").trim();\n const branch = rawBranch && rawBranch !== \"HEAD\" ? rawBranch : null;\n\n if (!remoteUrl && !branch) return EMPTY_LOCAL_GIT_INFO;\n\n const parsedRemote = parseGitRemoteUrl(remoteUrl);\n return {\n repository: parsedRemote?.repository ?? null,\n provider: parsedRemote?.provider ?? null,\n remoteUrl: remoteUrl || null,\n branch,\n };\n}\n\n/**\n * Probe git metadata for a **local** backend's workspace checkout by\n * shelling out via the agent server using a single consolidated bash\n * script (see `GIT_INFO_COMMAND`).\n *\n * Local-only by design. On cloud backends the conversation metadata\n * (`selected_repository`, `git_provider`, `selected_branch`) is the\n * source of truth, and probing via `/api/bash/execute_bash_command`\n * would (a) leak the user's local `getAgentServerWorkingDir()` path to\n * the cloud runtime when `workspace.working_dir` is missing, and\n * (b) hit a bash endpoint we don't want the frontend driving on cloud.\n *\n * On local, we keep the probe enabled until the active conversation\n * has a complete repo tuple so the control bar can recover from\n * partial metadata hydration after connect/clone flows.\n *\n * Returns `null` fields when the working dir is not a git checkout —\n * callers should treat that the same as \"no repo detected\".\n */\nexport const useLocalGitInfo = () => {\n const { data: conversation } = useActiveConversation();\n const runtimeIsReady = useRuntimeIsReady();\n const { backend } = useActiveBackend();\n const isLocalBackend = backend.kind === \"local\";\n\n const conversationId = conversation?.id;\n const conversationUrl = conversation?.conversation_url;\n const sessionApiKey = conversation?.session_api_key;\n const workingDir = conversation?.workspace?.working_dir?.trim();\n const hasConversationRepo = !!conversation?.selected_repository;\n const hasConversationProvider = !!conversation?.git_provider;\n const hasConversationBranch = !!conversation?.selected_branch;\n\n const queryEnabled =\n isLocalBackend &&\n runtimeIsReady &&\n !!conversationId &&\n !!workingDir &&\n (!hasConversationRepo ||\n !hasConversationProvider ||\n !hasConversationBranch);\n\n // Persistent WebSocket connection to the bash-events endpoint. The\n // connection is opened when the query is enabled and closed on unmount or\n // when the conversation changes.\n const runCommand = useBashCommandRunner(\n conversationUrl,\n sessionApiKey,\n queryEnabled,\n );\n\n // Keep a ref so queryFn can call the latest runner without capturing it\n // as a queryKey dependency (runCommand is stable but the linter can't\n // infer that).\n const runCommandRef = useRef(runCommand);\n runCommandRef.current = runCommand;\n\n // runCommandRef is a ref (always stable); the linter cannot infer this so\n // we disable the exhaustive-deps check here.\n\n return useQuery<LocalGitInfo>({\n queryKey: [\n \"local-git-info\",\n conversationId,\n conversationUrl,\n sessionApiKey,\n workingDir,\n ],\n queryFn: async () => {\n const run: RunCommand = (command, cwd, timeout) =>\n runCommandRef.current(command, cwd, timeout);\n // workingDir is guaranteed non-empty by the queryEnabled guard above.\n return probeGitInfo(run, workingDir!);\n },\n enabled: queryEnabled,\n retry: false,\n // Re-probe the workspace every 10s so the UI reflects branch/repo\n // changes (e.g. `git checkout`, adding a remote) without requiring a\n // manual refresh when there is no `selected_repository` recorded on\n // the conversation. Commands now run over the persistent WebSocket\n // connection rather than individual REST calls.\n staleTime: 10_000,\n refetchInterval: 10_000,\n gcTime: 1000 * 60 * 5,\n meta: { disableToast: true },\n });\n};\n"],"mappings":";;;;;;;;AAkBA,IAAM,IAAqC;CACzC,YAAY;CACZ,QAAQ;CACR,UAAU;CACV,WAAW;AACb,GAcM,IAAmB;CACvB;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,EAAE,KAAK,IAAI;AAEX,eAAe,EACb,GACA,GACuB;CACvB,IAAM,IAAS,MAAM,EAAI,GAAkB,GAAW,EAAE;CACxD,IAAI,EAAO,cAAc,GAAG,OAAO;CAEnC,IAAM,IAAK,EAAO,OAAO,QAAQ,IAAI,GAC/B,KACJ,KAAM,IAAI,EAAO,OAAO,MAAM,GAAG,CAAE,IAAI,EAAO,QAC9C,KAAK,GACD,KAAa,KAAM,IAAI,EAAO,OAAO,MAAM,IAAK,CAAC,IAAI,IAAI,KAAK,GAC9D,IAAS,KAAa,MAAc,SAAS,IAAY;CAE/D,IAAI,CAAC,KAAa,CAAC,GAAQ,OAAO;CAElC,IAAM,IAAe,EAAkB,CAAS;CAChD,OAAO;EACL,YAAY,GAAc,cAAc;EACxC,UAAU,GAAc,YAAY;EACpC,WAAW,KAAa;EACxB;CACF;AACF;AAqBA,IAAa,UAAwB;CACnC,IAAM,EAAE,MAAM,MAAiB,EAAsB,GAC/C,IAAiB,EAAkB,GACnC,EAAE,eAAY,EAAiB,GAC/B,IAAiB,EAAQ,SAAS,SAElC,IAAiB,GAAc,IAC/B,IAAkB,GAAc,kBAChC,IAAgB,GAAc,iBAC9B,IAAa,GAAc,WAAW,aAAa,KAAK,GACxD,IAAsB,CAAC,CAAC,GAAc,qBACtC,IAA0B,CAAC,CAAC,GAAc,cAC1C,IAAwB,CAAC,CAAC,GAAc,iBAExC,IACJ,KACA,KACA,CAAC,CAAC,KACF,CAAC,CAAC,MACD,CAAC,KACA,CAAC,KACD,CAAC,IAKC,IAAa,EACjB,GACA,GACA,CACF,GAKM,IAAgB,EAAO,CAAU;CAMvC,OALA,EAAc,UAAU,GAKjB,EAAuB;EAC5B,UAAU;GACR;GACA;GACA;GACA;GACA;EACF;EACA,SAAS,YAIA,GAHkB,GAAS,GAAK,MACrC,EAAc,QAAQ,GAAS,GAAK,CAAO,GAEpB,CAAW;EAEtC,SAAS;EACT,OAAO;EAMP,WAAW;EACX,iBAAiB;EACjB,QAAQ,MAAO,KAAK;EACpB,MAAM,EAAE,cAAc,GAAK;CAC7B,CAAC;AACH"}
@@ -1,2 +1,2 @@
1
- require("../../_virtual/_rolldown/runtime.cjs");const e=require("../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs"),t=require("../../api/config-service/config-service.api.cjs"),n=require("./use-verified-models.cjs");var r=10;async function i(e,n,a,o=0){if(o>=r)throw Error(`Too many pagination requests for provider ${e}`);let s=await t.default.searchModels({provider__eq:e,limit:100,page_id:a},n);if(s.next_page_id){let t=await i(e,n,s.next_page_id,o+1);return[...s.items,...t]}return s.items}var a=t=>e.useQuery({queryKey:[`config`,`models`,t],queryFn:async({client:e})=>i(t,await e.fetchQuery({queryKey:n.VERIFIED_MODELS_QUERY_KEY,queryFn:n.fetchVerifiedModelsByProvider,staleTime:n.VERIFIED_MODELS_STALE_TIME})),enabled:!!t,staleTime:n.VERIFIED_MODELS_STALE_TIME,gcTime:n.VERIFIED_MODELS_GC_TIME});exports.useProviderModels=a;
1
+ require("../../_virtual/_rolldown/runtime.cjs");const e=require("../../node_modules/@tanstack/react-query/build/modern/useQuery.cjs"),t=require("../../contexts/active-backend-context.cjs"),n=require("../../api/config-service/config-service.api.cjs"),r=require("./use-verified-models.cjs");var i=10;async function a(e,t,r,o=0){if(o>=i)throw Error(`Too many pagination requests for provider ${e}`);let s=await n.default.searchModels({provider__eq:e,limit:100,page_id:r},t);if(s.next_page_id){let n=await a(e,t,s.next_page_id,o+1);return[...s.items,...n]}return s.items}var o=n=>{let{backend:i,orgId:o}=t.useActiveBackend(),s=[i.id,i.connectionRevision??0,o];return e.useQuery({queryKey:[`config`,`models`,n,...s],queryFn:async({client:e})=>a(n,await e.fetchQuery({queryKey:[...r.VERIFIED_MODELS_QUERY_KEY,...s],queryFn:r.fetchVerifiedModelsByProvider,staleTime:r.VERIFIED_MODELS_STALE_TIME})),enabled:!!n,staleTime:r.VERIFIED_MODELS_STALE_TIME,gcTime:r.VERIFIED_MODELS_GC_TIME})};exports.useProviderModels=o;
2
2
  //# sourceMappingURL=use-provider-models.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-provider-models.cjs","names":[],"sources":["../../../src/hooks/query/use-provider-models.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport ConfigService from \"#/api/config-service/config-service.api\";\nimport type { LLMModel } from \"#/api/config-service/config-service.types\";\nimport {\n VERIFIED_MODELS_GC_TIME,\n VERIFIED_MODELS_QUERY_KEY,\n VERIFIED_MODELS_STALE_TIME,\n fetchVerifiedModelsByProvider,\n} from \"./use-verified-models\";\n\nconst MAX_PAGINATION_DEPTH = 10;\n\nasync function fetchPage(\n provider: string,\n verifiedByProvider: Record<string, string[]>,\n pageId?: string,\n depth = 0,\n): Promise<LLMModel[]> {\n if (depth >= MAX_PAGINATION_DEPTH) {\n throw new Error(`Too many pagination requests for provider ${provider}`);\n }\n\n const page = await ConfigService.searchModels(\n {\n provider__eq: provider,\n limit: 100,\n page_id: pageId,\n },\n verifiedByProvider,\n );\n\n if (page.next_page_id) {\n const rest = await fetchPage(\n provider,\n verifiedByProvider,\n page.next_page_id,\n depth + 1,\n );\n return [...page.items, ...rest];\n }\n return page.items;\n}\n\nexport const useProviderModels = (provider: string | null) =>\n useQuery({\n queryKey: [\"config\", \"models\", provider],\n queryFn: async ({ client }) => {\n const verifiedByProvider = await client.fetchQuery({\n queryKey: VERIFIED_MODELS_QUERY_KEY,\n queryFn: fetchVerifiedModelsByProvider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n });\n return fetchPage(provider!, verifiedByProvider);\n },\n enabled: !!provider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n gcTime: VERIFIED_MODELS_GC_TIME,\n });\n"],"mappings":"0OAUA,IAAM,EAAuB,GAE7B,eAAe,EACb,EACA,EACA,EACA,EAAQ,EACa,CACrB,GAAI,GAAS,EACX,MAAU,MAAM,6CAA6C,GAAU,EAGzE,IAAM,EAAO,MAAM,EAAA,QAAc,aAC/B,CACE,aAAc,EACd,MAAO,IACP,QAAS,CACX,EACA,CACF,EAEA,GAAI,EAAK,aAAc,CACrB,IAAM,EAAO,MAAM,EACjB,EACA,EACA,EAAK,aACL,EAAQ,CACV,EACA,MAAO,CAAC,GAAG,EAAK,MAAO,GAAG,CAAI,CAChC,CACA,OAAO,EAAK,KACd,CAEA,IAAa,EAAqB,GAChC,EAAA,SAAS,CACP,SAAU,CAAC,SAAU,SAAU,CAAQ,EACvC,QAAS,MAAO,CAAE,YAMT,EAAU,EAAW,MALK,EAAO,WAAW,CACjD,SAAU,EAAA,0BACV,QAAS,EAAA,8BACT,UAAW,EAAA,0BACb,CAAC,CAC6C,EAEhD,QAAS,CAAC,CAAC,EACX,UAAW,EAAA,2BACX,OAAQ,EAAA,uBACV,CAAC"}
1
+ {"version":3,"file":"use-provider-models.cjs","names":[],"sources":["../../../src/hooks/query/use-provider-models.ts"],"sourcesContent":["import { useQuery } from \"@tanstack/react-query\";\nimport ConfigService from \"#/api/config-service/config-service.api\";\nimport type { LLMModel } from \"#/api/config-service/config-service.types\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport {\n VERIFIED_MODELS_GC_TIME,\n VERIFIED_MODELS_QUERY_KEY,\n VERIFIED_MODELS_STALE_TIME,\n fetchVerifiedModelsByProvider,\n} from \"./use-verified-models\";\n\nconst MAX_PAGINATION_DEPTH = 10;\n\nasync function fetchPage(\n provider: string,\n verifiedByProvider: Record<string, string[]>,\n pageId?: string,\n depth = 0,\n): Promise<LLMModel[]> {\n if (depth >= MAX_PAGINATION_DEPTH) {\n throw new Error(`Too many pagination requests for provider ${provider}`);\n }\n\n const page = await ConfigService.searchModels(\n {\n provider__eq: provider,\n limit: 100,\n page_id: pageId,\n },\n verifiedByProvider,\n );\n\n if (page.next_page_id) {\n const rest = await fetchPage(\n provider,\n verifiedByProvider,\n page.next_page_id,\n depth + 1,\n );\n return [...page.items, ...rest];\n }\n return page.items;\n}\n\nexport const useProviderModels = (provider: string | null) => {\n // `ActiveBackendProvider` deliberately does not blanket-invalidate on\n // backend/org switches, so the query key must carry the active backend\n // identity (id, connection revision, org id). Otherwise React Query serves\n // the previous backend/org's cached page — including DB-driven `free`/\n // `default`/`verified` flags — for the full stale window after a switch.\n const { backend, orgId } = useActiveBackend();\n const backendScope = [\n backend.id,\n backend.connectionRevision ?? 0,\n orgId,\n ] as const;\n\n return useQuery({\n queryKey: [\"config\", \"models\", provider, ...backendScope],\n queryFn: async ({ client }) => {\n const verifiedByProvider = await client.fetchQuery({\n queryKey: [...VERIFIED_MODELS_QUERY_KEY, ...backendScope],\n queryFn: fetchVerifiedModelsByProvider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n });\n return fetchPage(provider!, verifiedByProvider);\n },\n enabled: !!provider,\n staleTime: VERIFIED_MODELS_STALE_TIME,\n gcTime: VERIFIED_MODELS_GC_TIME,\n });\n};\n"],"mappings":"iSAWA,IAAM,EAAuB,GAE7B,eAAe,EACb,EACA,EACA,EACA,EAAQ,EACa,CACrB,GAAI,GAAS,EACX,MAAU,MAAM,6CAA6C,GAAU,EAGzE,IAAM,EAAO,MAAM,EAAA,QAAc,aAC/B,CACE,aAAc,EACd,MAAO,IACP,QAAS,CACX,EACA,CACF,EAEA,GAAI,EAAK,aAAc,CACrB,IAAM,EAAO,MAAM,EACjB,EACA,EACA,EAAK,aACL,EAAQ,CACV,EACA,MAAO,CAAC,GAAG,EAAK,MAAO,GAAG,CAAI,CAChC,CACA,OAAO,EAAK,KACd,CAEA,IAAa,EAAqB,GAA4B,CAM5D,GAAM,CAAE,UAAS,SAAU,EAAA,iBAAiB,EACtC,EAAe,CACnB,EAAQ,GACR,EAAQ,oBAAsB,EAC9B,CACF,EAEA,OAAO,EAAA,SAAS,CACd,SAAU,CAAC,SAAU,SAAU,EAAU,GAAG,CAAY,EACxD,QAAS,MAAO,CAAE,YAMT,EAAU,EAAW,MALK,EAAO,WAAW,CACjD,SAAU,CAAC,GAAG,EAAA,0BAA2B,GAAG,CAAY,EACxD,QAAS,EAAA,8BACT,UAAW,EAAA,0BACb,CAAC,CAC6C,EAEhD,QAAS,CAAC,CAAC,EACX,UAAW,EAAA,2BACX,OAAQ,EAAA,uBACV,CAAC,CACH"}