@openhands/agent-canvas 1.16.0 → 1.18.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 (1093) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{acp-providers-CX3Lh9gj.js → acp-providers-DcY_mKYa.js} +1 -1
  4. package/build/assets/active-backend-context-BnH4-L8h.js +2 -0
  5. package/build/assets/{agent-context-settings-DSZlXrLj.js → agent-context-settings-D3peZkjX.js} +1 -1
  6. package/build/assets/agent-profiles-service.api-DkeZaqIl.js +1 -0
  7. package/build/assets/agent-profiles-settings-7yMWYjGF.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-jiCopc2E.js +88 -0
  11. package/build/assets/agent-settings-BF8hM6wb.js +2 -0
  12. package/build/assets/agent-settings-YPutBAeR.js +1 -0
  13. package/build/assets/alert-banner-eYyqvHL3.js +1 -0
  14. package/build/assets/{api-error-message-B-XdSk3b.js → api-error-message-BXo0lCI5.js} +1 -1
  15. package/build/assets/api-key-entry-screen-CCBvBD8u.js +1 -0
  16. package/build/assets/app-settings-BtYDs-Be.js +1 -0
  17. package/build/assets/automation-detail-BxXhb_m2.js +3 -0
  18. package/build/assets/{automation-export-D3a3of4G.js → automation-export-BJsfi7n2.js} +1 -1
  19. package/build/assets/automation-git-sync-B7o_Nbl-.js +1 -0
  20. package/build/assets/automation-setup-route-qaJ6lLCf.js +3 -0
  21. package/build/assets/{automation-templates-BRppNh99.js → automation-templates-Bb98r-kx.js} +1 -1
  22. package/build/assets/automations-list-DEPVNud3.js +1 -0
  23. package/build/assets/{back-nav-button-Cjl_hoxq.js → back-nav-button-B2yBiVMB.js} +1 -1
  24. package/build/assets/backend-form-modal-DxisQV_6.js +1 -0
  25. package/build/assets/backend-synced-settings-badge-DLEp6cq7.js +1 -0
  26. package/build/assets/badge-ehL-kgeO.js +1 -0
  27. package/build/assets/{base-modal-D_L0oEbG.js → base-modal-C1a0TsSx.js} +1 -1
  28. package/build/assets/bot-ByolqPEr.js +1 -0
  29. package/build/assets/{brand-button-BAfk4wGP.js → brand-button-CUMfjb99.js} +1 -1
  30. package/build/assets/browser-SE24Tk8g.js +46 -0
  31. package/build/assets/browser-tab-HjJxHA0V.js +1 -0
  32. package/build/assets/canvas-extension-page-BsUGBb1T.js +1 -0
  33. package/build/assets/canvas-extensions-C9Tm6s_E.js +1 -0
  34. package/build/assets/{canvas-extensions-runtime-2_x5Ezmi.js → canvas-extensions-runtime-B8kxM4rO.js} +1 -1
  35. package/build/assets/{chat-send-button-MUQqCZCS.js → chat-send-button-CGvc2wpE.js} +1 -1
  36. package/build/assets/check-Bo8s5Ylg.js +1 -0
  37. package/build/assets/chevron-down-Cr6Fn_wS.js +1 -0
  38. package/build/assets/chevron-right-CfdfRn-8.js +1 -0
  39. package/build/assets/circle-check-5pH3qCji.js +1 -0
  40. package/build/assets/circle-plus-check-toggle-BDX_ctNs.js +1 -0
  41. package/build/assets/{cloud-funnel-analytics-CztBCCPp.js → cloud-funnel-analytics-D5uJ_4JE.js} +1 -1
  42. package/build/assets/{cog-D-MPQW6G.js → cog-CAEugqtV.js} +1 -1
  43. package/build/assets/command-menu-CQxs4mtX.js +1 -0
  44. package/build/assets/commits-tab-D0xgjipP.js +1 -0
  45. package/build/assets/{condenser-settings-DmEOje-7.js → condenser-settings-D63NJRBT.js} +1 -1
  46. package/build/assets/confirmation-modal-78sd9Vny.js +1 -0
  47. package/build/assets/{context-menu-list-item-I86vKcmM.js → context-menu-list-item-Cm7-7Y-7.js} +1 -1
  48. package/build/assets/conversation-B_nweu5p.js +93 -0
  49. package/build/assets/conversation-edjzrv5w.js +1 -0
  50. package/build/assets/conversation-panel-edjzrv5w.js +1 -0
  51. package/build/assets/conversation-state-store-9D_3P7q4.js +1 -0
  52. package/build/assets/conversation-store-DC-JlJuq.js +6 -0
  53. package/build/assets/{conversation-tab-empty-state-FIbh_lc8.js → conversation-tab-empty-state-B3dpJXX0.js} +1 -1
  54. package/build/assets/copy-to-clipboard-button-B1ljgNxO.js +1 -0
  55. package/build/assets/device-verify-D5QRIS_x.js +1 -0
  56. package/build/assets/{dropdown-classes-Bc1djN0-.js → dropdown-classes-BGvOvW2_.js} +1 -1
  57. package/build/assets/edit-automation-modal-D_JWSmxT.js +1 -0
  58. package/build/assets/empty-state-FK_s1B60.js +1 -0
  59. package/build/assets/{entry.client-CqyPPjOq.js → entry.client-BMafTpjO.js} +2 -2
  60. package/build/assets/enum-filter-dropdown-UNaom12H.js +1 -0
  61. package/build/assets/environment-switch-overlay-COibRUA-.js +1 -0
  62. package/build/assets/error-state-Cf8LRuuY.js +1 -0
  63. package/build/assets/extensions-hub-C_6ahIua.js +1 -0
  64. package/build/assets/extensions-navigation-C5GfpzFe.js +1 -0
  65. package/build/assets/external-link-Bd7Tga-1.js +1 -0
  66. package/build/assets/files-tab-D5PPIWI1.js +1 -0
  67. package/build/assets/{form-control-classes-Bkh5FGrD.js → form-control-classes-Dbq-7YP_.js} +1 -1
  68. package/build/assets/format-model-name-Dfh9Udrx.js +1 -0
  69. package/build/assets/get-git-path-jU2zjqIB.js +1 -0
  70. package/build/assets/git-repo-dropdown-BoDDWXfX.js +1 -0
  71. package/build/assets/git-repo-dropdown-uxlFw2gn.js +1 -0
  72. package/build/assets/highlighted-source-view-C7pAXrT0.js +1 -0
  73. package/build/assets/home-BM5_Wz_R.js +1 -0
  74. package/build/assets/home-Ivfm7KC4.js +1 -0
  75. package/build/assets/home-automation-run-tooltip-B1N2uO9p.js +1 -0
  76. package/build/assets/home-store-DCbei3DX.js +1 -0
  77. package/build/assets/hooks-service-Dnmhqc0Z.js +1 -0
  78. package/build/assets/index-home-td9BN2T3.js +1 -0
  79. package/build/assets/info-BcajzHn1.js +1 -0
  80. package/build/assets/install-server-modal-B81I-5OE.js +1 -0
  81. package/build/assets/kebab-menu-item-content-SCJBwZnp.js +1 -0
  82. package/build/assets/{key-BmVyxs-2.js → key-O84CMFHb.js} +1 -1
  83. package/build/assets/launch-ZLxI4opd.js +1 -0
  84. package/build/assets/list-filter-B8PQk3RA.js +1 -0
  85. package/build/assets/{list-todo-apgLVjMw.js → list-todo-Caa8E1X_.js} +1 -1
  86. package/build/assets/llm-not-configured-banner-Dh_ajcM8.js +27 -0
  87. package/build/assets/llm-settings-CODhjQzy.js +1 -0
  88. package/build/assets/llm-settings-CZaXYgtw.js +1 -0
  89. package/build/assets/llm-subscription-service-CgIDc38n.js +1 -0
  90. package/build/assets/loader-circle-BxJgk73D.js +1 -0
  91. package/build/assets/{loading-spinner-BY9N3pOe.js → loading-spinner-BeLV0IXf.js} +1 -1
  92. package/build/assets/manage-backends-modal-cMXqCb-B.js +1 -0
  93. package/build/assets/manifest-bfeccb98.js +1 -0
  94. package/build/assets/manifest-subpage-layout-CVZgVKoY.js +1 -0
  95. package/build/assets/{map-provider-Cu3Zgumb.js → map-provider-iRs80q_6.js} +1 -1
  96. package/build/assets/markdown-renderer-DyFl9-mL.js +8 -0
  97. package/build/assets/mcp-CraxoXQv.js +1 -0
  98. package/build/assets/{mcp-marketplace-utils-AIBzzHTO.js → mcp-marketplace-utils-C7PYr293.js} +1 -1
  99. package/build/assets/mcp-page-CteH_K0N.js +9 -0
  100. package/build/assets/messages-Bk1xhbU2.js +41 -0
  101. package/build/assets/{modal-body-BxZjsju9.js → modal-body-C24WsdFb.js} +1 -1
  102. package/build/assets/{modal-classes-C8eycCwG.js → modal-classes-B0b9eiL8.js} +1 -1
  103. package/build/assets/modal-close-button-DfqfB2Nj.js +1 -0
  104. package/build/assets/{mouse-pointer-click-Co2uyaPA.js → mouse-pointer-click-BXlRN92t.js} +1 -1
  105. package/build/assets/onboarding-DXFBRqEy.js +1 -0
  106. package/build/assets/onboarding-modal-BT3Gx_sZ.js +1 -0
  107. package/build/assets/organization-service.api-B5CvmnYQ.js +1 -0
  108. package/build/assets/pencil-q-ihqDYv.js +1 -0
  109. package/build/assets/pin-BdroVQdN.js +1 -0
  110. package/build/assets/planner-tab-CstoOlAB.js +1 -0
  111. package/build/assets/plug-7ACOEbMs.js +1 -0
  112. package/build/assets/plugins-management-service-5rqVPEGN.js +1 -0
  113. package/build/assets/plus-C2e21TFC.js +1 -0
  114. package/build/assets/profiles-service.api-D1XKMgr0.js +1 -0
  115. package/build/assets/providers-XmZ9HM4Q.js +1 -0
  116. package/build/assets/proxy-XP6U1Dhv.js +1 -0
  117. package/build/assets/{puzzle-JM_QaRm0.js → puzzle-LJYnuxKe.js} +1 -1
  118. package/build/assets/react-BX7IV650.js +1 -0
  119. package/build/assets/recommended-automations-launcher-DEHRKIAg.js +1 -0
  120. package/build/assets/refresh-cw-BtdC3T8P.js +1 -0
  121. package/build/assets/root-CM6IZHlg.css +1 -0
  122. package/build/assets/root-DdFkodKW.js +2 -0
  123. package/build/assets/root-layout-DUquNZTU.js +2 -0
  124. package/build/assets/schema-field-Y3F8JvXd.js +1 -0
  125. package/build/assets/scroll-fade-state-Dt_fx2kf.js +1 -0
  126. package/build/assets/sdk-section-page-CNZTLwH9.js +1 -0
  127. package/build/assets/secret-form-9-BoGA40.js +1 -0
  128. package/build/assets/secrets-service-BOIsfD9j.js +1 -0
  129. package/build/assets/secrets-settings-CBJgFr42.js +1 -0
  130. package/build/assets/settings-D4hdToD4.js +1 -0
  131. package/build/assets/settings-dropdown-input-DBvsZ7-Q.js +1 -0
  132. package/build/assets/settings-index-DeQphyBK.js +1 -0
  133. package/build/assets/settings-input-dVuzapBA.js +1 -0
  134. package/build/assets/{settings-list-classes-BzeSZbF2.js → settings-list-classes-DhAPuXm6.js} +1 -1
  135. package/build/assets/settings-modal-tHiJ8iYV.js +1 -0
  136. package/build/assets/settings-nav-GAQjW1a-.js +16 -0
  137. package/build/assets/settings-service.api-C1vZH9Vh.js +1 -0
  138. package/build/assets/settings-switch-DDd1cQ8t.js +1 -0
  139. package/build/assets/{settings-utils-UYpEdPuD.js → settings-utils-BTaaRhLZ.js} +1 -1
  140. package/build/assets/shared-conversation-Cyg7m7ns.js +1 -0
  141. package/build/assets/shield-check-C3Rl0NF8.js +1 -0
  142. package/build/assets/{sidebar-layout-D6fS8-Gl.js → sidebar-layout-DH0E-5KW.js} +1 -1
  143. package/build/assets/sidebar-mobile-menu-toggle-CQ4iky__.js +1 -0
  144. package/build/assets/{sidebar-nav-link-DQQcMf1A.js → sidebar-nav-link-oLQiKUBu.js} +1 -1
  145. package/build/assets/{sidebar-onboarding-checklist-storage-BBkeI6Bp.js → sidebar-onboarding-checklist-storage-TfvNNnnr.js} +1 -1
  146. package/build/assets/sidebar-store-DAZVH-DR.js +1 -0
  147. package/build/assets/skill-card-pill-row-DfjIfj0K.js +1 -0
  148. package/build/assets/{skill-filter-w__3UCEE.js → skill-filter-DsNLmn76.js} +2 -2
  149. package/build/assets/{skill-icon-badge-RQkq8wvl.js → skill-icon-badge-0MK_68Nc.js} +1 -1
  150. package/build/assets/skill-scope-BE-H9TSZ.js +1 -0
  151. package/build/assets/skills-plugins-vzSdxSk_.js +1 -0
  152. package/build/assets/skills-settings-BiPOH9H9.js +1 -0
  153. package/build/assets/styled-tooltip-CejG0rqv.js +1 -0
  154. package/build/assets/task-list-tab-OkZSqnTp.js +1 -0
  155. package/build/assets/{terminal-CNElN42a.js → terminal-Dph60VFZ.js} +2 -2
  156. package/build/assets/toggle-switch-B2uXaAUS.js +1 -0
  157. package/build/assets/trash-2-Bb40zQzs.js +1 -0
  158. package/build/assets/trash-Ckh7yCaB.js +1 -0
  159. package/build/assets/triangle-alert-DEdu-b_L.js +1 -0
  160. package/build/assets/{typography-v1KN0tzi.js → typography-DgcLGdHk.js} +1 -1
  161. package/build/assets/usage-tab-MCmNRAnR.js +1 -0
  162. package/build/assets/use-acp-credential-form-ychoGqjC.js +1 -0
  163. package/build/assets/use-activate-agent-profile-8L8Xdghy.js +1 -0
  164. package/build/assets/use-agent-profiles-Bi8hSkze.js +1 -0
  165. package/build/assets/{use-automation-health-E6NZi9rb.js → use-automation-health-U99vvHxx.js} +1 -1
  166. package/build/assets/use-automation-permissions-Buus0R6h.js +1 -0
  167. package/build/assets/{use-automations-xijQKIl9.js → use-automations-Bq5hVuv0.js} +1 -1
  168. package/build/assets/{use-can-manage-org-profiles-CrJP5262.js → use-can-manage-org-profiles-MzIeEV8F.js} +1 -1
  169. package/build/assets/use-canvas-extensions-Ca9KoVEm.js +1 -0
  170. package/build/assets/{use-cloud-current-user-id-CDfAHpPX.js → use-cloud-current-user-id-B4ZLUZSa.js} +1 -1
  171. package/build/assets/use-config-l5tkDHyD.js +1 -0
  172. package/build/assets/use-create-conversation-i_PdFdIs.js +1 -0
  173. package/build/assets/use-create-secret-CjWq0J4O.js +1 -0
  174. package/build/assets/use-free-models-gCsJ5BV0.js +1 -0
  175. package/build/assets/{use-get-secrets-K3jmk-LH.js → use-get-secrets-CMTuWEp_.js} +1 -1
  176. package/build/assets/use-is-authed-A2Yinlt0.js +1 -0
  177. package/build/assets/{use-launch-skill-in-chat-BYjYihBP.js → use-launch-skill-in-chat-QbXDhiHr.js} +1 -1
  178. package/build/assets/use-llm-configured-Dl4zg8ht.js +1 -0
  179. package/build/assets/use-llm-profiles-SWsuFmFu.js +1 -0
  180. package/build/assets/use-manifest-capabilities-CdqYfUAC.js +1 -0
  181. package/build/assets/use-onboarding-completion-Xl63ZvzQ.js +1 -0
  182. package/build/assets/use-pinned-home-route-BXDIUaxQ.js +1 -0
  183. package/build/assets/use-plugins-marketplace-BIIaPiaB.js +1 -0
  184. package/build/assets/use-save-agent-profile-D-ZHHE94.js +1 -0
  185. package/build/assets/{use-save-settings-DYqkKv7T.js → use-save-settings-Dapkf4cC.js} +1 -1
  186. package/build/assets/use-settings-C2KKQKVC.js +1 -0
  187. package/build/assets/use-settings-nav-items-TOc0Rsfd.js +1 -0
  188. package/build/assets/use-tracking-j4uo9VEW.js +3 -0
  189. package/build/assets/{use-user-conversation-B9Gebw_g.js → use-user-conversation-5UFqMoO4.js} +1 -1
  190. package/build/assets/{useInfiniteQuery-C926xzY4.js → useInfiniteQuery-CtSRG7uW.js} +1 -1
  191. package/build/assets/{useMutation-B-qc391N.js → useMutation-Cszf8mKy.js} +1 -1
  192. package/build/assets/useQueries-pUohFMSP.js +1 -0
  193. package/build/assets/useQuery-DAAacS7O.js +1 -0
  194. package/build/assets/useTranslation-CCgUjPJF.js +1 -0
  195. package/build/assets/utils-BgLSZdyy.js +1 -0
  196. package/build/assets/{vendor~conversation-panel~conversation-C8Fn4XOf.js → vendor~conversation-panel~conversation-C22sJuWZ.js} +1 -1
  197. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~b0x1wrp2-Z0lly3FE.js +2 -0
  198. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-BXXGJNDU.js +185 -0
  199. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-40UR63Gt.js +1 -0
  200. package/build/assets/{vendor~index-home~home~conversation-panel~conversation-hWywRBaR.js → vendor~index-home~home~conversation-panel~conversation-UTl7gUo9.js} +1 -1
  201. package/build/assets/vendor~root-layout-CcdIqs-C.js +1 -0
  202. package/build/assets/{vendor~root-layout~conversation-panel~conversation-BMbMgaGu.js → vendor~root-layout~conversation-panel~conversation-C3EBV3kp.js} +1 -1
  203. package/build/assets/vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-BCe0JJVy.js +1 -0
  204. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation-j5MFqfSy.js → vendor~root-layout~index-home~home~conversation-panel~conversation-BGGbPcYh.js} +1 -1
  205. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-CxhwKuAd.js +1 -0
  206. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~nj1hli3r-DP9wmsjo.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~inibp4qe-CnsDDzeE.js} +1 -1
  207. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-CUmIwQCN.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-3eJ4ExtV.js} +12 -6
  208. package/build/assets/{verification-settings-gw_q6vkL.js → verification-settings-DdBpaVPz.js} +1 -1
  209. package/build/assets/{wrench-CUEI1krB.js → wrench-B8Wg_nbp.js} +1 -1
  210. package/build/assets/x-CX8lVGwm.js +1 -0
  211. package/build/index.html +4 -4
  212. package/build/locales/ar/openhands.json +79 -22
  213. package/build/locales/ca/openhands.json +79 -22
  214. package/build/locales/de/openhands.json +79 -22
  215. package/build/locales/en/openhands.json +79 -22
  216. package/build/locales/es/openhands.json +79 -22
  217. package/build/locales/fr/openhands.json +79 -22
  218. package/build/locales/it/openhands.json +79 -22
  219. package/build/locales/ja/openhands.json +79 -22
  220. package/build/locales/ko-KR/openhands.json +79 -22
  221. package/build/locales/no/openhands.json +79 -22
  222. package/build/locales/pt/openhands.json +79 -22
  223. package/build/locales/tr/openhands.json +79 -22
  224. package/build/locales/uk/openhands.json +79 -22
  225. package/build/locales/zh-CN/openhands.json +79 -22
  226. package/build/locales/zh-TW/openhands.json +79 -22
  227. package/config/defaults.json +3 -3
  228. package/dist/api/agent-server-adapter.cjs +3 -3
  229. package/dist/api/agent-server-adapter.cjs.map +1 -1
  230. package/dist/api/agent-server-adapter.d.ts +72 -12
  231. package/dist/api/agent-server-adapter.js +234 -132
  232. package/dist/api/agent-server-adapter.js.map +1 -1
  233. package/dist/api/agent-server-config.cjs +1 -1
  234. package/dist/api/agent-server-config.cjs.map +1 -1
  235. package/dist/api/agent-server-config.d.ts +6 -0
  236. package/dist/api/agent-server-config.js +6 -3
  237. package/dist/api/agent-server-config.js.map +1 -1
  238. package/dist/api/automation-service/automation-service.api.cjs +1 -1
  239. package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
  240. package/dist/api/automation-service/automation-service.api.d.ts +2 -2
  241. package/dist/api/automation-service/automation-service.api.js +7 -7
  242. package/dist/api/automation-service/automation-service.api.js.map +1 -1
  243. package/dist/api/cloud/organization-service.api.cjs.map +1 -1
  244. package/dist/api/cloud/organization-service.api.d.ts +9 -1
  245. package/dist/api/cloud/organization-service.api.js.map +1 -1
  246. package/dist/api/cloud/provider-connections-service.api.cjs +2 -0
  247. package/dist/api/cloud/provider-connections-service.api.cjs.map +1 -0
  248. package/dist/api/cloud/provider-connections-service.api.d.ts +5 -0
  249. package/dist/api/cloud/provider-connections-service.api.js +50 -0
  250. package/dist/api/cloud/provider-connections-service.api.js.map +1 -0
  251. package/dist/api/cloud/skills-service.api.cjs +1 -1
  252. package/dist/api/cloud/skills-service.api.cjs.map +1 -1
  253. package/dist/api/cloud/skills-service.api.d.ts +10 -0
  254. package/dist/api/cloud/skills-service.api.js +11 -1
  255. package/dist/api/cloud/skills-service.api.js.map +1 -1
  256. package/dist/api/config-service/config-service.api.cjs +1 -1
  257. package/dist/api/config-service/config-service.api.cjs.map +1 -1
  258. package/dist/api/config-service/config-service.api.js +6 -2
  259. package/dist/api/config-service/config-service.api.js.map +1 -1
  260. package/dist/api/config-service/config-service.types.d.ts +13 -0
  261. package/dist/api/conversation-metadata-store.cjs +1 -1
  262. package/dist/api/conversation-metadata-store.cjs.map +1 -1
  263. package/dist/api/conversation-metadata-store.d.ts +24 -0
  264. package/dist/api/conversation-metadata-store.js +10 -2
  265. package/dist/api/conversation-metadata-store.js.map +1 -1
  266. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  267. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  268. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +20 -0
  269. package/dist/api/conversation-service/agent-server-conversation-service.api.js +126 -77
  270. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  271. package/dist/api/hooks-service.cjs +2 -0
  272. package/dist/api/hooks-service.cjs.map +1 -0
  273. package/dist/api/hooks-service.d.ts +13 -0
  274. package/dist/api/hooks-service.js +20 -0
  275. package/dist/api/hooks-service.js.map +1 -0
  276. package/dist/api/provider-connections-service/provider-connections-service.api.cjs +1 -1
  277. package/dist/api/provider-connections-service/provider-connections-service.api.cjs.map +1 -1
  278. package/dist/api/provider-connections-service/provider-connections-service.api.js +25 -16
  279. package/dist/api/provider-connections-service/provider-connections-service.api.js.map +1 -1
  280. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  281. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  282. package/dist/api/settings-service/settings-service.api.d.ts +2 -0
  283. package/dist/api/settings-service/settings-service.api.js +18 -10
  284. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  285. package/dist/api/skills-service.cjs +1 -1
  286. package/dist/api/skills-service.cjs.map +1 -1
  287. package/dist/api/skills-service.d.ts +7 -0
  288. package/dist/api/skills-service.js +11 -8
  289. package/dist/api/skills-service.js.map +1 -1
  290. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.cjs +1 -1
  291. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.js +5 -5
  292. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.cjs +1 -1
  293. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.cjs.map +1 -1
  294. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.js +2 -1
  295. package/dist/components/conversation-events/chat/event-message-components/finish-event-message.js.map +1 -1
  296. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs +1 -1
  297. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs.map +1 -1
  298. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.d.ts +1 -1
  299. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js +28 -28
  300. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js.map +1 -1
  301. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.cjs +1 -1
  302. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.cjs.map +1 -1
  303. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.js +2 -1
  304. package/dist/components/conversation-events/chat/event-message-components/thought-event-message.js.map +1 -1
  305. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.cjs +1 -1
  306. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.cjs.map +1 -1
  307. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.d.ts +1 -1
  308. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.js +37 -37
  309. package/dist/components/conversation-events/chat/event-message-components/user-assistant-event-message.js.map +1 -1
  310. package/dist/components/conversation-events/chat/event-message.cjs +2 -2
  311. package/dist/components/conversation-events/chat/event-message.cjs.map +1 -1
  312. package/dist/components/conversation-events/chat/event-message.js +79 -70
  313. package/dist/components/conversation-events/chat/event-message.js.map +1 -1
  314. package/dist/components/conversation-events/chat/messages.cjs +1 -1
  315. package/dist/components/conversation-events/chat/messages.cjs.map +1 -1
  316. package/dist/components/conversation-events/chat/messages.js +28 -27
  317. package/dist/components/conversation-events/chat/messages.js.map +1 -1
  318. package/dist/components/features/automations/automation-card.cjs +1 -1
  319. package/dist/components/features/automations/automation-card.cjs.map +1 -1
  320. package/dist/components/features/automations/automation-card.js +104 -97
  321. package/dist/components/features/automations/automation-card.js.map +1 -1
  322. package/dist/components/features/automations/automation-list-row.cjs +1 -1
  323. package/dist/components/features/automations/automation-list-row.cjs.map +1 -1
  324. package/dist/components/features/automations/automation-list-row.js +89 -77
  325. package/dist/components/features/automations/automation-list-row.js.map +1 -1
  326. package/dist/components/features/automations/build-automation-menu-items.cjs +1 -1
  327. package/dist/components/features/automations/build-automation-menu-items.cjs.map +1 -1
  328. package/dist/components/features/automations/build-automation-menu-items.d.ts +3 -1
  329. package/dist/components/features/automations/build-automation-menu-items.js +12 -11
  330. package/dist/components/features/automations/build-automation-menu-items.js.map +1 -1
  331. package/dist/components/features/automations/detail/configuration-section.d.ts +7 -1
  332. package/dist/components/features/automations/detail/detail-header.d.ts +10 -5
  333. package/dist/components/features/automations/detail/run-status-badge.cjs +1 -1
  334. package/dist/components/features/automations/detail/run-status-badge.cjs.map +1 -1
  335. package/dist/components/features/automations/detail/run-status-badge.d.ts +2 -2
  336. package/dist/components/features/automations/detail/run-status-badge.js +82 -53
  337. package/dist/components/features/automations/detail/run-status-badge.js.map +1 -1
  338. package/dist/components/features/automations/kebab-menu.cjs +1 -1
  339. package/dist/components/features/automations/kebab-menu.cjs.map +1 -1
  340. package/dist/components/features/automations/kebab-menu.js +9 -5
  341. package/dist/components/features/automations/kebab-menu.js.map +1 -1
  342. package/dist/components/features/automations/recommended-automations-launcher.cjs +1 -1
  343. package/dist/components/features/automations/recommended-automations-launcher.cjs.map +1 -1
  344. package/dist/components/features/automations/recommended-automations-launcher.js +12 -12
  345. package/dist/components/features/automations/recommended-automations-launcher.js.map +1 -1
  346. package/dist/components/features/automations/recommended-automations-section.cjs +1 -1
  347. package/dist/components/features/automations/recommended-automations-section.js +9 -9
  348. package/dist/components/features/backends/backend-selector.cjs +1 -1
  349. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  350. package/dist/components/features/backends/backend-selector.js +123 -112
  351. package/dist/components/features/backends/backend-selector.js.map +1 -1
  352. package/dist/components/features/backends/manage-backends-modal.cjs +1 -1
  353. package/dist/components/features/backends/manage-backends-modal.cjs.map +1 -1
  354. package/dist/components/features/backends/manage-backends-modal.js +29 -29
  355. package/dist/components/features/backends/manage-backends-modal.js.map +1 -1
  356. package/dist/components/features/chat/change-agent-button.cjs +1 -1
  357. package/dist/components/features/chat/change-agent-button.js +4 -4
  358. package/dist/components/features/chat/chat-interface.cjs +2 -2
  359. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  360. package/dist/components/features/chat/chat-interface.js +137 -137
  361. package/dist/components/features/chat/chat-interface.js.map +1 -1
  362. package/dist/components/features/chat/chat-message.cjs +1 -1
  363. package/dist/components/features/chat/chat-message.cjs.map +1 -1
  364. package/dist/components/features/chat/chat-message.d.ts +2 -1
  365. package/dist/components/features/chat/chat-message.js +88 -79
  366. package/dist/components/features/chat/chat-message.js.map +1 -1
  367. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs +1 -1
  368. package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs.map +1 -1
  369. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js +62 -61
  370. package/dist/components/features/chat/components/chat-input-llm-profile-picker.js.map +1 -1
  371. package/dist/components/features/chat/components/context-window-meter.cjs +1 -1
  372. package/dist/components/features/chat/components/context-window-meter.js +4 -4
  373. package/dist/components/features/chat/confirmation-mode-enabled.cjs +1 -1
  374. package/dist/components/features/chat/confirmation-mode-enabled.js +4 -4
  375. package/dist/components/features/chat/generic-event-message.cjs +1 -1
  376. package/dist/components/features/chat/generic-event-message.cjs.map +1 -1
  377. package/dist/components/features/chat/generic-event-message.d.ts +2 -1
  378. package/dist/components/features/chat/generic-event-message.js +41 -22
  379. package/dist/components/features/chat/generic-event-message.js.map +1 -1
  380. package/dist/components/features/chat/interactive-chat-box.cjs +1 -1
  381. package/dist/components/features/chat/interactive-chat-box.cjs.map +1 -1
  382. package/dist/components/features/chat/interactive-chat-box.js +20 -19
  383. package/dist/components/features/chat/interactive-chat-box.js.map +1 -1
  384. package/dist/components/features/chat/model-messages.cjs +1 -1
  385. package/dist/components/features/chat/model-messages.cjs.map +1 -1
  386. package/dist/components/features/chat/model-messages.js +27 -26
  387. package/dist/components/features/chat/model-messages.js.map +1 -1
  388. package/dist/components/features/conversation/conversation-overview-git-section.cjs +1 -1
  389. package/dist/components/features/conversation/conversation-overview-git-section.js +4 -4
  390. package/dist/components/features/conversation/conversation-overview-skills-panel.cjs +1 -1
  391. package/dist/components/features/conversation/conversation-overview-skills-panel.js +4 -4
  392. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs +1 -1
  393. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs.map +1 -1
  394. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js +77 -78
  395. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js.map +1 -1
  396. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
  397. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
  398. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +88 -88
  399. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
  400. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.cjs +2 -0
  401. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.cjs.map +1 -0
  402. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.d.ts +21 -0
  403. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.js +239 -0
  404. package/dist/components/features/conversation-panel/advanced-conversation-options-modal.js.map +1 -0
  405. package/dist/components/features/conversation-panel/budget-display.cjs +1 -1
  406. package/dist/components/features/conversation-panel/budget-display.cjs.map +1 -1
  407. package/dist/components/features/conversation-panel/budget-display.d.ts +1 -2
  408. package/dist/components/features/conversation-panel/budget-display.js +12 -19
  409. package/dist/components/features/conversation-panel/budget-display.js.map +1 -1
  410. package/dist/components/features/conversation-panel/conversation-active-tag-filters.cjs +2 -0
  411. package/dist/components/features/conversation-panel/conversation-active-tag-filters.cjs.map +1 -0
  412. package/dist/components/features/conversation-panel/conversation-active-tag-filters.d.ts +28 -0
  413. package/dist/components/features/conversation-panel/conversation-active-tag-filters.js +69 -0
  414. package/dist/components/features/conversation-panel/conversation-active-tag-filters.js.map +1 -0
  415. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs +1 -1
  416. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs.map +1 -1
  417. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.d.ts +2 -1
  418. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js +19 -18
  419. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js.map +1 -1
  420. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
  421. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
  422. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.d.ts +2 -1
  423. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +91 -80
  424. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
  425. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -1
  426. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
  427. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +45 -44
  428. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
  429. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs +1 -1
  430. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs.map +1 -1
  431. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js +1 -1
  432. package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js.map +1 -1
  433. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs +1 -1
  434. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs.map +1 -1
  435. package/dist/components/features/conversation-panel/conversation-card/conversation-card.d.ts +7 -1
  436. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js +87 -81
  437. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js.map +1 -1
  438. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs +1 -1
  439. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs.map +1 -1
  440. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.d.ts +2 -1
  441. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js +1 -1
  442. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js.map +1 -1
  443. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs +1 -1
  444. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs.map +1 -1
  445. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.d.ts +2 -1
  446. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js +2 -1
  447. package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js.map +1 -1
  448. package/dist/components/features/conversation-panel/conversation-layouts-menu.cjs +2 -0
  449. package/dist/components/features/conversation-panel/conversation-layouts-menu.cjs.map +1 -0
  450. package/dist/components/features/conversation-panel/conversation-layouts-menu.d.ts +29 -0
  451. package/dist/components/features/conversation-panel/conversation-layouts-menu.js +177 -0
  452. package/dist/components/features/conversation-panel/conversation-layouts-menu.js.map +1 -0
  453. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
  454. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
  455. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +37 -0
  456. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +85 -45
  457. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
  458. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  459. package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
  460. package/dist/components/features/conversation-panel/conversation-panel.js +296 -290
  461. package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
  462. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.cjs +2 -0
  463. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.cjs.map +1 -0
  464. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.d.ts +22 -0
  465. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.js +153 -0
  466. package/dist/components/features/conversation-panel/edit-conversation-tags-modal.js.map +1 -0
  467. package/dist/components/features/conversation-panel/hide-older-conversations-row.cjs +2 -0
  468. package/dist/components/features/conversation-panel/hide-older-conversations-row.cjs.map +1 -0
  469. package/dist/components/features/conversation-panel/hide-older-conversations-row.d.ts +6 -0
  470. package/dist/components/features/conversation-panel/hide-older-conversations-row.js +65 -0
  471. package/dist/components/features/conversation-panel/hide-older-conversations-row.js.map +1 -0
  472. package/dist/components/features/conversation-panel/menu-row.cjs +1 -1
  473. package/dist/components/features/conversation-panel/menu-row.cjs.map +1 -1
  474. package/dist/components/features/conversation-panel/menu-row.d.ts +11 -1
  475. package/dist/components/features/conversation-panel/menu-row.js +27 -16
  476. package/dist/components/features/conversation-panel/menu-row.js.map +1 -1
  477. package/dist/components/features/home/featured-automations/automation-health-indicator.cjs +1 -1
  478. package/dist/components/features/home/featured-automations/automation-health-indicator.cjs.map +1 -1
  479. package/dist/components/features/home/featured-automations/automation-health-indicator.js +1 -1
  480. package/dist/components/features/home/featured-automations/automation-health-indicator.js.map +1 -1
  481. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.cjs +1 -1
  482. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.cjs.map +1 -1
  483. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.d.ts +4 -3
  484. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.js +22 -25
  485. package/dist/components/features/home/featured-automations/automation-run-activity-metrics.js.map +1 -1
  486. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.cjs +1 -1
  487. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.cjs.map +1 -1
  488. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.d.ts +1 -1
  489. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.js +15 -15
  490. package/dist/components/features/home/featured-automations/automation-run-activity-sparkline.js.map +1 -1
  491. package/dist/components/features/home/featured-automations/automation-run-health.cjs +1 -1
  492. package/dist/components/features/home/featured-automations/automation-run-health.cjs.map +1 -1
  493. package/dist/components/features/home/featured-automations/automation-run-health.d.ts +5 -3
  494. package/dist/components/features/home/featured-automations/automation-run-health.js +30 -23
  495. package/dist/components/features/home/featured-automations/automation-run-health.js.map +1 -1
  496. package/dist/components/features/home/featured-automations/build-home-automation-menu-items.d.ts +1 -2
  497. package/dist/components/features/home/featured-automations/build-pinned-automation-menu-items.d.ts +0 -1
  498. package/dist/components/features/home/featured-automations/home-automation-activity.d.ts +3 -2
  499. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs +1 -1
  500. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.cjs.map +1 -1
  501. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.d.ts +1 -1
  502. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js +42 -42
  503. package/dist/components/features/home/featured-automations/home-automation-run-tooltip.js.map +1 -1
  504. package/dist/components/features/images/image-lightbox.cjs +2 -0
  505. package/dist/components/features/images/image-lightbox.cjs.map +1 -0
  506. package/dist/components/features/images/image-lightbox.d.ts +11 -0
  507. package/dist/components/features/images/image-lightbox.js +37 -0
  508. package/dist/components/features/images/image-lightbox.js.map +1 -0
  509. package/dist/components/features/images/image-preview.cjs +1 -1
  510. package/dist/components/features/images/image-preview.cjs.map +1 -1
  511. package/dist/components/features/images/image-preview.d.ts +2 -1
  512. package/dist/components/features/images/image-preview.js +33 -13
  513. package/dist/components/features/images/image-preview.js.map +1 -1
  514. package/dist/components/features/manifest/manifest-form-field.d.ts +3 -1
  515. package/dist/components/features/manifest/manifest-review-step.d.ts +3 -1
  516. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +3 -4
  517. package/dist/components/features/settings/cloud-settings-link.cjs +1 -1
  518. package/dist/components/features/settings/cloud-settings-link.cjs.map +1 -1
  519. package/dist/components/features/settings/cloud-settings-link.d.ts +3 -1
  520. package/dist/components/features/settings/cloud-settings-link.js +22 -19
  521. package/dist/components/features/settings/cloud-settings-link.js.map +1 -1
  522. package/dist/components/features/settings/integrations-settings-link.cjs +1 -1
  523. package/dist/components/features/settings/integrations-settings-link.cjs.map +1 -1
  524. package/dist/components/features/settings/integrations-settings-link.d.ts +3 -1
  525. package/dist/components/features/settings/integrations-settings-link.js +20 -17
  526. package/dist/components/features/settings/integrations-settings-link.js.map +1 -1
  527. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  528. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +3 -2
  529. package/dist/components/features/settings/llm-profiles/profile-row.cjs +1 -1
  530. package/dist/components/features/settings/llm-profiles/profile-row.js +1 -1
  531. package/dist/components/features/settings/llm-profiles/profiles-body.d.ts +2 -2
  532. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs +1 -1
  533. package/dist/components/features/settings/sdk-settings/sdk-section-page.cjs.map +1 -1
  534. package/dist/components/features/settings/sdk-settings/sdk-section-page.d.ts +8 -7
  535. package/dist/components/features/settings/sdk-settings/sdk-section-page.js +172 -142
  536. package/dist/components/features/settings/sdk-settings/sdk-section-page.js.map +1 -1
  537. package/dist/components/features/settings/secrets-settings/secret-form.cjs +1 -1
  538. package/dist/components/features/settings/secrets-settings/secret-form.cjs.map +1 -1
  539. package/dist/components/features/settings/secrets-settings/secret-form.js +38 -25
  540. package/dist/components/features/settings/secrets-settings/secret-form.js.map +1 -1
  541. package/dist/components/features/settings/settings-dropdown-input.cjs +1 -1
  542. package/dist/components/features/settings/settings-dropdown-input.js +6 -6
  543. package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
  544. package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
  545. package/dist/components/features/sidebar/sidebar-rail-body.d.ts +4 -1
  546. package/dist/components/features/sidebar/sidebar-rail-body.js +157 -139
  547. package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
  548. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  549. package/dist/components/features/sidebar/sidebar.cjs.map +1 -1
  550. package/dist/components/features/sidebar/sidebar.js +28 -26
  551. package/dist/components/features/sidebar/sidebar.js.map +1 -1
  552. package/dist/components/features/sidebar/use-sidebar-onboarding-checklist.cjs +1 -1
  553. package/dist/components/features/sidebar/use-sidebar-onboarding-checklist.js +3 -3
  554. package/dist/components/features/skills/extensions-navigation.cjs +1 -1
  555. package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
  556. package/dist/components/features/skills/extensions-navigation.js +3 -3
  557. package/dist/components/features/skills/extensions-navigation.js.map +1 -1
  558. package/dist/components/providers/agent-server-ui-providers.cjs +1 -1
  559. package/dist/components/providers/agent-server-ui-providers.cjs.map +1 -1
  560. package/dist/components/providers/agent-server-ui-providers.js +33 -29
  561. package/dist/components/providers/agent-server-ui-providers.js.map +1 -1
  562. package/dist/components/shared/buttons/remove-button.cjs +1 -1
  563. package/dist/components/shared/buttons/remove-button.cjs.map +1 -1
  564. package/dist/components/shared/buttons/remove-button.d.ts +2 -1
  565. package/dist/components/shared/buttons/remove-button.js +2 -1
  566. package/dist/components/shared/buttons/remove-button.js.map +1 -1
  567. package/dist/components/shared/buttons/styled-tooltip.cjs +1 -1
  568. package/dist/components/shared/buttons/styled-tooltip.cjs.map +1 -1
  569. package/dist/components/shared/buttons/styled-tooltip.d.ts +2 -1
  570. package/dist/components/shared/buttons/styled-tooltip.js +2 -1
  571. package/dist/components/shared/buttons/styled-tooltip.js.map +1 -1
  572. package/dist/components/shared/filters/enum-filter-dropdown.cjs +1 -1
  573. package/dist/components/shared/filters/enum-filter-dropdown.cjs.map +1 -1
  574. package/dist/components/shared/filters/enum-filter-dropdown.d.ts +5 -1
  575. package/dist/components/shared/filters/enum-filter-dropdown.js +14 -14
  576. package/dist/components/shared/filters/enum-filter-dropdown.js.map +1 -1
  577. package/dist/components/shared/free-models-note.cjs +2 -0
  578. package/dist/components/shared/free-models-note.cjs.map +1 -0
  579. package/dist/components/shared/free-models-note.d.ts +3 -0
  580. package/dist/components/shared/free-models-note.js +20 -0
  581. package/dist/components/shared/free-models-note.js.map +1 -0
  582. package/dist/components/shared/modals/confirmation-modal.cjs +1 -1
  583. package/dist/components/shared/modals/confirmation-modal.cjs.map +1 -1
  584. package/dist/components/shared/modals/confirmation-modal.d.ts +2 -1
  585. package/dist/components/shared/modals/confirmation-modal.js +8 -8
  586. package/dist/components/shared/modals/confirmation-modal.js.map +1 -1
  587. package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
  588. package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
  589. package/dist/components/shared/modals/settings/model-selector.js +102 -112
  590. package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
  591. package/dist/config/defaults.cjs +1 -1
  592. package/dist/config/defaults.cjs.map +1 -1
  593. package/dist/config/defaults.js +3 -3
  594. package/dist/config/defaults.js.map +1 -1
  595. package/dist/constants/acp-providers.cjs.map +1 -1
  596. package/dist/constants/acp-providers.d.ts +12 -0
  597. package/dist/constants/acp-providers.js.map +1 -1
  598. package/dist/constants/settings-nav.d.ts +8 -0
  599. package/dist/contexts/conversation-websocket-context.cjs +3 -3
  600. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  601. package/dist/contexts/conversation-websocket-context.d.ts +5 -0
  602. package/dist/contexts/conversation-websocket-context.js +194 -198
  603. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  604. package/dist/contexts/websocket-provider-wrapper.cjs +1 -1
  605. package/dist/contexts/websocket-provider-wrapper.cjs.map +1 -1
  606. package/dist/contexts/websocket-provider-wrapper.js +31 -15
  607. package/dist/contexts/websocket-provider-wrapper.js.map +1 -1
  608. package/dist/hooks/chat/record-model-switch-message.cjs +1 -1
  609. package/dist/hooks/chat/record-model-switch-message.cjs.map +1 -1
  610. package/dist/hooks/chat/record-model-switch-message.d.ts +31 -0
  611. package/dist/hooks/chat/record-model-switch-message.js +25 -12
  612. package/dist/hooks/chat/record-model-switch-message.js.map +1 -1
  613. package/dist/hooks/chat/use-plan-mode-interceptor.cjs +2 -0
  614. package/dist/hooks/chat/use-plan-mode-interceptor.cjs.map +1 -0
  615. package/dist/hooks/chat/use-plan-mode-interceptor.d.ts +16 -0
  616. package/dist/hooks/chat/use-plan-mode-interceptor.js +43 -0
  617. package/dist/hooks/chat/use-plan-mode-interceptor.js.map +1 -0
  618. package/dist/hooks/chat/use-slash-command.cjs +1 -1
  619. package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
  620. package/dist/hooks/chat/use-slash-command.js +63 -62
  621. package/dist/hooks/chat/use-slash-command.js.map +1 -1
  622. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  623. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  624. package/dist/hooks/mutation/use-create-conversation.js +5 -1
  625. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  626. package/dist/hooks/mutation/use-read-conversation-file.cjs +1 -1
  627. package/dist/hooks/mutation/use-read-conversation-file.cjs.map +1 -1
  628. package/dist/hooks/mutation/use-read-conversation-file.js +11 -4
  629. package/dist/hooks/mutation/use-read-conversation-file.js.map +1 -1
  630. package/dist/hooks/mutation/use-save-settings.cjs +1 -1
  631. package/dist/hooks/mutation/use-save-settings.js +3 -3
  632. package/dist/hooks/mutation/use-switch-llm-profile.cjs +1 -1
  633. package/dist/hooks/mutation/use-switch-llm-profile.cjs.map +1 -1
  634. package/dist/hooks/mutation/use-switch-llm-profile.js +13 -25
  635. package/dist/hooks/mutation/use-switch-llm-profile.js.map +1 -1
  636. package/dist/hooks/mutation/use-update-conversation-tags.cjs +2 -0
  637. package/dist/hooks/mutation/use-update-conversation-tags.cjs.map +1 -0
  638. package/dist/hooks/mutation/use-update-conversation-tags.d.ts +8 -0
  639. package/dist/hooks/mutation/use-update-conversation-tags.js +45 -0
  640. package/dist/hooks/mutation/use-update-conversation-tags.js.map +1 -0
  641. package/dist/hooks/query/query-keys.cjs +1 -1
  642. package/dist/hooks/query/query-keys.cjs.map +1 -1
  643. package/dist/hooks/query/query-keys.d.ts +6 -0
  644. package/dist/hooks/query/query-keys.js +2 -2
  645. package/dist/hooks/query/query-keys.js.map +1 -1
  646. package/dist/hooks/query/use-automation-sdk-version.cjs +1 -1
  647. package/dist/hooks/query/use-automation-sdk-version.cjs.map +1 -1
  648. package/dist/hooks/query/use-automation-sdk-version.d.ts +0 -1
  649. package/dist/hooks/query/use-automation-sdk-version.js +30 -55
  650. package/dist/hooks/query/use-automation-sdk-version.js.map +1 -1
  651. package/dist/hooks/query/use-conversation-skills.cjs +1 -1
  652. package/dist/hooks/query/use-conversation-skills.cjs.map +1 -1
  653. package/dist/hooks/query/use-conversation-skills.d.ts +14 -5
  654. package/dist/hooks/query/use-conversation-skills.js +20 -4
  655. package/dist/hooks/query/use-conversation-skills.js.map +1 -1
  656. package/dist/hooks/query/use-free-models.cjs +2 -0
  657. package/dist/hooks/query/use-free-models.cjs.map +1 -0
  658. package/dist/hooks/query/use-free-models.d.ts +23 -0
  659. package/dist/hooks/query/use-free-models.js +67 -0
  660. package/dist/hooks/query/use-free-models.js.map +1 -0
  661. package/dist/hooks/query/use-local-git-info.cjs.map +1 -1
  662. package/dist/hooks/query/use-local-git-info.js.map +1 -1
  663. package/dist/hooks/query/use-provider-connections.cjs +1 -1
  664. package/dist/hooks/query/use-provider-connections.cjs.map +1 -1
  665. package/dist/hooks/query/use-provider-connections.d.ts +6 -3
  666. package/dist/hooks/query/use-provider-connections.js +7 -3
  667. package/dist/hooks/query/use-provider-connections.js.map +1 -1
  668. package/dist/hooks/query/use-provider-models.cjs +1 -1
  669. package/dist/hooks/query/use-provider-models.cjs.map +1 -1
  670. package/dist/hooks/query/use-provider-models.js +34 -25
  671. package/dist/hooks/query/use-provider-models.js.map +1 -1
  672. package/dist/hooks/query/use-search-providers.cjs +1 -1
  673. package/dist/hooks/query/use-search-providers.cjs.map +1 -1
  674. package/dist/hooks/query/use-search-providers.js +31 -19
  675. package/dist/hooks/query/use-search-providers.js.map +1 -1
  676. package/dist/hooks/query/use-sub-conversations.cjs +1 -1
  677. package/dist/hooks/query/use-sub-conversations.cjs.map +1 -1
  678. package/dist/hooks/query/use-sub-conversations.js +16 -16
  679. package/dist/hooks/query/use-sub-conversations.js.map +1 -1
  680. package/dist/hooks/use-acp-model-context.cjs +1 -1
  681. package/dist/hooks/use-acp-model-context.js +3 -3
  682. package/dist/hooks/use-agent-state.cjs +1 -1
  683. package/dist/hooks/use-agent-state.cjs.map +1 -1
  684. package/dist/hooks/use-agent-state.d.ts +17 -1
  685. package/dist/hooks/use-agent-state.js +19 -9
  686. package/dist/hooks/use-agent-state.js.map +1 -1
  687. package/dist/hooks/use-automation-permissions.cjs +2 -0
  688. package/dist/hooks/use-automation-permissions.cjs.map +1 -0
  689. package/dist/hooks/use-automation-permissions.d.ts +43 -0
  690. package/dist/hooks/use-automation-permissions.js +52 -0
  691. package/dist/hooks/use-automation-permissions.js.map +1 -0
  692. package/dist/hooks/use-chat-input-model-state.cjs +1 -1
  693. package/dist/hooks/use-chat-input-model-state.js +3 -3
  694. package/dist/hooks/use-handle-plan-click.cjs +1 -1
  695. package/dist/hooks/use-handle-plan-click.cjs.map +1 -1
  696. package/dist/hooks/use-handle-plan-click.d.ts +3 -1
  697. package/dist/hooks/use-handle-plan-click.js +85 -26
  698. package/dist/hooks/use-handle-plan-click.js.map +1 -1
  699. package/dist/hooks/use-home-automation-actions.cjs +1 -1
  700. package/dist/hooks/use-home-automation-actions.cjs.map +1 -1
  701. package/dist/hooks/use-home-automation-actions.d.ts +1 -2
  702. package/dist/hooks/use-home-automation-actions.js +1 -1
  703. package/dist/hooks/use-home-automation-actions.js.map +1 -1
  704. package/dist/hooks/use-llm-configured.cjs +1 -1
  705. package/dist/hooks/use-llm-configured.js +3 -3
  706. package/dist/hooks/use-manifest-preflight.d.ts +1 -1
  707. package/dist/hooks/use-unified-websocket-status.cjs +1 -1
  708. package/dist/hooks/use-unified-websocket-status.cjs.map +1 -1
  709. package/dist/hooks/use-unified-websocket-status.d.ts +7 -0
  710. package/dist/hooks/use-unified-websocket-status.js +5 -1
  711. package/dist/hooks/use-unified-websocket-status.js.map +1 -1
  712. package/dist/i18n/declaration.cjs +1 -1
  713. package/dist/i18n/declaration.cjs.map +1 -1
  714. package/dist/i18n/declaration.d.ts +71 -14
  715. package/dist/i18n/declaration.js +1 -1
  716. package/dist/i18n/declaration.js.map +1 -1
  717. package/dist/i18n/translation.cjs +2 -2
  718. package/dist/i18n/translation.cjs.map +1 -1
  719. package/dist/i18n/translation.js +1281 -312
  720. package/dist/i18n/translation.js.map +1 -1
  721. package/dist/locales/ar/openhands.json +79 -22
  722. package/dist/locales/ca/openhands.json +79 -22
  723. package/dist/locales/de/openhands.json +79 -22
  724. package/dist/locales/en/openhands.json +79 -22
  725. package/dist/locales/es/openhands.json +79 -22
  726. package/dist/locales/fr/openhands.json +79 -22
  727. package/dist/locales/it/openhands.json +79 -22
  728. package/dist/locales/ja/openhands.json +79 -22
  729. package/dist/locales/ko-KR/openhands.json +79 -22
  730. package/dist/locales/no/openhands.json +79 -22
  731. package/dist/locales/pt/openhands.json +79 -22
  732. package/dist/locales/tr/openhands.json +79 -22
  733. package/dist/locales/uk/openhands.json +79 -22
  734. package/dist/locales/zh-CN/openhands.json +79 -22
  735. package/dist/locales/zh-TW/openhands.json +79 -22
  736. package/dist/manifests/automation-insights.cjs.map +1 -1
  737. package/dist/manifests/automation-insights.d.ts +10 -2
  738. package/dist/manifests/automation-insights.js.map +1 -1
  739. package/dist/manifests/automation-setup.cjs +1 -1
  740. package/dist/manifests/automation-setup.cjs.map +1 -1
  741. package/dist/manifests/automation-setup.d.ts +9 -16
  742. package/dist/manifests/automation-setup.js +17 -7
  743. package/dist/manifests/automation-setup.js.map +1 -1
  744. package/dist/manifests/manifest-actions.d.ts +1 -1
  745. package/dist/manifests/manifest-capabilities.d.ts +3 -1
  746. package/dist/manifests/manifest-local-validation.cjs +2 -0
  747. package/dist/manifests/manifest-local-validation.cjs.map +1 -0
  748. package/dist/manifests/manifest-local-validation.d.ts +15 -11
  749. package/dist/manifests/manifest-local-validation.js +8 -0
  750. package/dist/manifests/manifest-local-validation.js.map +1 -0
  751. package/dist/manifests/manifest-template.cjs +2 -0
  752. package/dist/manifests/manifest-template.cjs.map +1 -0
  753. package/dist/manifests/manifest-template.d.ts +4 -4
  754. package/dist/manifests/manifest-template.js +12 -0
  755. package/dist/manifests/manifest-template.js.map +1 -0
  756. package/dist/manifests/manifest-validation.cjs +1 -1
  757. package/dist/manifests/manifest-validation.cjs.map +1 -1
  758. package/dist/manifests/manifest-validation.js +106 -28
  759. package/dist/manifests/manifest-validation.js.map +1 -1
  760. package/dist/manifests/types.cjs.map +1 -1
  761. package/dist/manifests/types.d.ts +39 -4
  762. package/dist/manifests/types.js.map +1 -1
  763. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.cjs +8 -0
  764. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.cjs.map +1 -0
  765. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.js +221 -0
  766. package/dist/node_modules/@openhands/extensions/automations/catalog/custom-automation/manifest.js.map +1 -0
  767. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs +1 -1
  768. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.cjs.map +1 -1
  769. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js +1 -1
  770. package/dist/node_modules/@openhands/extensions/automations/catalog/github-issue-to-pr/manifest.js.map +1 -1
  771. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs +1 -1
  772. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.cjs.map +1 -1
  773. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js +1 -1
  774. package/dist/node_modules/@openhands/extensions/automations/catalog/github-pr-reviewer/manifest.js.map +1 -1
  775. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs +1 -1
  776. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.cjs.map +1 -1
  777. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js +1 -1
  778. package/dist/node_modules/@openhands/extensions/automations/catalog/qa-changes/manifest.js.map +1 -1
  779. package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs +1 -1
  780. package/dist/node_modules/@openhands/extensions/automations/catalog-index.cjs.map +1 -1
  781. package/dist/node_modules/@openhands/extensions/automations/catalog-index.js +22 -20
  782. package/dist/node_modules/@openhands/extensions/automations/catalog-index.js.map +1 -1
  783. package/dist/node_modules/@openhands/extensions/automations/interface.cjs +1 -1
  784. package/dist/node_modules/@openhands/extensions/automations/interface.cjs.map +1 -1
  785. package/dist/node_modules/@openhands/extensions/automations/interface.js +2 -1
  786. package/dist/node_modules/@openhands/extensions/automations/interface.js.map +1 -1
  787. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs +1 -1
  788. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.cjs.map +1 -1
  789. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js +24 -5
  790. package/dist/node_modules/@openhands/extensions/integrations/catalog/gitlab.js.map +1 -1
  791. package/dist/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs +1 -1
  792. package/dist/node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js +1 -1
  793. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.cjs +2 -0
  794. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.cjs.map +1 -0
  795. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.js +21 -0
  796. package/dist/node_modules/lucide-react/dist/esm/icons/circle-question-mark.js.map +1 -0
  797. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.cjs +2 -0
  798. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.cjs.map +1 -0
  799. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.js +23 -0
  800. package/dist/node_modules/lucide-react/dist/esm/icons/shrink.js.map +1 -0
  801. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.cjs +2 -0
  802. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.cjs.map +1 -0
  803. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.js +43 -0
  804. package/dist/node_modules/lucide-react/dist/esm/icons/sliders-horizontal.js.map +1 -0
  805. package/dist/node_modules/lucide-react/dist/esm/icons/tags.cjs +2 -0
  806. package/dist/node_modules/lucide-react/dist/esm/icons/tags.cjs.map +1 -0
  807. package/dist/node_modules/lucide-react/dist/esm/icons/tags.js +22 -0
  808. package/dist/node_modules/lucide-react/dist/esm/icons/tags.js.map +1 -0
  809. package/dist/package.cjs +1 -1
  810. package/dist/package.cjs.map +1 -1
  811. package/dist/package.js +4 -4
  812. package/dist/package.js.map +1 -1
  813. package/dist/routes/agent-settings.d.ts +2 -0
  814. package/dist/routes/app-settings.cjs +1 -1
  815. package/dist/routes/app-settings.cjs.map +1 -1
  816. package/dist/routes/app-settings.js +53 -48
  817. package/dist/routes/app-settings.js.map +1 -1
  818. package/dist/routes/llm-settings.cjs +1 -1
  819. package/dist/routes/llm-settings.cjs.map +1 -1
  820. package/dist/routes/llm-settings.d.ts +3 -1
  821. package/dist/routes/llm-settings.js +120 -127
  822. package/dist/routes/llm-settings.js.map +1 -1
  823. package/dist/routes/planner-tab.cjs +1 -1
  824. package/dist/routes/planner-tab.cjs.map +1 -1
  825. package/dist/routes/planner-tab.js +44 -28
  826. package/dist/routes/planner-tab.js.map +1 -1
  827. package/dist/services/telemetry.cjs +1 -1
  828. package/dist/services/telemetry.cjs.map +1 -1
  829. package/dist/services/telemetry.d.ts +5 -0
  830. package/dist/services/telemetry.js +122 -115
  831. package/dist/services/telemetry.js.map +1 -1
  832. package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
  833. package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
  834. package/dist/stores/conversation-panel-preferences-store.d.ts +32 -5
  835. package/dist/stores/conversation-panel-preferences-store.js +29 -12
  836. package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
  837. package/dist/stores/conversation-state-store.cjs +1 -1
  838. package/dist/stores/conversation-state-store.cjs.map +1 -1
  839. package/dist/stores/conversation-state-store.d.ts +9 -3
  840. package/dist/stores/conversation-state-store.js +6 -3
  841. package/dist/stores/conversation-state-store.js.map +1 -1
  842. package/dist/stores/conversation-store.cjs +1 -1
  843. package/dist/stores/conversation-store.cjs.map +1 -1
  844. package/dist/stores/conversation-store.d.ts +2 -0
  845. package/dist/stores/conversation-store.js +3 -0
  846. package/dist/stores/conversation-store.js.map +1 -1
  847. package/dist/stores/free-models-store.cjs +2 -0
  848. package/dist/stores/free-models-store.cjs.map +1 -0
  849. package/dist/stores/free-models-store.d.ts +30 -0
  850. package/dist/stores/free-models-store.js +22 -0
  851. package/dist/stores/free-models-store.js.map +1 -0
  852. package/dist/stores/model-store.cjs +1 -1
  853. package/dist/stores/model-store.cjs.map +1 -1
  854. package/dist/stores/model-store.d.ts +7 -0
  855. package/dist/stores/model-store.js +4 -0
  856. package/dist/stores/model-store.js.map +1 -1
  857. package/dist/types/automation.cjs.map +1 -1
  858. package/dist/types/automation.d.ts +46 -1
  859. package/dist/types/automation.js.map +1 -1
  860. package/dist/ui/context-menu.d.ts +1 -1
  861. package/dist/ui/toggle-switch.cjs +1 -1
  862. package/dist/ui/toggle-switch.cjs.map +1 -1
  863. package/dist/ui/toggle-switch.d.ts +4 -1
  864. package/dist/ui/toggle-switch.js +4 -3
  865. package/dist/ui/toggle-switch.js.map +1 -1
  866. package/dist/utils/automation-catalog.cjs +1 -1
  867. package/dist/utils/automation-catalog.cjs.map +1 -1
  868. package/dist/utils/automation-catalog.d.ts +12 -1
  869. package/dist/utils/automation-catalog.js +42 -12
  870. package/dist/utils/automation-catalog.js.map +1 -1
  871. package/dist/utils/automation-run-display.cjs +2 -0
  872. package/dist/utils/automation-run-display.cjs.map +1 -0
  873. package/dist/utils/automation-run-display.d.ts +20 -0
  874. package/dist/utils/automation-run-display.js +85 -0
  875. package/dist/utils/automation-run-display.js.map +1 -0
  876. package/dist/utils/automation-schedule.cjs +1 -1
  877. package/dist/utils/automation-schedule.cjs.map +1 -1
  878. package/dist/utils/automation-schedule.d.ts +16 -0
  879. package/dist/utils/automation-schedule.js +2 -0
  880. package/dist/utils/automation-schedule.js.map +1 -1
  881. package/dist/utils/constants.cjs +1 -1
  882. package/dist/utils/constants.cjs.map +1 -1
  883. package/dist/utils/constants.d.ts +4 -0
  884. package/dist/utils/constants.js +23 -3
  885. package/dist/utils/constants.js.map +1 -1
  886. package/dist/utils/format-event-timestamp.cjs +2 -0
  887. package/dist/utils/format-event-timestamp.cjs.map +1 -0
  888. package/dist/utils/format-event-timestamp.d.ts +1 -0
  889. package/dist/utils/format-event-timestamp.js +13 -0
  890. package/dist/utils/format-event-timestamp.js.map +1 -0
  891. package/dist/utils/format-model-name.cjs +1 -1
  892. package/dist/utils/format-model-name.cjs.map +1 -1
  893. package/dist/utils/format-model-name.d.ts +27 -6
  894. package/dist/utils/format-model-name.js +14 -6
  895. package/dist/utils/format-model-name.js.map +1 -1
  896. package/dist/utils/git-diff-stats.cjs +2 -2
  897. package/dist/utils/git-diff-stats.cjs.map +1 -1
  898. package/dist/utils/git-diff-stats.d.ts +4 -0
  899. package/dist/utils/git-diff-stats.js +12 -2
  900. package/dist/utils/git-diff-stats.js.map +1 -1
  901. package/dist/utils/plan-file.cjs +4 -0
  902. package/dist/utils/plan-file.cjs.map +1 -0
  903. package/dist/utils/plan-file.d.ts +18 -0
  904. package/dist/utils/plan-file.js +58 -0
  905. package/dist/utils/plan-file.js.map +1 -0
  906. package/dist/utils/recommended-automation-rail.cjs +1 -1
  907. package/dist/utils/recommended-automation-rail.cjs.map +1 -1
  908. package/dist/utils/recommended-automation-rail.js +1 -0
  909. package/dist/utils/recommended-automation-rail.js.map +1 -1
  910. package/dist/utils/scroll-fade-state.cjs +1 -1
  911. package/dist/utils/scroll-fade-state.cjs.map +1 -1
  912. package/dist/utils/scroll-fade-state.d.ts +6 -0
  913. package/dist/utils/scroll-fade-state.js +8 -1
  914. package/dist/utils/scroll-fade-state.js.map +1 -1
  915. package/dist/utils/sdk-settings-schema.cjs +1 -1
  916. package/dist/utils/sdk-settings-schema.cjs.map +1 -1
  917. package/dist/utils/sdk-settings-schema.d.ts +1 -0
  918. package/dist/utils/sdk-settings-schema.js +25 -22
  919. package/dist/utils/sdk-settings-schema.js.map +1 -1
  920. package/dist/utils/utils.cjs +1 -1
  921. package/dist/utils/utils.cjs.map +1 -1
  922. package/dist/utils/utils.js +11 -11
  923. package/dist/utils/utils.js.map +1 -1
  924. package/package.json +4 -4
  925. package/scripts/check-sdk-version-sync.mjs +6 -6
  926. package/scripts/dev-safe.mjs +1 -1
  927. package/scripts/dev-with-automation.mjs +1 -1
  928. package/scripts/static-server.mjs +33 -1
  929. package/build/assets/active-backend-context-CrHTlgvN.js +0 -2
  930. package/build/assets/agent-profiles-settings-Dzgb7G9z.js +0 -1
  931. package/build/assets/agent-server-conversation-service.api-tnqNjbpE.js +0 -86
  932. package/build/assets/agent-settings-DLtsqaOs.js +0 -2
  933. package/build/assets/agent-settings-fFvR-XC2.js +0 -1
  934. package/build/assets/alert-banner-HP6mX-Vr.js +0 -1
  935. package/build/assets/api-key-entry-screen-qTzrmJcr.js +0 -1
  936. package/build/assets/app-settings-dbnneVaV.js +0 -1
  937. package/build/assets/automation-detail-BmwlCUAB.js +0 -3
  938. package/build/assets/automation-git-sync-CprN5DcU.js +0 -1
  939. package/build/assets/automation-setup-route-C40kXjMY.js +0 -1
  940. package/build/assets/automations-list-DuNcRawl.js +0 -1
  941. package/build/assets/backend-form-modal-lhGwzXFI.js +0 -1
  942. package/build/assets/backend-synced-settings-badge-DPkz30BE.js +0 -1
  943. package/build/assets/badge-BU3S0VtE.js +0 -1
  944. package/build/assets/bot-dMYHVfYM.js +0 -1
  945. package/build/assets/browser-DSAo0tz1.js +0 -46
  946. package/build/assets/browser-tab-C9vy56R5.js +0 -1
  947. package/build/assets/canvas-extension-page-dx1gFrfW.js +0 -1
  948. package/build/assets/canvas-extensions-8A-XhpgP.js +0 -1
  949. package/build/assets/check-CS0vmpQQ.js +0 -1
  950. package/build/assets/chevron-down-e1QBpjVy.js +0 -1
  951. package/build/assets/chevron-right-ZG4R5G4Q.js +0 -1
  952. package/build/assets/circle-check-DzWLNsbE.js +0 -1
  953. package/build/assets/circle-plus-check-toggle-BO_kFZt3.js +0 -1
  954. package/build/assets/command-menu--UIlGL72.js +0 -1
  955. package/build/assets/commits-tab-AnZwLvh-.js +0 -1
  956. package/build/assets/confirmation-modal-Dv1ktp8K.js +0 -1
  957. package/build/assets/conversation-BbGrAO4Z.js +0 -93
  958. package/build/assets/conversation-mYT6NAoj.js +0 -1
  959. package/build/assets/conversation-panel-mYT6NAoj.js +0 -1
  960. package/build/assets/conversation-state-store-B0ImPLHO.js +0 -1
  961. package/build/assets/conversation-store-Ca3ghXap.js +0 -6
  962. package/build/assets/copy-to-clipboard-button-Ca8iSkkA.js +0 -1
  963. package/build/assets/device-verify-Bhn2e_Ye.js +0 -1
  964. package/build/assets/edit-automation-modal-DadGbJ2u.js +0 -1
  965. package/build/assets/empty-state-BoGVDecT.js +0 -1
  966. package/build/assets/enum-filter-dropdown-Bgca129f.js +0 -1
  967. package/build/assets/environment-switch-overlay-BmBRT7gv.js +0 -1
  968. package/build/assets/error-state-Body8Ngd.js +0 -1
  969. package/build/assets/extensions-hub-CcDqq_vR.js +0 -1
  970. package/build/assets/extensions-navigation-Cr4yzYk4.js +0 -1
  971. package/build/assets/external-link-DKUlnCQV.js +0 -1
  972. package/build/assets/files-tab-D4o2VyE_.js +0 -1
  973. package/build/assets/format-model-name-CasAP3nd.js +0 -1
  974. package/build/assets/get-git-path-ChKDVHZp.js +0 -1
  975. package/build/assets/git-repo-dropdown-4ROKl2cB.js +0 -1
  976. package/build/assets/git-repo-dropdown-BkqzkMmM.js +0 -1
  977. package/build/assets/highlighted-source-view-BFRxGT_Q.js +0 -1
  978. package/build/assets/home-ChpSuepX.js +0 -1
  979. package/build/assets/home-DIsB440S.js +0 -1
  980. package/build/assets/home-automation-run-tooltip-BVNDWjAH.js +0 -1
  981. package/build/assets/home-store-CY9Ej0ux.js +0 -1
  982. package/build/assets/index-home-BdlrFIif.js +0 -1
  983. package/build/assets/info-B6RQawoW.js +0 -1
  984. package/build/assets/install-server-modal-Dpy89p4g.js +0 -1
  985. package/build/assets/kebab-menu-item-content-D8hSNGmm.js +0 -1
  986. package/build/assets/launch-IrTDEFCs.js +0 -1
  987. package/build/assets/list-filter-DsEK7NFC.js +0 -1
  988. package/build/assets/llm-not-configured-banner-CIFABh3K.js +0 -27
  989. package/build/assets/llm-settings-B9juXhIa.js +0 -1
  990. package/build/assets/llm-settings-De1d5dfr.js +0 -1
  991. package/build/assets/llm-subscription-service-BpJ0ouQo.js +0 -1
  992. package/build/assets/loader-circle-CC1au6hc.js +0 -1
  993. package/build/assets/manage-backends-modal-BrPV3oB-.js +0 -1
  994. package/build/assets/manifest-80fb1c29.js +0 -1
  995. package/build/assets/manifest-subpage-layout-CAOYzrYp.js +0 -1
  996. package/build/assets/markdown-renderer-BzTlxI8x.js +0 -8
  997. package/build/assets/mcp-BTs_SXfZ.js +0 -1
  998. package/build/assets/mcp-page-BEBYqH_K.js +0 -9
  999. package/build/assets/messages-DKV9cRc2.js +0 -41
  1000. package/build/assets/modal-close-button-DDzdRvCs.js +0 -1
  1001. package/build/assets/onboarding-DV_OXoeP.js +0 -1
  1002. package/build/assets/onboarding-modal-rkyBYkeQ.js +0 -1
  1003. package/build/assets/organization-service.api-DrYNkEN_.js +0 -1
  1004. package/build/assets/pencil-47jAZH_V.js +0 -1
  1005. package/build/assets/pin-By41Vl5J.js +0 -1
  1006. package/build/assets/planner-tab-C9uZmhme.js +0 -1
  1007. package/build/assets/plug-CG5HFcNG.js +0 -1
  1008. package/build/assets/plugins-management-service-BMJgC3Ga.js +0 -1
  1009. package/build/assets/plus-C7Slq-0I.js +0 -1
  1010. package/build/assets/profiles-service.api-ChoKrL0l.js +0 -1
  1011. package/build/assets/providers-BhMcY5nv.js +0 -1
  1012. package/build/assets/proxy-9Fe1v5na.js +0 -1
  1013. package/build/assets/recommended-automations-launcher-6ABrI9Cy.js +0 -1
  1014. package/build/assets/refresh-cw-3q3Frrii.js +0 -1
  1015. package/build/assets/root-Dc8YmC9F.js +0 -2
  1016. package/build/assets/root-VDaypvK4.css +0 -1
  1017. package/build/assets/root-layout-CXkhSPYh.js +0 -2
  1018. package/build/assets/schema-field-D-y9-LDb.js +0 -1
  1019. package/build/assets/scroll-fade-state-Dw-i4SGq.js +0 -1
  1020. package/build/assets/sdk-section-page-BlFiizlG.js +0 -1
  1021. package/build/assets/secret-form-BB7the8W.js +0 -1
  1022. package/build/assets/secrets-service-ByZVENbp.js +0 -1
  1023. package/build/assets/secrets-settings-B_GpxKW2.js +0 -1
  1024. package/build/assets/settings-C0EWuBtA.js +0 -1
  1025. package/build/assets/settings-dropdown-input-BcwylHs2.js +0 -1
  1026. package/build/assets/settings-index-5w5UW2K3.js +0 -1
  1027. package/build/assets/settings-input-Db-1g8Ql.js +0 -1
  1028. package/build/assets/settings-modal-DfJ3tcJQ.js +0 -1
  1029. package/build/assets/settings-service.api-DJ1r62Zr.js +0 -1
  1030. package/build/assets/settings-switch-CS-dJDmA.js +0 -1
  1031. package/build/assets/shared-conversation-CA5ISEDy.js +0 -1
  1032. package/build/assets/shield-check-D2aCRd16.js +0 -1
  1033. package/build/assets/sidebar-mobile-menu-toggle-C_fb0Jki.js +0 -1
  1034. package/build/assets/sidebar-store-DGqHkVLw.js +0 -1
  1035. package/build/assets/skill-card-pill-row-B27-xyig.js +0 -1
  1036. package/build/assets/skill-scope-B42H-XOc.js +0 -1
  1037. package/build/assets/skills-plugins-tgF8EiXC.js +0 -1
  1038. package/build/assets/skills-settings-BgJ9PaOe.js +0 -1
  1039. package/build/assets/styled-tooltip-C4jf5Fv9.js +0 -1
  1040. package/build/assets/task-list-tab-C5zcaktH.js +0 -1
  1041. package/build/assets/toggle-switch-ChOLXv2n.js +0 -1
  1042. package/build/assets/trash-2-D7K26eDO.js +0 -1
  1043. package/build/assets/trash-D-HYiVYM.js +0 -1
  1044. package/build/assets/triangle-alert-CtdXWHUI.js +0 -1
  1045. package/build/assets/usage-tab-CVDffLPt.js +0 -1
  1046. package/build/assets/use-acp-credential-form-XIQmpX6M.js +0 -1
  1047. package/build/assets/use-activate-agent-profile-BLfmvDiG.js +0 -1
  1048. package/build/assets/use-agent-profiles-Bd9Bvvp1.js +0 -1
  1049. package/build/assets/use-canvas-extensions-Bi693ujJ.js +0 -1
  1050. package/build/assets/use-config-A9QovgXO.js +0 -1
  1051. package/build/assets/use-create-conversation-Dx3l7mFZ.js +0 -1
  1052. package/build/assets/use-create-secret-B9cUFWRG.js +0 -1
  1053. package/build/assets/use-has-permission-B3C8rQmR.js +0 -1
  1054. package/build/assets/use-is-authed-nTBcM7dE.js +0 -1
  1055. package/build/assets/use-llm-configured-BV9nZ1_j.js +0 -1
  1056. package/build/assets/use-llm-profiles-BO-P4Oyo.js +0 -1
  1057. package/build/assets/use-manifest-capabilities-Cw_flwEz.js +0 -1
  1058. package/build/assets/use-onboarding-completion-D8U7k4Qb.js +0 -1
  1059. package/build/assets/use-pinned-home-route-CckOZrmb.js +0 -1
  1060. package/build/assets/use-plugins-marketplace-7i94Zu3Z.js +0 -1
  1061. package/build/assets/use-save-agent-profile-BUjSsDXK.js +0 -1
  1062. package/build/assets/use-settings-DSXLbvBl.js +0 -1
  1063. package/build/assets/use-settings-nav-items-CwRw-CmX.js +0 -16
  1064. package/build/assets/use-tracking-D53y3TH9.js +0 -3
  1065. package/build/assets/useQueries-DlbhlH9I.js +0 -1
  1066. package/build/assets/useQuery-BSTgq1rl.js +0 -1
  1067. package/build/assets/utils-CN5ZfZy1.js +0 -1
  1068. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~czjee3a3-DqZDXfg2.js +0 -186
  1069. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ew9ih1s8-rfV8mQ9m.js +0 -1
  1070. package/build/assets/vendor~root-layout-C9Ju9DQg.js +0 -1
  1071. package/build/assets/vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-BWir3rJs.js +0 -1
  1072. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation~mcp~automations-list~aut~ibfbmr9f-cDuKr-WD.js +0 -1
  1073. package/build/assets/vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iyia17eu-Chf7vmKB.js +0 -1
  1074. package/build/assets/x-DJdhbdmt.js +0 -1
  1075. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +0 -2
  1076. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +0 -1
  1077. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +0 -35
  1078. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +0 -247
  1079. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +0 -1
  1080. package/dist/hooks/query/use-skills.cjs +0 -2
  1081. package/dist/hooks/query/use-skills.cjs.map +0 -1
  1082. package/dist/hooks/query/use-skills.d.ts +0 -7
  1083. package/dist/hooks/query/use-skills.js +0 -13
  1084. package/dist/hooks/query/use-skills.js.map +0 -1
  1085. package/dist/hooks/use-has-permission.cjs +0 -2
  1086. package/dist/hooks/use-has-permission.cjs.map +0 -1
  1087. package/dist/hooks/use-has-permission.d.ts +0 -1
  1088. package/dist/hooks/use-has-permission.js +0 -8
  1089. package/dist/hooks/use-has-permission.js.map +0 -1
  1090. package/dist/node_modules/lucide-react/dist/esm/icons/eye.cjs +0 -2
  1091. package/dist/node_modules/lucide-react/dist/esm/icons/eye.cjs.map +0 -1
  1092. package/dist/node_modules/lucide-react/dist/esm/icons/eye.js +0 -14
  1093. package/dist/node_modules/lucide-react/dist/esm/icons/eye.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-settings-schema.js","names":[],"sources":["../../src/utils/sdk-settings-schema.ts"],"sourcesContent":["import {\n SettingProminence,\n Settings,\n SettingsFieldSchema,\n SettingsSchema,\n SettingsSectionSchema,\n SettingsValue,\n} from \"#/types/settings\";\nimport { getSettingsFieldConstraints } from \"#/utils/sdk-settings-field-metadata\";\nimport {\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n} from \"#/constants/llm-subscription\";\n\nexport type SettingsFormValues = Record<string, string | boolean>;\nexport type SettingsDirtyState = Record<string, boolean>;\nexport type SdkSettingsPayload = Record<string, SettingsValue>;\nexport type SettingsValueSource = \"agent_settings\" | \"conversation_settings\";\n\nexport type SettingsView = \"basic\" | \"advanced\" | \"all\";\n\n/** Fields that are rendered by purpose-built components instead of the\n * generic `SchemaField` renderer. */\nexport const SPECIALLY_RENDERED_KEYS = new Set([\n \"llm.model\",\n \"llm.api_key\",\n \"llm.base_url\",\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n]);\n\n/** Prominence tiers visible at each view level. */\nconst VIEW_PROMINENCES: Record<SettingsView, Set<SettingProminence>> = {\n basic: new Set<SettingProminence>([\"critical\"]),\n advanced: new Set<SettingProminence>([\"critical\", \"major\"]),\n all: new Set<SettingProminence>([\"critical\", \"major\", \"minor\"]),\n};\n\n/**\n * True when `schema` looks like a usable `SettingsSchema` — i.e. an\n * object with an array `sections` field. Guards every helper in this\n * module against malformed/empty schema responses (e.g. when the\n * frontend ends up pointing at a host that does not actually serve\n * `/api/settings/agent-schema`, such as an unconfigured Vercel preview\n * origin that returns the React Router SPA shell for arbitrary\n * `/api/*` paths). Without this check, `schema.sections.filter(...)`\n * inside `SdkSectionPage` blows up with\n * `Cannot read properties of undefined (reading 'filter')` and React\n * Router escalates to a full-screen error page.\n */\nexport function isValidSettingsSchema(\n schema: SettingsSchema | null | undefined,\n): schema is SettingsSchema {\n return !!schema && Array.isArray((schema as SettingsSchema).sections);\n}\n\nfunction getSchemaFields(schema: SettingsSchema): SettingsFieldSchema[] {\n if (!isValidSettingsSchema(schema)) return [];\n return schema.sections.flatMap((section) => section.fields);\n}\n\n/** Traverse a nested object using a dotted key path (e.g. \"llm.model\"). */\nfunction lookupDotted(\n obj: Record<string, unknown> | null | undefined,\n key: string,\n): unknown {\n if (!obj) return undefined;\n const parts = key.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current == null || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/** Set a value in a nested object at a dotted key path (e.g. \"llm.model\"). */\nfunction setDotted(\n obj: Record<string, unknown>,\n key: string,\n value: unknown,\n): void {\n const parts = key.split(\".\");\n let current: Record<string, unknown> = obj;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const part = parts[i];\n if (\n current[part] == null ||\n typeof current[part] !== \"object\" ||\n Array.isArray(current[part])\n ) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n current[parts[parts.length - 1]] = value;\n}\n\nexport function getSettingValue(\n settings: Settings,\n key: string,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsValue {\n return (lookupDotted(settings[source] as Record<string, unknown>, key) ??\n null) as SettingsValue;\n}\n\nexport function getAgentSettingValue(\n settings: Settings,\n key: string,\n): SettingsValue {\n return getSettingValue(settings, key, \"agent_settings\");\n}\n\nexport function getConversationSettingValue(\n settings: Settings,\n key: string,\n): SettingsValue {\n return getSettingValue(settings, key, \"conversation_settings\");\n}\n\nfunction isChoiceField(field: SettingsFieldSchema): boolean {\n return field.choices.length > 0;\n}\n\nfunction isCriticalField(field: SettingsFieldSchema): boolean {\n return field.prominence === \"critical\";\n}\n\nfunction isMinorField(field: SettingsFieldSchema): boolean {\n return field.prominence === \"minor\";\n}\n\nexport function normalizeFieldValue(\n field: SettingsFieldSchema,\n rawValue: unknown,\n): string | boolean {\n const resolvedValue = rawValue ?? field.default;\n\n if (isChoiceField(field)) {\n return resolvedValue === null || resolvedValue === undefined\n ? \"\"\n : String(resolvedValue);\n }\n\n if (field.value_type === \"boolean\") {\n return Boolean(resolvedValue ?? false);\n }\n\n if (resolvedValue === null || resolvedValue === undefined) {\n return \"\";\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n return JSON.stringify(resolvedValue, null, 2);\n }\n\n return String(resolvedValue);\n}\n\nfunction normalizeComparableValue(\n field: SettingsFieldSchema,\n rawValue: unknown,\n): boolean | number | string | null {\n if (rawValue === undefined) {\n return null;\n }\n\n if (field.value_type === \"boolean\") {\n if (typeof rawValue === \"string\") {\n if (rawValue === \"true\") {\n return true;\n }\n if (rawValue === \"false\") {\n return false;\n }\n }\n if (rawValue === null) {\n return null;\n }\n return Boolean(rawValue);\n }\n\n if (field.value_type === \"integer\" || field.value_type === \"number\") {\n if (rawValue === \"\" || rawValue === null) {\n return null;\n }\n\n const parsedValue =\n typeof rawValue === \"number\" ? rawValue : Number(String(rawValue));\n return Number.isNaN(parsedValue) ? null : parsedValue;\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n if (rawValue === null) {\n return null;\n }\n\n // Treat empty objects as null so that serializer artifacts\n // (e.g. mcp_config: {} vs schema default null) don't trigger\n // a spurious view escalation in inferInitialView.\n if (\n field.value_type === \"object\" &&\n typeof rawValue === \"object\" &&\n !Array.isArray(rawValue) &&\n Object.keys(rawValue as Record<string, unknown>).length === 0\n ) {\n return null;\n }\n\n if (typeof rawValue === \"string\") {\n const trimmedValue = rawValue.trim();\n if (!trimmedValue) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(trimmedValue);\n // Also normalise stringified empty objects\n if (\n field.value_type === \"object\" &&\n parsed !== null &&\n typeof parsed === \"object\" &&\n !Array.isArray(parsed) &&\n Object.keys(parsed as Record<string, unknown>).length === 0\n ) {\n return null;\n }\n return JSON.stringify(parsed);\n } catch {\n return trimmedValue;\n }\n }\n\n return JSON.stringify(rawValue);\n }\n\n if (rawValue === null) {\n return null;\n }\n\n return String(rawValue);\n}\n\nexport function buildInitialSettingsFormValues(\n settings: Settings,\n schemaOverride?: SettingsSchema | null,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsFormValues {\n const schema =\n schemaOverride ??\n (source === \"conversation_settings\"\n ? settings.conversation_settings_schema\n : settings.agent_settings_schema);\n if (!schema) {\n return {};\n }\n\n return Object.fromEntries(\n getSchemaFields(schema).map((field) => [\n field.key,\n normalizeFieldValue(field, getSettingValue(settings, field.key, source)),\n ]),\n );\n}\n\nexport function inferInitialView(\n settings: Settings,\n schemaOverride?: SettingsSchema | null,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsView {\n const schema =\n schemaOverride ??\n (source === \"conversation_settings\"\n ? settings.conversation_settings_schema\n : settings.agent_settings_schema);\n if (!schema) {\n return \"basic\";\n }\n\n let hasMinorOverride = false;\n let hasMajorOverride = false;\n\n for (const field of getSchemaFields(schema)) {\n if (!isCriticalField(field)) {\n const currentValue = getSettingValue(settings, field.key, source);\n const isDifferent =\n normalizeComparableValue(\n field,\n currentValue ?? field.default ?? null,\n ) !== normalizeComparableValue(field, field.default ?? null);\n\n if (isDifferent) {\n if (isMinorField(field)) {\n hasMinorOverride = true;\n } else {\n hasMajorOverride = true;\n }\n }\n }\n }\n\n if (hasMinorOverride) return \"all\";\n if (hasMajorOverride) return \"advanced\";\n return \"basic\";\n}\n\n/** Determine which view tier to default to based on whether the user has\n * overridden any non-critical settings. */\nexport function hasAdvancedSettingsOverrides(settings: Settings): boolean {\n return inferInitialView(settings) !== \"basic\";\n}\n\nexport function isSettingsFieldVisible(\n field: SettingsFieldSchema,\n values: SettingsFormValues,\n): boolean {\n return field.depends_on.every((dependency) => values[dependency] === true);\n}\n\nfunction parseBooleanFieldValue(rawValue: string | boolean): boolean | null {\n if (typeof rawValue === \"boolean\") {\n return rawValue;\n }\n\n const normalizedValue = rawValue.trim().toLowerCase();\n if (!normalizedValue) {\n return null;\n }\n if (normalizedValue === \"true\") {\n return true;\n }\n if (normalizedValue === \"false\") {\n return false;\n }\n\n throw new Error(`Expected a boolean value, received: ${rawValue}`);\n}\n\nexport function coerceFieldValue(\n field: SettingsFieldSchema,\n rawValue: string | boolean,\n): SettingsValue {\n if (field.value_type === \"boolean\") {\n return parseBooleanFieldValue(rawValue);\n }\n\n if (field.value_type === \"integer\" || field.value_type === \"number\") {\n const stringValue = String(rawValue).trim();\n if (!stringValue) {\n return null;\n }\n\n const parsedValue = Number(stringValue);\n if (Number.isNaN(parsedValue)) {\n throw new Error(`Expected a numeric value, received: ${stringValue}`);\n }\n if (field.value_type === \"integer\" && !Number.isInteger(parsedValue)) {\n throw new Error(`Expected an integer value, received: ${stringValue}`);\n }\n\n const constraints = getSettingsFieldConstraints(field.key);\n if (constraints?.min != null && parsedValue < constraints.min) {\n throw new Error(`${field.label} must be at least ${constraints.min}`);\n }\n if (constraints?.max != null && parsedValue > constraints.max) {\n throw new Error(`${field.label} must be at most ${constraints.max}`);\n }\n\n return parsedValue;\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n const stringValue = String(rawValue).trim();\n if (!stringValue) {\n return null;\n }\n\n let parsedValue: unknown;\n try {\n parsedValue = JSON.parse(stringValue);\n } catch {\n throw new Error(`Invalid JSON for ${field.label}`);\n }\n\n if (field.value_type === \"array\") {\n if (!Array.isArray(parsedValue)) {\n throw new Error(`${field.label} must be a JSON array`);\n }\n return parsedValue as SettingsValue[];\n }\n\n if (\n parsedValue === null ||\n Array.isArray(parsedValue) ||\n typeof parsedValue !== \"object\"\n ) {\n throw new Error(`${field.label} must be a JSON object`);\n }\n\n return parsedValue as { [key: string]: SettingsValue };\n }\n\n const stringValue = String(rawValue);\n if (stringValue === \"\" && !field.secret) {\n return null;\n }\n\n return stringValue;\n}\n\nexport function buildSdkSettingsPayload(\n schema: SettingsSchema,\n values: SettingsFormValues,\n dirty: SettingsDirtyState,\n): SdkSettingsPayload {\n const payload: Record<string, unknown> = {};\n\n for (const field of getSchemaFields(schema)) {\n if (dirty[field.key]) {\n setDotted(payload, field.key, coerceFieldValue(field, values[field.key]));\n }\n }\n\n return payload as SdkSettingsPayload;\n}\n\nfunction isFieldVisibleInView(\n field: SettingsFieldSchema,\n view: SettingsView,\n): boolean {\n return VIEW_PROMINENCES[view].has(field.prominence);\n}\n\nexport function buildSdkSettingsPayloadForView(\n schema: SettingsSchema,\n values: SettingsFormValues,\n dirty: SettingsDirtyState,\n view: SettingsView,\n): SdkSettingsPayload {\n const payload = buildSdkSettingsPayload(schema, values, dirty) as Record<\n string,\n unknown\n >;\n\n for (const field of getSchemaFields(schema)) {\n if (!isFieldVisibleInView(field, view)) {\n setDotted(payload, field.key, field.default ?? null);\n }\n }\n\n return payload as SdkSettingsPayload;\n}\n\n/** Return sections with fields filtered for the current view tier.\n * Specially-rendered fields are excluded from the generic list. */\nexport function getVisibleSettingsSections(\n schema: SettingsSchema,\n values: SettingsFormValues,\n view: SettingsView,\n excludeKeys: Set<string> = SPECIALLY_RENDERED_KEYS,\n): SettingsSectionSchema[] {\n if (!isValidSettingsSchema(schema)) return [];\n return schema.sections\n .map((section) => ({\n ...section,\n fields: section.fields.filter(\n (field) =>\n !excludeKeys.has(field.key) &&\n isFieldVisibleInView(field, view) &&\n isSettingsFieldVisible(field, values),\n ),\n }))\n .filter((section) => section.fields.length > 0);\n}\n\n/** Whether the schema has any \"critical\" prominence fields (the basic tier). */\nexport function hasCriticalSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"critical\");\n}\n\n/** Whether the schema has any fields visible in the \"advanced\" tier. */\nexport function hasAdvancedSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"major\");\n}\n\n/** Whether the schema has any \"minor\" prominence fields. */\nexport function hasMinorSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"minor\");\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"sdk-settings-schema.js","names":[],"sources":["../../src/utils/sdk-settings-schema.ts"],"sourcesContent":["import {\n SettingProminence,\n Settings,\n SettingsFieldSchema,\n SettingsSchema,\n SettingsSectionSchema,\n SettingsValue,\n} from \"#/types/settings\";\nimport { getSettingsFieldConstraints } from \"#/utils/sdk-settings-field-metadata\";\nimport {\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n} from \"#/constants/llm-subscription\";\n\nexport type SettingsFormValues = Record<string, string | boolean>;\nexport type SettingsDirtyState = Record<string, boolean>;\nexport type SdkSettingsPayload = Record<string, SettingsValue>;\nexport type SettingsValueSource = \"agent_settings\" | \"conversation_settings\";\n\nexport type SettingsView = \"basic\" | \"advanced\" | \"all\";\n\n/** Fields that are rendered by purpose-built components instead of the\n * generic `SchemaField` renderer. */\nexport const SPECIALLY_RENDERED_KEYS = new Set([\n \"llm.model\",\n \"llm.api_key\",\n \"llm.base_url\",\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n]);\n\n/** Prominence tiers visible at each view level. */\nconst VIEW_PROMINENCES: Record<SettingsView, Set<SettingProminence>> = {\n basic: new Set<SettingProminence>([\"critical\"]),\n advanced: new Set<SettingProminence>([\"critical\", \"major\"]),\n all: new Set<SettingProminence>([\"critical\", \"major\", \"minor\"]),\n};\n\n/**\n * True when `schema` looks like a usable `SettingsSchema` — i.e. an\n * object with an array `sections` field. Guards every helper in this\n * module against malformed/empty schema responses (e.g. when the\n * frontend ends up pointing at a host that does not actually serve\n * `/api/settings/agent-schema`, such as an unconfigured Vercel preview\n * origin that returns the React Router SPA shell for arbitrary\n * `/api/*` paths). Without this check, `schema.sections.filter(...)`\n * inside `SdkSectionPage` blows up with\n * `Cannot read properties of undefined (reading 'filter')` and React\n * Router escalates to a full-screen error page.\n */\nexport function isValidSettingsSchema(\n schema: SettingsSchema | null | undefined,\n): schema is SettingsSchema {\n return !!schema && Array.isArray((schema as SettingsSchema).sections);\n}\n\nfunction getSchemaFields(schema: SettingsSchema): SettingsFieldSchema[] {\n if (!isValidSettingsSchema(schema)) return [];\n return schema.sections.flatMap((section) => section.fields);\n}\n\n/** Traverse a nested object using a dotted key path (e.g. \"llm.model\"). */\nfunction lookupDotted(\n obj: Record<string, unknown> | null | undefined,\n key: string,\n): unknown {\n if (!obj) return undefined;\n const parts = key.split(\".\");\n let current: unknown = obj;\n for (const part of parts) {\n if (current == null || typeof current !== \"object\") return undefined;\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n}\n\n/** Set a value in a nested object at a dotted key path (e.g. \"llm.model\"). */\nfunction setDotted(\n obj: Record<string, unknown>,\n key: string,\n value: unknown,\n): void {\n const parts = key.split(\".\");\n let current: Record<string, unknown> = obj;\n for (let i = 0; i < parts.length - 1; i += 1) {\n const part = parts[i];\n if (\n current[part] == null ||\n typeof current[part] !== \"object\" ||\n Array.isArray(current[part])\n ) {\n current[part] = {};\n }\n current = current[part] as Record<string, unknown>;\n }\n current[parts[parts.length - 1]] = value;\n}\n\nexport function getSettingValue(\n settings: Settings,\n key: string,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsValue {\n return (lookupDotted(settings[source] as Record<string, unknown>, key) ??\n null) as SettingsValue;\n}\n\nexport function getAgentSettingValue(\n settings: Settings,\n key: string,\n): SettingsValue {\n return getSettingValue(settings, key, \"agent_settings\");\n}\n\nexport function getConversationSettingValue(\n settings: Settings,\n key: string,\n): SettingsValue {\n return getSettingValue(settings, key, \"conversation_settings\");\n}\n\nfunction isChoiceField(field: SettingsFieldSchema): boolean {\n return field.choices.length > 0;\n}\n\nfunction isCriticalField(field: SettingsFieldSchema): boolean {\n return field.prominence === \"critical\";\n}\n\nfunction isMinorField(field: SettingsFieldSchema): boolean {\n return field.prominence === \"minor\";\n}\n\nexport function normalizeFieldValue(\n field: SettingsFieldSchema,\n rawValue: unknown,\n): string | boolean {\n const resolvedValue = rawValue ?? field.default;\n\n if (isChoiceField(field)) {\n return resolvedValue === null || resolvedValue === undefined\n ? \"\"\n : String(resolvedValue);\n }\n\n if (field.value_type === \"boolean\") {\n return Boolean(resolvedValue ?? false);\n }\n\n if (resolvedValue === null || resolvedValue === undefined) {\n return \"\";\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n return JSON.stringify(resolvedValue, null, 2);\n }\n\n return String(resolvedValue);\n}\n\nfunction stableJsonStringify(value: unknown): string {\n if (value === null || typeof value !== \"object\") {\n return JSON.stringify(value);\n }\n\n if (Array.isArray(value)) {\n return `[${value.map(stableJsonStringify).join(\",\")}]`;\n }\n\n const sortedEntries = Object.entries(value as Record<string, unknown>).sort(\n ([leftKey], [rightKey]) => leftKey.localeCompare(rightKey),\n );\n\n return `{${sortedEntries\n .map(\n ([key, entryValue]) =>\n `${JSON.stringify(key)}:${stableJsonStringify(entryValue)}`,\n )\n .join(\",\")}}`;\n}\n\nexport function normalizeComparableValue(\n field: SettingsFieldSchema,\n rawValue: unknown,\n): boolean | number | string | null {\n if (rawValue === undefined) {\n return null;\n }\n\n if (field.value_type === \"boolean\") {\n if (typeof rawValue === \"string\") {\n if (rawValue === \"true\") {\n return true;\n }\n if (rawValue === \"false\") {\n return false;\n }\n }\n if (rawValue === null) {\n return null;\n }\n return Boolean(rawValue);\n }\n\n if (field.value_type === \"integer\" || field.value_type === \"number\") {\n if (rawValue === \"\" || rawValue === null) {\n return null;\n }\n\n const parsedValue =\n typeof rawValue === \"number\" ? rawValue : Number(String(rawValue));\n return Number.isNaN(parsedValue) ? null : parsedValue;\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n if (rawValue === null) {\n return null;\n }\n\n // Treat empty objects as null so that serializer artifacts\n // (e.g. mcp_config: {} vs schema default null) don't trigger\n // a spurious view escalation in inferInitialView.\n if (\n field.value_type === \"object\" &&\n typeof rawValue === \"object\" &&\n !Array.isArray(rawValue) &&\n Object.keys(rawValue as Record<string, unknown>).length === 0\n ) {\n return null;\n }\n\n if (typeof rawValue === \"string\") {\n const trimmedValue = rawValue.trim();\n if (!trimmedValue) {\n return null;\n }\n try {\n const parsed: unknown = JSON.parse(trimmedValue);\n // Also normalise stringified empty objects\n if (\n field.value_type === \"object\" &&\n parsed !== null &&\n typeof parsed === \"object\" &&\n !Array.isArray(parsed) &&\n Object.keys(parsed as Record<string, unknown>).length === 0\n ) {\n return null;\n }\n return stableJsonStringify(parsed);\n } catch {\n return trimmedValue;\n }\n }\n\n return stableJsonStringify(rawValue);\n }\n\n if (rawValue === null) {\n return null;\n }\n\n return String(rawValue);\n}\n\nexport function buildInitialSettingsFormValues(\n settings: Settings,\n schemaOverride?: SettingsSchema | null,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsFormValues {\n const schema =\n schemaOverride ??\n (source === \"conversation_settings\"\n ? settings.conversation_settings_schema\n : settings.agent_settings_schema);\n if (!schema) {\n return {};\n }\n\n return Object.fromEntries(\n getSchemaFields(schema).map((field) => [\n field.key,\n normalizeFieldValue(field, getSettingValue(settings, field.key, source)),\n ]),\n );\n}\n\nexport function inferInitialView(\n settings: Settings,\n schemaOverride?: SettingsSchema | null,\n source: SettingsValueSource = \"agent_settings\",\n): SettingsView {\n const schema =\n schemaOverride ??\n (source === \"conversation_settings\"\n ? settings.conversation_settings_schema\n : settings.agent_settings_schema);\n if (!schema) {\n return \"basic\";\n }\n\n let hasMinorOverride = false;\n let hasMajorOverride = false;\n\n for (const field of getSchemaFields(schema)) {\n if (!isCriticalField(field)) {\n const currentValue = getSettingValue(settings, field.key, source);\n const isDifferent =\n normalizeComparableValue(\n field,\n currentValue ?? field.default ?? null,\n ) !== normalizeComparableValue(field, field.default ?? null);\n\n if (isDifferent) {\n if (isMinorField(field)) {\n hasMinorOverride = true;\n } else {\n hasMajorOverride = true;\n }\n }\n }\n }\n\n if (hasMinorOverride) return \"all\";\n if (hasMajorOverride) return \"advanced\";\n return \"basic\";\n}\n\n/** Determine which view tier to default to based on whether the user has\n * overridden any non-critical settings. */\nexport function hasAdvancedSettingsOverrides(settings: Settings): boolean {\n return inferInitialView(settings) !== \"basic\";\n}\n\nexport function isSettingsFieldVisible(\n field: SettingsFieldSchema,\n values: SettingsFormValues,\n): boolean {\n return field.depends_on.every((dependency) => values[dependency] === true);\n}\n\nfunction parseBooleanFieldValue(rawValue: string | boolean): boolean | null {\n if (typeof rawValue === \"boolean\") {\n return rawValue;\n }\n\n const normalizedValue = rawValue.trim().toLowerCase();\n if (!normalizedValue) {\n return null;\n }\n if (normalizedValue === \"true\") {\n return true;\n }\n if (normalizedValue === \"false\") {\n return false;\n }\n\n throw new Error(`Expected a boolean value, received: ${rawValue}`);\n}\n\nexport function coerceFieldValue(\n field: SettingsFieldSchema,\n rawValue: string | boolean,\n): SettingsValue {\n if (field.value_type === \"boolean\") {\n return parseBooleanFieldValue(rawValue);\n }\n\n if (field.value_type === \"integer\" || field.value_type === \"number\") {\n const stringValue = String(rawValue).trim();\n if (!stringValue) {\n return null;\n }\n\n const parsedValue = Number(stringValue);\n if (Number.isNaN(parsedValue)) {\n throw new Error(`Expected a numeric value, received: ${stringValue}`);\n }\n if (field.value_type === \"integer\" && !Number.isInteger(parsedValue)) {\n throw new Error(`Expected an integer value, received: ${stringValue}`);\n }\n\n const constraints = getSettingsFieldConstraints(field.key);\n if (constraints?.min != null && parsedValue < constraints.min) {\n throw new Error(`${field.label} must be at least ${constraints.min}`);\n }\n if (constraints?.max != null && parsedValue > constraints.max) {\n throw new Error(`${field.label} must be at most ${constraints.max}`);\n }\n\n return parsedValue;\n }\n\n if (field.value_type === \"array\" || field.value_type === \"object\") {\n const stringValue = String(rawValue).trim();\n if (!stringValue) {\n return null;\n }\n\n let parsedValue: unknown;\n try {\n parsedValue = JSON.parse(stringValue);\n } catch {\n throw new Error(`Invalid JSON for ${field.label}`);\n }\n\n if (field.value_type === \"array\") {\n if (!Array.isArray(parsedValue)) {\n throw new Error(`${field.label} must be a JSON array`);\n }\n return parsedValue as SettingsValue[];\n }\n\n if (\n parsedValue === null ||\n Array.isArray(parsedValue) ||\n typeof parsedValue !== \"object\"\n ) {\n throw new Error(`${field.label} must be a JSON object`);\n }\n\n return parsedValue as { [key: string]: SettingsValue };\n }\n\n const stringValue = String(rawValue);\n if (stringValue === \"\" && !field.secret) {\n return null;\n }\n\n return stringValue;\n}\n\nexport function buildSdkSettingsPayload(\n schema: SettingsSchema,\n values: SettingsFormValues,\n dirty: SettingsDirtyState,\n): SdkSettingsPayload {\n const payload: Record<string, unknown> = {};\n\n for (const field of getSchemaFields(schema)) {\n if (dirty[field.key]) {\n setDotted(payload, field.key, coerceFieldValue(field, values[field.key]));\n }\n }\n\n return payload as SdkSettingsPayload;\n}\n\nfunction isFieldVisibleInView(\n field: SettingsFieldSchema,\n view: SettingsView,\n): boolean {\n return VIEW_PROMINENCES[view].has(field.prominence);\n}\n\nexport function buildSdkSettingsPayloadForView(\n schema: SettingsSchema,\n values: SettingsFormValues,\n dirty: SettingsDirtyState,\n view: SettingsView,\n): SdkSettingsPayload {\n const payload = buildSdkSettingsPayload(schema, values, dirty) as Record<\n string,\n unknown\n >;\n\n for (const field of getSchemaFields(schema)) {\n if (!isFieldVisibleInView(field, view)) {\n setDotted(payload, field.key, field.default ?? null);\n }\n }\n\n return payload as SdkSettingsPayload;\n}\n\n/** Return sections with fields filtered for the current view tier.\n * Specially-rendered fields are excluded from the generic list. */\nexport function getVisibleSettingsSections(\n schema: SettingsSchema,\n values: SettingsFormValues,\n view: SettingsView,\n excludeKeys: Set<string> = SPECIALLY_RENDERED_KEYS,\n): SettingsSectionSchema[] {\n if (!isValidSettingsSchema(schema)) return [];\n return schema.sections\n .map((section) => ({\n ...section,\n fields: section.fields.filter(\n (field) =>\n !excludeKeys.has(field.key) &&\n isFieldVisibleInView(field, view) &&\n isSettingsFieldVisible(field, values),\n ),\n }))\n .filter((section) => section.fields.length > 0);\n}\n\n/** Whether the schema has any \"critical\" prominence fields (the basic tier). */\nexport function hasCriticalSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"critical\");\n}\n\n/** Whether the schema has any fields visible in the \"advanced\" tier. */\nexport function hasAdvancedSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"major\");\n}\n\n/** Whether the schema has any \"minor\" prominence fields. */\nexport function hasMinorSettings(schema: SettingsSchema | null): boolean {\n if (!schema) return false;\n return getSchemaFields(schema).some((f) => f.prominence === \"minor\");\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");const e=require("../i18n/declaration.cjs"),t=require("../node_modules/clsx/dist/clsx.cjs"),n=require("../node_modules/tailwind-merge/dist/bundle-mjs.cjs"),r=require("../types/agent-state.cjs"),i=require("./status.cjs"),a=require("../constants/status-colors.cjs");function o(...e){return n.twMerge(t.default(e))}var s=(e,t)=>{let n=window.URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.URL.revokeObjectURL(n)},c=(e,t)=>{let n=parseFloat(e.style.height||``);return Number.isFinite(n)?n:t},l=(e,t)=>{e.style.setProperty(`height`,`${t}px`)},u=()=>/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),d=()=>u()?!0:`ontouchstart`in window||navigator.maxTouchPoints>0?typeof window.matchMedia==`function`?!window.matchMedia(`(pointer: fine)`).matches:!0:!1,f=e=>e.split(`.`).pop()?.toUpperCase()||`FILE`,p=(e,t)=>{if(!e)return!1;switch(e){case`bitbucket`:case`bitbucket_data_center`:return!0;case`github`:return t===`cloud`;default:return!1}},m=(e,t)=>{if(t&&t.trim()!==``)return t.startsWith(`http`)?t:`https://${t}`;switch(e){case`github`:return`https://github.com`;case`gitlab`:return`https://gitlab.com`;case`bitbucket`:return`https://bitbucket.org`;case`azure_devops`:return`https://dev.azure.com`;case`forgejo`:return`https://codeberg.org`;default:return``}},h=e=>e===`gitlab`?`GitLab`:e===`bitbucket`?`Bitbucket`:e===`bitbucket_data_center`?`Bitbucket Data Center`:e===`azure_devops`?`Azure DevOps`:e===`forgejo`?`Forgejo`:`GitHub`,g=e=>e?`merge request`:`pull request`,_=e=>e?`MR`:`PR`,v=(e,t,n,r)=>{let i=m(t,r);switch(t){case`github`:return`${i}/${n}/pull/${e}`;case`forgejo`:return`${i}/${n}/pull/${e}`;case`gitlab`:return`${i}/${n}/-/merge_requests/${e}`;case`bitbucket`:return`${i}/${n}/pull-requests/${e}`;case`bitbucket_data_center`:{let[t,r]=n.split(`/`);return`${i}/projects/${t}/repos/${r}/pull-requests/${e}`}case`azure_devops`:{let t=n.split(`/`);if(t.length===3){let[n,r,a]=t;return`${i}/${n}/${r}/_git/${a}/pullrequest/${e}`}return``}default:return``}},y=(e,t,n)=>{let r=m(e,n);if(e===`bitbucket_data_center`){let[e,n]=t.split(`/`);return`${r}/projects/${e}/repos/${n}`}return`${r}/${t}`},b=(e,t,n,r)=>{let i=m(e,r);switch(e){case`github`:return`${i}/${t}/tree/${n}`;case`forgejo`:return`${i}/${t}/src/branch/${n}`;case`gitlab`:return`${i}/${t}/-/tree/${n}`;case`bitbucket`:return`${i}/${t}/src/${n}`;case`bitbucket_data_center`:{let e=t.split(`/`);if(e.length>=2){let[t,r]=e;return`${i}/projects/${t}/repos/${r}/browse?at=refs/heads/${n}`}return``}case`azure_devops`:{let e=t.split(`/`);if(e.length===3){let[t,r,a]=e;return`${i}/${t}/${r}/_git/${a}?version=GB${n}`}return``}default:return``}},x=`Please review the current changes and create a git commit with a concise, descriptive message.`,S=()=>x,C=()=>`Please pull the latest code from the repository.`,w=e=>`Please push the changes to a remote branch on ${h(e)}, but do NOT create a ${g(e===`gitlab`)}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`,T=e=>{let t=h(e),n=g(e===`gitlab`);return`Please push the changes to ${t} and open a ${n}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${n} template exists in the repository, please follow it when creating the ${_(e===`gitlab`)} description.`},E=()=>`Please create a new branch with a descriptive name related to the work you plan to do.`,D=e=>!!e&&e!==`ERROR`&&e!==`READY`,O=e=>{let{isPausing:t,isTask:n,taskStatus:i,isStartingStatus:o,isStopStatus:s,curAgentState:c}=e;return t?`#FFD600`:n&&i?i===`ERROR`?a.OH_STATUS_ERROR_COLOR:`#FFD600`:o?`#FFD600`:s?`#ffffff`:c===r.AgentState.ERROR?a.OH_STATUS_ERROR_COLOR:a.OH_STATUS_SUCCESS_COLOR};function k({isPausing:t=!1,isTask:n,taskStatus:a,taskDetail:o,isStartingStatus:s,isStopStatus:c,curAgentState:l,errorMessage:u,t:d}){return t?d(e.I18nKey.COMMON$STOPPING):n&&a?a===`ERROR`?o||d(e.I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION):a===`READY`?d(e.I18nKey.CONVERSATION$READY):o||d(i.getTaskStatusI18nKey(a)):s?d(e.I18nKey.COMMON$STARTING):c?d(e.I18nKey.COMMON$SERVER_STOPPED):l===r.AgentState.ERROR?u||d(e.I18nKey.COMMON$ERROR):d(e.I18nKey.COMMON$RUNNING)}exports.cn=o,exports.constructBranchUrl=b,exports.constructPullRequestUrl=v,exports.constructRepositoryUrl=y,exports.downloadBlob=s,exports.getCreateNewBranchPrompt=E,exports.getCreatePRPrompt=T,exports.getFileExtension=f,exports.getGitCommitPrompt=S,exports.getGitProviderBaseUrl=m,exports.getGitPullPrompt=C,exports.getGitPushPrompt=w,exports.getProviderName=h,exports.getStatusColor=O,exports.getStatusText=k,exports.getStyleHeightPx=c,exports.isMobileDevice=d,exports.isMobileUserAgent=u,exports.isTaskPolling=D,exports.setStyleHeightPx=l,exports.shouldUseInstallationRepos=p;
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../i18n/declaration.cjs"),t=require("../node_modules/clsx/dist/clsx.cjs"),n=require("../node_modules/tailwind-merge/dist/bundle-mjs.cjs"),r=require("../types/agent-state.cjs"),i=require("./status.cjs"),a=require("../constants/status-colors.cjs");function o(...e){return n.twMerge(t.default(e))}var s=(e,t)=>{let n=window.URL.createObjectURL(e),r=document.createElement(`a`);r.href=n,r.download=t,document.body.appendChild(r),r.click(),document.body.removeChild(r),window.URL.revokeObjectURL(n)},c=(e,t)=>{let n=parseFloat(e.style.height||``);return Number.isFinite(n)?n:t},l=(e,t)=>{e.style.setProperty(`height`,`${t}px`)},u=()=>/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent),d=()=>u()?!0:`ontouchstart`in window||navigator.maxTouchPoints>0?typeof window.matchMedia==`function`?!window.matchMedia(`(pointer: fine)`).matches:!0:!1,f=e=>e.split(`.`).pop()?.toUpperCase()||`FILE`,p=(e,t)=>{if(!e)return!1;switch(e){case`bitbucket`:case`bitbucket_data_center`:return!0;case`github`:return t===`cloud`;default:return!1}},m=(e,t)=>{if(t&&t.trim()!==``)return t.startsWith(`http`)?t:`https://${t}`;switch(e){case`github`:return`https://github.com`;case`gitlab`:return`https://gitlab.com`;case`bitbucket`:return`https://bitbucket.org`;case`azure_devops`:return`https://dev.azure.com`;case`forgejo`:return`https://codeberg.org`;default:return``}},h=e=>e===`gitlab`?`GitLab`:e===`bitbucket`?`Bitbucket`:e===`bitbucket_data_center`?`Bitbucket Data Center`:e===`azure_devops`?`Azure DevOps`:e===`forgejo`?`Forgejo`:`GitHub`,g=e=>e?`merge request`:`pull request`,_=e=>e?`MR`:`PR`,v=(e,t,n,r)=>{let i=m(t,r);switch(t){case`github`:return`${i}/${n}/pull/${e}`;case`forgejo`:return`${i}/${n}/pull/${e}`;case`gitlab`:return`${i}/${n}/-/merge_requests/${e}`;case`bitbucket`:return`${i}/${n}/pull-requests/${e}`;case`bitbucket_data_center`:{let[t,r]=n.split(`/`);return`${i}/projects/${t}/repos/${r}/pull-requests/${e}`}case`azure_devops`:{let t=n.split(`/`);if(t.length===3){let[n,r,a]=t;return`${i}/${n}/${r}/_git/${a}/pullrequest/${e}`}return``}default:return``}},y=(e,t,n)=>{let r=m(e,n);if(e===`bitbucket_data_center`){let[e,n]=t.split(`/`);return`${r}/projects/${e}/repos/${n}`}return`${r}/${t}`},b=e=>e.split(`/`).map(encodeURIComponent).join(`/`),x=(e,t,n,r)=>{let i=m(e,r);switch(e){case`github`:return`${i}/${t}/tree/${b(n)}`;case`forgejo`:return`${i}/${t}/src/branch/${b(n)}`;case`gitlab`:return`${i}/${t}/-/tree/${b(n)}`;case`bitbucket`:return`${i}/${t}/src/${b(n)}`;case`bitbucket_data_center`:{let e=t.split(`/`);if(e.length>=2){let[t,r]=e;return`${i}/projects/${t}/repos/${r}/browse?at=refs/heads/${encodeURIComponent(n)}`}return``}case`azure_devops`:{let e=t.split(`/`);if(e.length===3){let[t,r,a]=e;return`${i}/${t}/${r}/_git/${a}?version=GB${encodeURIComponent(n)}`}return``}default:return``}},S=`Please review the current changes and create a git commit with a concise, descriptive message.`,C=()=>S,w=()=>`Please pull the latest code from the repository.`,T=e=>`Please push the changes to a remote branch on ${h(e)}, but do NOT create a ${g(e===`gitlab`)}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`,E=e=>{let t=h(e),n=g(e===`gitlab`);return`Please push the changes to ${t} and open a ${n}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${n} template exists in the repository, please follow it when creating the ${_(e===`gitlab`)} description.`},D=()=>`Please create a new branch with a descriptive name related to the work you plan to do.`,O=e=>!!e&&e!==`ERROR`&&e!==`READY`,k=e=>{let{isPausing:t,isTask:n,taskStatus:i,isStartingStatus:o,isStopStatus:s,curAgentState:c}=e;return t?`#FFD600`:n&&i?i===`ERROR`?a.OH_STATUS_ERROR_COLOR:`#FFD600`:o?`#FFD600`:s?`#ffffff`:c===r.AgentState.ERROR?a.OH_STATUS_ERROR_COLOR:a.OH_STATUS_SUCCESS_COLOR};function A({isPausing:t=!1,isTask:n,taskStatus:a,taskDetail:o,isStartingStatus:s,isStopStatus:c,curAgentState:l,errorMessage:u,t:d}){return t?d(e.I18nKey.COMMON$STOPPING):n&&a?a===`ERROR`?o||d(e.I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION):a===`READY`?d(e.I18nKey.CONVERSATION$READY):o||d(i.getTaskStatusI18nKey(a)):s?d(e.I18nKey.COMMON$STARTING):c?d(e.I18nKey.COMMON$SERVER_STOPPED):l===r.AgentState.ERROR?u||d(e.I18nKey.COMMON$ERROR):d(e.I18nKey.COMMON$RUNNING)}exports.cn=o,exports.constructBranchUrl=x,exports.constructPullRequestUrl=v,exports.constructRepositoryUrl=y,exports.downloadBlob=s,exports.getCreateNewBranchPrompt=D,exports.getCreatePRPrompt=E,exports.getFileExtension=f,exports.getGitCommitPrompt=C,exports.getGitProviderBaseUrl=m,exports.getGitPullPrompt=w,exports.getGitPushPrompt=T,exports.getProviderName=h,exports.getStatusColor=k,exports.getStatusText=A,exports.getStyleHeightPx=c,exports.isMobileDevice=d,exports.isMobileUserAgent=u,exports.isTaskPolling=O,exports.setStyleHeightPx=l,exports.shouldUseInstallationRepos=p;
2
2
  //# sourceMappingURL=utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.cjs","names":[],"sources":["../../src/utils/utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Provider } from \"#/types/settings\";\nimport { SuggestedTaskGroup } from \"#/utils/types\";\nimport { ConversationStatus } from \"#/types/conversation-status\";\nimport { GitRepository } from \"#/types/git\";\nimport { sanitizeQuery } from \"#/utils/sanitize-query\";\nimport { PRODUCT_URL } from \"#/utils/constants\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { getTaskStatusI18nKey } from \"#/utils/status\";\nimport type { AppConversationStartTaskStatus } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n OH_STATUS_ERROR_COLOR,\n OH_STATUS_SUCCESS_COLOR,\n} from \"#/constants/status-colors\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Trigger a download for a provided Blob with the given filename\n */\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(url);\n};\n\n/**\n * Get the numeric height value from an element's style property\n * @param el The HTML element to get the height from\n * @param fallback The fallback value to return if style height is invalid\n * @returns The numeric height value in pixels, or the fallback value\n *\n * @example\n * getStyleHeightPx(element, 20) // Returns 20 if element.style.height is \"auto\" or invalid\n * getStyleHeightPx(element, 20) // Returns 100 if element.style.height is \"100px\"\n */\nexport const getStyleHeightPx = (el: HTMLElement, fallback: number): number => {\n const elementHeight = parseFloat(el.style.height || \"\");\n return Number.isFinite(elementHeight) ? elementHeight : fallback;\n};\n\n/**\n * Set the height style property of an element to a specific pixel value\n * @param el The HTML element to set the height for\n * @param height The height value in pixels to set\n *\n * @example\n * setStyleHeightPx(element, 100) // Sets element.style.height to \"100px\"\n * setStyleHeightPx(textarea, 200) // Sets textarea.style.height to \"200px\"\n */\nexport const setStyleHeightPx = (el: HTMLElement, height: number): void => {\n el.style.setProperty(\"height\", `${height}px`);\n};\n\n/**\n * Detect a phone/tablet user agent (Android, iPhone, iPad, …).\n * Unlike isMobileDevice, this ignores touch capability, so a desktop OS with\n * a touchscreen (e.g. a Windows 2-in-1) is NOT matched. Use this when the\n * decision depends on having a physical keyboard rather than on touch input.\n */\nexport const isMobileUserAgent = (): boolean =>\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent,\n );\n\n/**\n * Detect if the user is on a mobile device.\n * Touch support alone is not sufficient — touchscreen laptops have touch\n * but use a mouse/trackpad as primary input. We check that the primary\n * pointing device is coarse (finger) to avoid false positives.\n */\nexport const isMobileDevice = (): boolean => {\n if (isMobileUserAgent()) return true;\n\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n if (!hasTouch) return false;\n\n // If matchMedia is available, check whether the primary pointer is fine\n // (mouse/trackpad). Touchscreen laptops report fine, real mobile devices don't.\n if (typeof window.matchMedia === \"function\") {\n return !window.matchMedia(\"(pointer: fine)\").matches;\n }\n\n // Fallback: touch present but no matchMedia — assume mobile\n return true;\n};\n\n/**\n * Checks if the current domain is the production domain\n * @returns True if the current domain matches the production URL\n */\nexport const isProductionDomain = (): boolean =>\n window.location.origin === PRODUCT_URL.PRODUCTION;\n\ninterface EventActionHistory {\n args?: {\n LLM_API_KEY?: string;\n [key: string]: unknown;\n };\n extras?: {\n open_page_urls: string[];\n active_page_index: number;\n dom_object: Record<string, unknown>;\n axtree_object: Record<string, unknown>;\n extra_element_properties: Record<string, unknown>;\n last_browser_action: string;\n last_browser_action_error: unknown;\n focused_element_bid: string;\n };\n [key: string]: unknown;\n}\n\nexport const removeUnwantedKeys = (\n data: EventActionHistory[],\n): EventActionHistory[] => {\n const UNDESIRED_KEYS = [\n \"open_page_urls\",\n \"active_page_index\",\n \"dom_object\",\n \"axtree_object\",\n \"extra_element_properties\",\n \"last_browser_action\",\n \"last_browser_action_error\",\n \"focused_element_bid\",\n ];\n\n return data\n .filter((item) => {\n // Skip items that have a status key\n if (\"status\" in item) {\n return false;\n }\n return true;\n })\n .map((item) => {\n // Create a shallow copy of item\n const newItem = { ...item };\n\n // Check if extras exists and delete it from a new extras object\n if (newItem.extras) {\n const newExtras = { ...newItem.extras };\n UNDESIRED_KEYS.forEach((key) => {\n delete newExtras[key as keyof typeof newExtras];\n });\n newItem.extras = newExtras;\n }\n\n return newItem;\n });\n};\n\n/**\n * Get file extension from file name in uppercase format\n * @param fileName The file name to extract extension from\n * @returns The file extension in uppercase, or \"FILE\" if no extension found\n *\n * @example\n * getFileExtension(\"document.pdf\") // \"PDF\"\n * getFileExtension(\"image.jpeg\") // \"JPEG\"\n * getFileExtension(\"noextension\") // \"FILE\"\n */\nexport const getFileExtension = (fileName: string): string => {\n const extension = fileName.split(\".\").pop()?.toUpperCase();\n return extension || \"FILE\";\n};\n\n/**\n * Whether to use the installation-scoped repo flow\n * (`/api/v1/git/installations/search` → `/api/v1/git/repositories/search?installation_id=…`)\n * for the given provider/backend combo.\n *\n * Mirrors OpenHands' cloud frontend (parameterized by `app_mode`):\n * - bitbucket / bitbucket_data_center → always installation-based\n * - github → installation-based ONLY when the active backend is cloud\n * - gitlab / azure_devops / forgejo → direct (search) flow\n *\n * `appMode` accepts the active backend `kind` (\"local\" | \"cloud\") so call\n * sites can hand it through directly.\n */\nexport const shouldUseInstallationRepos = (\n provider: Provider | null | undefined,\n appMode?: \"local\" | \"cloud\",\n) => {\n if (!provider) return false;\n\n switch (provider) {\n case \"bitbucket\":\n case \"bitbucket_data_center\":\n return true;\n case \"github\":\n return appMode === \"cloud\";\n default:\n return false;\n }\n};\n\nexport const getGitProviderBaseUrl = (\n gitProvider: Provider,\n host?: string | null,\n): string => {\n // If custom host provided, use it (with https:// prefix if needed)\n if (host && host.trim() !== \"\") {\n return host.startsWith(\"http\") ? host : `https://${host}`;\n }\n\n // Fall back to defaults\n switch (gitProvider) {\n case \"github\":\n return \"https://github.com\";\n case \"gitlab\":\n return \"https://gitlab.com\";\n case \"bitbucket\":\n return \"https://bitbucket.org\";\n case \"azure_devops\":\n return \"https://dev.azure.com\";\n case \"forgejo\":\n // Default UI links to Codeberg unless a custom host is available in settings\n // Note: UI link builders don't currently receive host; consider plumbing settings if needed\n return \"https://codeberg.org\";\n default:\n return \"\";\n }\n};\n\n/**\n * Get the name of the git provider\n * @param gitProvider The git provider\n * @returns The name of the git provider\n */\nexport const getProviderName = (gitProvider: Provider) => {\n if (gitProvider === \"gitlab\") return \"GitLab\";\n if (gitProvider === \"bitbucket\") return \"Bitbucket\";\n if (gitProvider === \"bitbucket_data_center\") return \"Bitbucket Data Center\";\n if (gitProvider === \"azure_devops\") return \"Azure DevOps\";\n if (gitProvider === \"forgejo\") return \"Forgejo\";\n return \"GitHub\";\n};\n\n/**\n * Get the name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The name of the PR\n */\nexport const getPR = (isGitLab: boolean) =>\n isGitLab ? \"merge request\" : \"pull request\";\n\n/**\n * Get the short name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The short name of the PR\n */\nexport const getPRShort = (isGitLab: boolean) => (isGitLab ? \"MR\" : \"PR\");\n\n/**\n * Construct the pull request (merge request) URL for different providers\n * @param prNumber The pull request number\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The pull request URL\n *\n * @example\n * constructPullRequestUrl(123, \"github\", \"owner/repo\") // \"https://github.com/owner/repo/pull/123\"\n * constructPullRequestUrl(456, \"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo/-/merge_requests/456\"\n * constructPullRequestUrl(789, \"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo/pull-requests/789\"\n * constructPullRequestUrl(789, \"bitbucket\", \"PROJECT/repo\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/pull-requests/789\"\n */\nexport const constructPullRequestUrl = (\n prNumber: number,\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/merge_requests/${prNumber}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/pull-requests/${prNumber}`;\n case \"bitbucket_data_center\": {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}/pull-requests/${prNumber}`;\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}/pullrequest/${prNumber}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n/**\n * Construct the repository URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The repository URL\n *\n * @example\n * constructRepositoryUrl(\"github\", \"owner/repo\") // \"https://github.com/owner/repo\"\n * constructRepositoryUrl(\"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo\"\n * constructRepositoryUrl(\"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo\"\n */\nexport const constructRepositoryUrl = (\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n if (provider === \"bitbucket_data_center\") {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}`;\n }\n return `${baseUrl}/${repositoryName}`;\n};\n\n/**\n * Construct the branch URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @param branchName The branch name\n * @param host Optional custom host for self-hosted instances\n * @returns The branch URL\n *\n * @example\n * constructBranchUrl(\"github\", \"owner/repo\", \"main\") // \"https://github.com/owner/repo/tree/main\"\n * constructBranchUrl(\"gitlab\", \"owner/repo\", \"develop\") // \"https://gitlab.com/owner/repo/-/tree/develop\"\n * constructBranchUrl(\"bitbucket\", \"owner/repo\", \"feature\") // \"https://bitbucket.org/owner/repo/src/feature\"\n * constructBranchUrl(\"bitbucket\", \"PROJECT/repo\", \"feature\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/browse?at=refs/heads/feature\"\n */\nexport const constructBranchUrl = (\n provider: Provider,\n repositoryName: string,\n branchName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/tree/${branchName}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/src/branch/${branchName}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/tree/${branchName}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/src/${branchName}`;\n case \"bitbucket_data_center\": {\n // Bitbucket Server format: /projects/{PROJECT}/repos/{repo}/browse?at=refs/heads/{branch}\n const parts = repositoryName.split(\"/\");\n if (parts.length >= 2) {\n const [project, repo] = parts;\n return `${baseUrl}/projects/${project}/repos/${repo}/browse?at=refs/heads/${branchName}`;\n }\n return \"\";\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}?version=GB${branchName}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n// Git Action Prompts\n\nconst GIT_COMMIT_PROMPT =\n \"Please review the current changes and create a git commit with a concise, descriptive message.\";\n\nexport const getGitCommitPrompt = (): string => GIT_COMMIT_PROMPT;\n\n/**\n * Generate a git pull prompt\n * @returns The git pull prompt\n */\nexport const getGitPullPrompt = (): string =>\n \"Please pull the latest code from the repository.\";\n\n/**\n * Generate a git push prompt\n * @param gitProvider The git provider\n * @returns The git push prompt\n */\nexport const getGitPushPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the changes to a remote branch on ${providerName}, but do NOT create a ${pr}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`;\n};\n\n/**\n * Generate a create pull request prompt\n * @param gitProvider The git provider\n * @returns The create PR prompt\n */\nexport const getCreatePRPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n const prShort = getPRShort(gitProvider === \"gitlab\");\n\n return `Please push the changes to ${providerName} and open a ${pr}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.`;\n};\n\n/**\n * Generate a push to existing PR prompt\n * @param gitProvider The git provider\n * @returns The push to PR prompt\n */\nexport const getPushToPRPrompt = (gitProvider: Provider): string => {\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the latest changes to the existing ${pr}.`;\n};\n\n/**\n * Generate a create new branch prompt\n * @returns The create new branch prompt\n */\nexport const getCreateNewBranchPrompt = (): string =>\n \"Please create a new branch with a descriptive name related to the work you plan to do.\";\n\n// Helper functions\nexport function getTotalTaskCount(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n): number {\n if (!suggestedTasks) return 0;\n return suggestedTasks.flatMap((group) => group.tasks).length;\n}\n\nexport function getLimitedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[],\n maxTasks: number,\n): SuggestedTaskGroup[] {\n const limitedGroups: SuggestedTaskGroup[] = [];\n let taskCount = 0;\n\n for (const group of suggestedTasks) {\n if (taskCount >= maxTasks) break;\n\n const remainingTasksNeeded = maxTasks - taskCount;\n const tasksToShow = group.tasks.slice(0, remainingTasksNeeded);\n\n if (tasksToShow.length > 0) {\n limitedGroups.push({\n ...group,\n tasks: tasksToShow,\n });\n taskCount += tasksToShow.length;\n }\n }\n\n return limitedGroups;\n}\n\nexport function getDisplayedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n isExpanded: boolean,\n): SuggestedTaskGroup[] {\n if (!suggestedTasks || suggestedTasks.length === 0) {\n return [];\n }\n\n if (isExpanded) {\n return suggestedTasks;\n }\n\n return getLimitedTaskGroups(suggestedTasks, 3);\n}\n\n/**\n * Get the label for a conversation status\n * @param status The conversation status\n * @returns The localized label for the status\n */\nexport const getConversationStatusLabel = (\n status: ConversationStatus,\n): string => {\n switch (status) {\n case \"STOPPED\":\n return \"COMMON$STOPPED\";\n case \"RUNNING\":\n return \"COMMON$RUNNING\";\n case \"STARTING\":\n return \"COMMON$STARTING\";\n case \"ERROR\":\n return \"COMMON$ERROR\";\n case \"ARCHIVED\":\n return \"COMMON$ARCHIVED\"; // Use STOPPED for archived conversations\n default:\n return \"COMMON$UNKNOWN\";\n }\n};\n\n// Task Tracking Utility Functions\n\n/**\n * Get the status icon for a task status\n * @param status The task status\n * @returns The emoji icon for the status\n */\nexport const getStatusIcon = (status: string) => {\n switch (status) {\n case \"todo\":\n return \"⏳\";\n case \"in_progress\":\n return \"🔄\";\n case \"done\":\n return \"✅\";\n default:\n return \"❓\";\n }\n};\n\n/**\n * Get the CSS class names for a task status badge\n * @param status The task status\n * @returns The CSS class names for styling the status badge\n */\nexport const getStatusClassName = (status: string) => {\n if (status === \"done\") {\n return \"bg-green-800 text-green-200\";\n }\n if (status === \"in_progress\") {\n return \"bg-yellow-800 text-yellow-200\";\n }\n return \"bg-tertiary text-[var(--oh-text-tertiary)]\";\n};\n\n/**\n * Helper function to apply client-side filtering based on search query\n * @param repo The Git repository to check\n * @param searchQuery The search query string\n * @returns True if the repository should be included based on the search query\n */\nexport const shouldIncludeRepository = (\n repo: GitRepository,\n searchQuery: string,\n): boolean => {\n if (!searchQuery.trim()) {\n return true;\n }\n\n const sanitizedQuery = sanitizeQuery(searchQuery);\n const sanitizedRepoName = sanitizeQuery(repo.full_name);\n return sanitizedRepoName.includes(sanitizedQuery);\n};\n\n/**\n * Get the OpenHands query string based on the provider\n * @param provider The git provider\n * @returns The query string for searching OpenHands repositories\n */\nexport const getOpenHandsQuery = (provider: Provider | null): string => {\n const providerRepositorySuffix: Record<string, string> = {\n gitlab: \"openhands-config\",\n azure_devops: \"openhands-config\",\n default: \".openhands\",\n } as const;\n\n return provider && provider in providerRepositorySuffix\n ? providerRepositorySuffix[provider]\n : providerRepositorySuffix.default;\n};\n\n/**\n * Check if a repository has the OpenHands suffix based on the provider\n * @param repo The Git repository to check\n * @param provider The git provider\n * @returns True if the repository has the OpenHands suffix\n */\nexport const hasOpenHandsSuffix = (\n repo: GitRepository,\n provider: Provider | null,\n): boolean => repo.full_name.endsWith(`/${getOpenHandsQuery(provider)}`);\n\n/**\n * Build headers for V1 API requests that require session authentication\n * @param sessionApiKey Session API key for authentication\n * @returns Headers object with X-Session-API-Key if provided\n */\nexport const buildSessionHeaders = (\n sessionApiKey?: string | null,\n): Record<string, string> => {\n const headers: Record<string, string> = {};\n if (sessionApiKey) {\n headers[\"X-Session-API-Key\"] = sessionApiKey;\n }\n return headers;\n};\n\n/**\n * Check if a task is currently being polled (loading state)\n * @param taskStatus The task status string (e.g., \"WORKING\", \"ERROR\", \"READY\")\n * @returns True if the task is in a loading state (not ERROR and not READY)\n *\n * @example\n * isTaskPolling(\"WORKING\") // Returns true\n * isTaskPolling(\"PREPARING_REPOSITORY\") // Returns true\n * isTaskPolling(\"READY\") // Returns false\n * isTaskPolling(\"ERROR\") // Returns false\n * isTaskPolling(null) // Returns false\n * isTaskPolling(undefined) // Returns false\n */\nexport const isTaskPolling = (taskStatus: string | null | undefined): boolean =>\n !!taskStatus && taskStatus !== \"ERROR\" && taskStatus !== \"READY\";\n\n/**\n * Get the appropriate color based on agent status\n * @param options Configuration object for status color calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.isStartingStatus Whether the agent is in a starting state (LOADING or INIT)\n * @param options.isStopStatus Whether the conversation status is STOPPED\n * @param options.curAgentState The current agent state\n * @returns The hex color code for the status\n *\n * @example\n * getStatusColor({\n * isPausing: false,\n * isTask: false,\n * taskStatus: undefined,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"var(--oh-status-success)\"\n */\nexport const getStatusColor = (options: {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n}): string => {\n const {\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n } = options;\n\n // Show pausing status\n if (isPausing) {\n return \"#FFD600\";\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return OH_STATUS_ERROR_COLOR;\n }\n return \"#FFD600\";\n }\n\n if (isStartingStatus) {\n return \"#FFD600\";\n }\n if (isStopStatus) {\n return \"#ffffff\";\n }\n if (curAgentState === AgentState.ERROR) {\n return OH_STATUS_ERROR_COLOR;\n }\n return OH_STATUS_SUCCESS_COLOR;\n};\n\ninterface GetStatusTextArgs {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: AppConversationStartTaskStatus | null;\n taskDetail?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n errorMessage?: string | null;\n t: (t: string) => string;\n}\n\n/**\n * Get the server status text based on agent and task state\n *\n * @param options Configuration object for status text calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.taskDetail Optional task-specific detail text\n * @param options.isStartingStatus Whether the conversation is in STARTING state\n * @param options.isStopStatus Whether the conversation is STOPPED\n * @param options.curAgentState The current agent state\n * @param options.errorMessage Optional agent error message\n * @returns Localized human-readable status text\n *\n * @example\n * getStatusText({\n * isPausing: false,\n * isTask: true,\n * taskStatus: \"STARTING_CONVERSATION\",\n * taskDetail: null,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"Starting conversation\"\n */\nexport function getStatusText({\n isPausing = false,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n}: GetStatusTextArgs): string {\n // Show pausing status\n if (isPausing) {\n return t(I18nKey.COMMON$STOPPING);\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return taskDetail || t(I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION);\n }\n\n if (taskStatus === \"READY\") {\n return t(I18nKey.CONVERSATION$READY);\n }\n\n return taskDetail || t(getTaskStatusI18nKey(taskStatus));\n }\n\n if (isStartingStatus) {\n return t(I18nKey.COMMON$STARTING);\n }\n\n if (isStopStatus) {\n return t(I18nKey.COMMON$SERVER_STOPPED);\n }\n\n if (curAgentState === AgentState.ERROR) {\n return errorMessage || t(I18nKey.COMMON$ERROR);\n }\n\n return t(I18nKey.COMMON$RUNNING);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.cjs","names":[],"sources":["../../src/utils/utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Provider } from \"#/types/settings\";\nimport { SuggestedTaskGroup } from \"#/utils/types\";\nimport { ConversationStatus } from \"#/types/conversation-status\";\nimport { GitRepository } from \"#/types/git\";\nimport { sanitizeQuery } from \"#/utils/sanitize-query\";\nimport { PRODUCT_URL } from \"#/utils/constants\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { getTaskStatusI18nKey } from \"#/utils/status\";\nimport type { AppConversationStartTaskStatus } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n OH_STATUS_ERROR_COLOR,\n OH_STATUS_SUCCESS_COLOR,\n} from \"#/constants/status-colors\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Trigger a download for a provided Blob with the given filename\n */\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(url);\n};\n\n/**\n * Get the numeric height value from an element's style property\n * @param el The HTML element to get the height from\n * @param fallback The fallback value to return if style height is invalid\n * @returns The numeric height value in pixels, or the fallback value\n *\n * @example\n * getStyleHeightPx(element, 20) // Returns 20 if element.style.height is \"auto\" or invalid\n * getStyleHeightPx(element, 20) // Returns 100 if element.style.height is \"100px\"\n */\nexport const getStyleHeightPx = (el: HTMLElement, fallback: number): number => {\n const elementHeight = parseFloat(el.style.height || \"\");\n return Number.isFinite(elementHeight) ? elementHeight : fallback;\n};\n\n/**\n * Set the height style property of an element to a specific pixel value\n * @param el The HTML element to set the height for\n * @param height The height value in pixels to set\n *\n * @example\n * setStyleHeightPx(element, 100) // Sets element.style.height to \"100px\"\n * setStyleHeightPx(textarea, 200) // Sets textarea.style.height to \"200px\"\n */\nexport const setStyleHeightPx = (el: HTMLElement, height: number): void => {\n el.style.setProperty(\"height\", `${height}px`);\n};\n\n/**\n * Detect a phone/tablet user agent (Android, iPhone, iPad, …).\n * Unlike isMobileDevice, this ignores touch capability, so a desktop OS with\n * a touchscreen (e.g. a Windows 2-in-1) is NOT matched. Use this when the\n * decision depends on having a physical keyboard rather than on touch input.\n */\nexport const isMobileUserAgent = (): boolean =>\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent,\n );\n\n/**\n * Detect if the user is on a mobile device.\n * Touch support alone is not sufficient — touchscreen laptops have touch\n * but use a mouse/trackpad as primary input. We check that the primary\n * pointing device is coarse (finger) to avoid false positives.\n */\nexport const isMobileDevice = (): boolean => {\n if (isMobileUserAgent()) return true;\n\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n if (!hasTouch) return false;\n\n // If matchMedia is available, check whether the primary pointer is fine\n // (mouse/trackpad). Touchscreen laptops report fine, real mobile devices don't.\n if (typeof window.matchMedia === \"function\") {\n return !window.matchMedia(\"(pointer: fine)\").matches;\n }\n\n // Fallback: touch present but no matchMedia — assume mobile\n return true;\n};\n\n/**\n * Checks if the current domain is the production domain\n * @returns True if the current domain matches the production URL\n */\nexport const isProductionDomain = (): boolean =>\n window.location.origin === PRODUCT_URL.PRODUCTION;\n\ninterface EventActionHistory {\n args?: {\n LLM_API_KEY?: string;\n [key: string]: unknown;\n };\n extras?: {\n open_page_urls: string[];\n active_page_index: number;\n dom_object: Record<string, unknown>;\n axtree_object: Record<string, unknown>;\n extra_element_properties: Record<string, unknown>;\n last_browser_action: string;\n last_browser_action_error: unknown;\n focused_element_bid: string;\n };\n [key: string]: unknown;\n}\n\nexport const removeUnwantedKeys = (\n data: EventActionHistory[],\n): EventActionHistory[] => {\n const UNDESIRED_KEYS = [\n \"open_page_urls\",\n \"active_page_index\",\n \"dom_object\",\n \"axtree_object\",\n \"extra_element_properties\",\n \"last_browser_action\",\n \"last_browser_action_error\",\n \"focused_element_bid\",\n ];\n\n return data\n .filter((item) => {\n // Skip items that have a status key\n if (\"status\" in item) {\n return false;\n }\n return true;\n })\n .map((item) => {\n // Create a shallow copy of item\n const newItem = { ...item };\n\n // Check if extras exists and delete it from a new extras object\n if (newItem.extras) {\n const newExtras = { ...newItem.extras };\n UNDESIRED_KEYS.forEach((key) => {\n delete newExtras[key as keyof typeof newExtras];\n });\n newItem.extras = newExtras;\n }\n\n return newItem;\n });\n};\n\n/**\n * Get file extension from file name in uppercase format\n * @param fileName The file name to extract extension from\n * @returns The file extension in uppercase, or \"FILE\" if no extension found\n *\n * @example\n * getFileExtension(\"document.pdf\") // \"PDF\"\n * getFileExtension(\"image.jpeg\") // \"JPEG\"\n * getFileExtension(\"noextension\") // \"FILE\"\n */\nexport const getFileExtension = (fileName: string): string => {\n const extension = fileName.split(\".\").pop()?.toUpperCase();\n return extension || \"FILE\";\n};\n\n/**\n * Whether to use the installation-scoped repo flow\n * (`/api/v1/git/installations/search` → `/api/v1/git/repositories/search?installation_id=…`)\n * for the given provider/backend combo.\n *\n * Mirrors OpenHands' cloud frontend (parameterized by `app_mode`):\n * - bitbucket / bitbucket_data_center → always installation-based\n * - github → installation-based ONLY when the active backend is cloud\n * - gitlab / azure_devops / forgejo → direct (search) flow\n *\n * `appMode` accepts the active backend `kind` (\"local\" | \"cloud\") so call\n * sites can hand it through directly.\n */\nexport const shouldUseInstallationRepos = (\n provider: Provider | null | undefined,\n appMode?: \"local\" | \"cloud\",\n) => {\n if (!provider) return false;\n\n switch (provider) {\n case \"bitbucket\":\n case \"bitbucket_data_center\":\n return true;\n case \"github\":\n return appMode === \"cloud\";\n default:\n return false;\n }\n};\n\nexport const getGitProviderBaseUrl = (\n gitProvider: Provider,\n host?: string | null,\n): string => {\n // If custom host provided, use it (with https:// prefix if needed)\n if (host && host.trim() !== \"\") {\n return host.startsWith(\"http\") ? host : `https://${host}`;\n }\n\n // Fall back to defaults\n switch (gitProvider) {\n case \"github\":\n return \"https://github.com\";\n case \"gitlab\":\n return \"https://gitlab.com\";\n case \"bitbucket\":\n return \"https://bitbucket.org\";\n case \"azure_devops\":\n return \"https://dev.azure.com\";\n case \"forgejo\":\n // Default UI links to Codeberg unless a custom host is available in settings\n // Note: UI link builders don't currently receive host; consider plumbing settings if needed\n return \"https://codeberg.org\";\n default:\n return \"\";\n }\n};\n\n/**\n * Get the name of the git provider\n * @param gitProvider The git provider\n * @returns The name of the git provider\n */\nexport const getProviderName = (gitProvider: Provider) => {\n if (gitProvider === \"gitlab\") return \"GitLab\";\n if (gitProvider === \"bitbucket\") return \"Bitbucket\";\n if (gitProvider === \"bitbucket_data_center\") return \"Bitbucket Data Center\";\n if (gitProvider === \"azure_devops\") return \"Azure DevOps\";\n if (gitProvider === \"forgejo\") return \"Forgejo\";\n return \"GitHub\";\n};\n\n/**\n * Get the name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The name of the PR\n */\nexport const getPR = (isGitLab: boolean) =>\n isGitLab ? \"merge request\" : \"pull request\";\n\n/**\n * Get the short name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The short name of the PR\n */\nexport const getPRShort = (isGitLab: boolean) => (isGitLab ? \"MR\" : \"PR\");\n\n/**\n * Construct the pull request (merge request) URL for different providers\n * @param prNumber The pull request number\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The pull request URL\n *\n * @example\n * constructPullRequestUrl(123, \"github\", \"owner/repo\") // \"https://github.com/owner/repo/pull/123\"\n * constructPullRequestUrl(456, \"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo/-/merge_requests/456\"\n * constructPullRequestUrl(789, \"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo/pull-requests/789\"\n * constructPullRequestUrl(789, \"bitbucket\", \"PROJECT/repo\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/pull-requests/789\"\n */\nexport const constructPullRequestUrl = (\n prNumber: number,\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/merge_requests/${prNumber}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/pull-requests/${prNumber}`;\n case \"bitbucket_data_center\": {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}/pull-requests/${prNumber}`;\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}/pullrequest/${prNumber}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n/**\n * Construct the repository URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The repository URL\n *\n * @example\n * constructRepositoryUrl(\"github\", \"owner/repo\") // \"https://github.com/owner/repo\"\n * constructRepositoryUrl(\"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo\"\n * constructRepositoryUrl(\"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo\"\n */\nexport const constructRepositoryUrl = (\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n if (provider === \"bitbucket_data_center\") {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}`;\n }\n return `${baseUrl}/${repositoryName}`;\n};\n\n/**\n * Percent-encode a branch name for use as a URL path component. Each\n * `/`-separated segment is encoded on its own so nested branch names such as\n * `release/1.0` keep their slashes while `#`, `%` and `&` are escaped.\n */\nconst encodeBranchPath = (branchName: string): string =>\n branchName.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n/**\n * Construct the branch URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @param branchName The branch name\n * @param host Optional custom host for self-hosted instances\n * @returns The branch URL\n *\n * @example\n * constructBranchUrl(\"github\", \"owner/repo\", \"main\") // \"https://github.com/owner/repo/tree/main\"\n * constructBranchUrl(\"gitlab\", \"owner/repo\", \"develop\") // \"https://gitlab.com/owner/repo/-/tree/develop\"\n * constructBranchUrl(\"bitbucket\", \"owner/repo\", \"feature\") // \"https://bitbucket.org/owner/repo/src/feature\"\n * constructBranchUrl(\"bitbucket\", \"PROJECT/repo\", \"feature\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/browse?at=refs/heads/feature\"\n */\nexport const constructBranchUrl = (\n provider: Provider,\n repositoryName: string,\n branchName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/tree/${encodeBranchPath(branchName)}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/src/branch/${encodeBranchPath(branchName)}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/tree/${encodeBranchPath(branchName)}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/src/${encodeBranchPath(branchName)}`;\n case \"bitbucket_data_center\": {\n // Bitbucket Server format: /projects/{PROJECT}/repos/{repo}/browse?at=refs/heads/{branch}\n const parts = repositoryName.split(\"/\");\n if (parts.length >= 2) {\n const [project, repo] = parts;\n // The branch is one query value here, so encode it whole: an\n // unencoded `&` would start a new query parameter.\n return `${baseUrl}/projects/${project}/repos/${repo}/browse?at=refs/heads/${encodeURIComponent(branchName)}`;\n }\n return \"\";\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n // The branch is one query value here, so encode it whole: an\n // unencoded `&` would start a new query parameter.\n return `${baseUrl}/${org}/${project}/_git/${repo}?version=GB${encodeURIComponent(branchName)}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n// Git Action Prompts\n\nconst GIT_COMMIT_PROMPT =\n \"Please review the current changes and create a git commit with a concise, descriptive message.\";\n\nexport const getGitCommitPrompt = (): string => GIT_COMMIT_PROMPT;\n\n/**\n * Generate a git pull prompt\n * @returns The git pull prompt\n */\nexport const getGitPullPrompt = (): string =>\n \"Please pull the latest code from the repository.\";\n\n/**\n * Generate a git push prompt\n * @param gitProvider The git provider\n * @returns The git push prompt\n */\nexport const getGitPushPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the changes to a remote branch on ${providerName}, but do NOT create a ${pr}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`;\n};\n\n/**\n * Generate a create pull request prompt\n * @param gitProvider The git provider\n * @returns The create PR prompt\n */\nexport const getCreatePRPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n const prShort = getPRShort(gitProvider === \"gitlab\");\n\n return `Please push the changes to ${providerName} and open a ${pr}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.`;\n};\n\n/**\n * Generate a push to existing PR prompt\n * @param gitProvider The git provider\n * @returns The push to PR prompt\n */\nexport const getPushToPRPrompt = (gitProvider: Provider): string => {\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the latest changes to the existing ${pr}.`;\n};\n\n/**\n * Generate a create new branch prompt\n * @returns The create new branch prompt\n */\nexport const getCreateNewBranchPrompt = (): string =>\n \"Please create a new branch with a descriptive name related to the work you plan to do.\";\n\n// Helper functions\nexport function getTotalTaskCount(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n): number {\n if (!suggestedTasks) return 0;\n return suggestedTasks.flatMap((group) => group.tasks).length;\n}\n\nexport function getLimitedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[],\n maxTasks: number,\n): SuggestedTaskGroup[] {\n const limitedGroups: SuggestedTaskGroup[] = [];\n let taskCount = 0;\n\n for (const group of suggestedTasks) {\n if (taskCount >= maxTasks) break;\n\n const remainingTasksNeeded = maxTasks - taskCount;\n const tasksToShow = group.tasks.slice(0, remainingTasksNeeded);\n\n if (tasksToShow.length > 0) {\n limitedGroups.push({\n ...group,\n tasks: tasksToShow,\n });\n taskCount += tasksToShow.length;\n }\n }\n\n return limitedGroups;\n}\n\nexport function getDisplayedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n isExpanded: boolean,\n): SuggestedTaskGroup[] {\n if (!suggestedTasks || suggestedTasks.length === 0) {\n return [];\n }\n\n if (isExpanded) {\n return suggestedTasks;\n }\n\n return getLimitedTaskGroups(suggestedTasks, 3);\n}\n\n/**\n * Get the label for a conversation status\n * @param status The conversation status\n * @returns The localized label for the status\n */\nexport const getConversationStatusLabel = (\n status: ConversationStatus,\n): string => {\n switch (status) {\n case \"STOPPED\":\n return \"COMMON$STOPPED\";\n case \"RUNNING\":\n return \"COMMON$RUNNING\";\n case \"STARTING\":\n return \"COMMON$STARTING\";\n case \"ERROR\":\n return \"COMMON$ERROR\";\n case \"ARCHIVED\":\n return \"COMMON$ARCHIVED\"; // Use STOPPED for archived conversations\n default:\n return \"COMMON$UNKNOWN\";\n }\n};\n\n// Task Tracking Utility Functions\n\n/**\n * Get the status icon for a task status\n * @param status The task status\n * @returns The emoji icon for the status\n */\nexport const getStatusIcon = (status: string) => {\n switch (status) {\n case \"todo\":\n return \"⏳\";\n case \"in_progress\":\n return \"🔄\";\n case \"done\":\n return \"✅\";\n default:\n return \"❓\";\n }\n};\n\n/**\n * Get the CSS class names for a task status badge\n * @param status The task status\n * @returns The CSS class names for styling the status badge\n */\nexport const getStatusClassName = (status: string) => {\n if (status === \"done\") {\n return \"bg-green-800 text-green-200\";\n }\n if (status === \"in_progress\") {\n return \"bg-yellow-800 text-yellow-200\";\n }\n return \"bg-tertiary text-[var(--oh-text-tertiary)]\";\n};\n\n/**\n * Helper function to apply client-side filtering based on search query\n * @param repo The Git repository to check\n * @param searchQuery The search query string\n * @returns True if the repository should be included based on the search query\n */\nexport const shouldIncludeRepository = (\n repo: GitRepository,\n searchQuery: string,\n): boolean => {\n if (!searchQuery.trim()) {\n return true;\n }\n\n const sanitizedQuery = sanitizeQuery(searchQuery);\n const sanitizedRepoName = sanitizeQuery(repo.full_name);\n return sanitizedRepoName.includes(sanitizedQuery);\n};\n\n/**\n * Get the OpenHands query string based on the provider\n * @param provider The git provider\n * @returns The query string for searching OpenHands repositories\n */\nexport const getOpenHandsQuery = (provider: Provider | null): string => {\n const providerRepositorySuffix: Record<string, string> = {\n gitlab: \"openhands-config\",\n azure_devops: \"openhands-config\",\n default: \".openhands\",\n } as const;\n\n return provider && provider in providerRepositorySuffix\n ? providerRepositorySuffix[provider]\n : providerRepositorySuffix.default;\n};\n\n/**\n * Check if a repository has the OpenHands suffix based on the provider\n * @param repo The Git repository to check\n * @param provider The git provider\n * @returns True if the repository has the OpenHands suffix\n */\nexport const hasOpenHandsSuffix = (\n repo: GitRepository,\n provider: Provider | null,\n): boolean => repo.full_name.endsWith(`/${getOpenHandsQuery(provider)}`);\n\n/**\n * Build headers for V1 API requests that require session authentication\n * @param sessionApiKey Session API key for authentication\n * @returns Headers object with X-Session-API-Key if provided\n */\nexport const buildSessionHeaders = (\n sessionApiKey?: string | null,\n): Record<string, string> => {\n const headers: Record<string, string> = {};\n if (sessionApiKey) {\n headers[\"X-Session-API-Key\"] = sessionApiKey;\n }\n return headers;\n};\n\n/**\n * Check if a task is currently being polled (loading state)\n * @param taskStatus The task status string (e.g., \"WORKING\", \"ERROR\", \"READY\")\n * @returns True if the task is in a loading state (not ERROR and not READY)\n *\n * @example\n * isTaskPolling(\"WORKING\") // Returns true\n * isTaskPolling(\"PREPARING_REPOSITORY\") // Returns true\n * isTaskPolling(\"READY\") // Returns false\n * isTaskPolling(\"ERROR\") // Returns false\n * isTaskPolling(null) // Returns false\n * isTaskPolling(undefined) // Returns false\n */\nexport const isTaskPolling = (taskStatus: string | null | undefined): boolean =>\n !!taskStatus && taskStatus !== \"ERROR\" && taskStatus !== \"READY\";\n\n/**\n * Get the appropriate color based on agent status\n * @param options Configuration object for status color calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.isStartingStatus Whether the agent is in a starting state (LOADING or INIT)\n * @param options.isStopStatus Whether the conversation status is STOPPED\n * @param options.curAgentState The current agent state\n * @returns The hex color code for the status\n *\n * @example\n * getStatusColor({\n * isPausing: false,\n * isTask: false,\n * taskStatus: undefined,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"var(--oh-status-success)\"\n */\nexport const getStatusColor = (options: {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n}): string => {\n const {\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n } = options;\n\n // Show pausing status\n if (isPausing) {\n return \"#FFD600\";\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return OH_STATUS_ERROR_COLOR;\n }\n return \"#FFD600\";\n }\n\n if (isStartingStatus) {\n return \"#FFD600\";\n }\n if (isStopStatus) {\n return \"#ffffff\";\n }\n if (curAgentState === AgentState.ERROR) {\n return OH_STATUS_ERROR_COLOR;\n }\n return OH_STATUS_SUCCESS_COLOR;\n};\n\ninterface GetStatusTextArgs {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: AppConversationStartTaskStatus | null;\n taskDetail?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n errorMessage?: string | null;\n t: (t: string) => string;\n}\n\n/**\n * Get the server status text based on agent and task state\n *\n * @param options Configuration object for status text calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.taskDetail Optional task-specific detail text\n * @param options.isStartingStatus Whether the conversation is in STARTING state\n * @param options.isStopStatus Whether the conversation is STOPPED\n * @param options.curAgentState The current agent state\n * @param options.errorMessage Optional agent error message\n * @returns Localized human-readable status text\n *\n * @example\n * getStatusText({\n * isPausing: false,\n * isTask: true,\n * taskStatus: \"STARTING_CONVERSATION\",\n * taskDetail: null,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"Starting conversation\"\n */\nexport function getStatusText({\n isPausing = false,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n}: GetStatusTextArgs): string {\n // Show pausing status\n if (isPausing) {\n return t(I18nKey.COMMON$STOPPING);\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return taskDetail || t(I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION);\n }\n\n if (taskStatus === \"READY\") {\n return t(I18nKey.CONVERSATION$READY);\n }\n\n return taskDetail || t(getTaskStatusI18nKey(taskStatus));\n }\n\n if (isStartingStatus) {\n return t(I18nKey.COMMON$STARTING);\n }\n\n if (isStopStatus) {\n return t(I18nKey.COMMON$SERVER_STOPPED);\n }\n\n if (curAgentState === AgentState.ERROR) {\n return errorMessage || t(I18nKey.COMMON$ERROR);\n }\n\n return t(I18nKey.COMMON$RUNNING);\n}\n"],"mappings":"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"}
@@ -62,18 +62,18 @@ var c = (e, t) => {
62
62
  return `${r}/projects/${e}/repos/${n}`;
63
63
  }
64
64
  return `${r}/${t}`;
65
- }, x = (e, t, n, r) => {
65
+ }, x = (e) => e.split("/").map(encodeURIComponent).join("/"), S = (e, t, n, r) => {
66
66
  let i = h(e, r);
67
67
  switch (e) {
68
- case "github": return `${i}/${t}/tree/${n}`;
69
- case "forgejo": return `${i}/${t}/src/branch/${n}`;
70
- case "gitlab": return `${i}/${t}/-/tree/${n}`;
71
- case "bitbucket": return `${i}/${t}/src/${n}`;
68
+ case "github": return `${i}/${t}/tree/${x(n)}`;
69
+ case "forgejo": return `${i}/${t}/src/branch/${x(n)}`;
70
+ case "gitlab": return `${i}/${t}/-/tree/${x(n)}`;
71
+ case "bitbucket": return `${i}/${t}/src/${x(n)}`;
72
72
  case "bitbucket_data_center": {
73
73
  let e = t.split("/");
74
74
  if (e.length >= 2) {
75
75
  let [t, r] = e;
76
- return `${i}/projects/${t}/repos/${r}/browse?at=refs/heads/${n}`;
76
+ return `${i}/projects/${t}/repos/${r}/browse?at=refs/heads/${encodeURIComponent(n)}`;
77
77
  }
78
78
  return "";
79
79
  }
@@ -81,23 +81,23 @@ var c = (e, t) => {
81
81
  let e = t.split("/");
82
82
  if (e.length === 3) {
83
83
  let [t, r, a] = e;
84
- return `${i}/${t}/${r}/_git/${a}?version=GB${n}`;
84
+ return `${i}/${t}/${r}/_git/${a}?version=GB${encodeURIComponent(n)}`;
85
85
  }
86
86
  return "";
87
87
  }
88
88
  default: return "";
89
89
  }
90
- }, S = "Please review the current changes and create a git commit with a concise, descriptive message.", C = () => S, w = () => "Please pull the latest code from the repository.", T = (e) => `Please push the changes to a remote branch on ${g(e)}, but do NOT create a ${_(e === "gitlab")}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`, E = (e) => {
90
+ }, C = "Please review the current changes and create a git commit with a concise, descriptive message.", w = () => C, T = () => "Please pull the latest code from the repository.", E = (e) => `Please push the changes to a remote branch on ${g(e)}, but do NOT create a ${_(e === "gitlab")}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`, D = (e) => {
91
91
  let t = g(e), n = _(e === "gitlab");
92
92
  return `Please push the changes to ${t} and open a ${n}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${n} template exists in the repository, please follow it when creating the ${v(e === "gitlab")} description.`;
93
- }, D = () => "Please create a new branch with a descriptive name related to the work you plan to do.", O = (e) => !!e && e !== "ERROR" && e !== "READY", k = (e) => {
93
+ }, O = () => "Please create a new branch with a descriptive name related to the work you plan to do.", k = (e) => !!e && e !== "ERROR" && e !== "READY", A = (e) => {
94
94
  let { isPausing: t, isTask: n, taskStatus: i, isStartingStatus: s, isStopStatus: c, curAgentState: l } = e;
95
95
  return t ? "#FFD600" : n && i ? i === "ERROR" ? a : "#FFD600" : s ? "#FFD600" : c ? "#ffffff" : l === r.ERROR ? a : o;
96
96
  };
97
- function A({ isPausing: t = !1, isTask: n, taskStatus: a, taskDetail: o, isStartingStatus: s, isStopStatus: c, curAgentState: l, errorMessage: u, t: d }) {
97
+ function j({ isPausing: t = !1, isTask: n, taskStatus: a, taskDetail: o, isStartingStatus: s, isStopStatus: c, curAgentState: l, errorMessage: u, t: d }) {
98
98
  return t ? d(e.COMMON$STOPPING) : n && a ? a === "ERROR" ? o || d(e.CONVERSATION$ERROR_STARTING_CONVERSATION) : a === "READY" ? d(e.CONVERSATION$READY) : o || d(i(a)) : s ? d(e.COMMON$STARTING) : c ? d(e.COMMON$SERVER_STOPPED) : l === r.ERROR ? u || d(e.COMMON$ERROR) : d(e.COMMON$RUNNING);
99
99
  }
100
100
  //#endregion
101
- export { s as cn, x as constructBranchUrl, y as constructPullRequestUrl, b as constructRepositoryUrl, c as downloadBlob, D as getCreateNewBranchPrompt, E as getCreatePRPrompt, p as getFileExtension, C as getGitCommitPrompt, h as getGitProviderBaseUrl, w as getGitPullPrompt, T as getGitPushPrompt, g as getProviderName, k as getStatusColor, A as getStatusText, l as getStyleHeightPx, f as isMobileDevice, d as isMobileUserAgent, O as isTaskPolling, u as setStyleHeightPx, m as shouldUseInstallationRepos };
101
+ export { s as cn, S as constructBranchUrl, y as constructPullRequestUrl, b as constructRepositoryUrl, c as downloadBlob, O as getCreateNewBranchPrompt, D as getCreatePRPrompt, p as getFileExtension, w as getGitCommitPrompt, h as getGitProviderBaseUrl, T as getGitPullPrompt, E as getGitPushPrompt, g as getProviderName, A as getStatusColor, j as getStatusText, l as getStyleHeightPx, f as isMobileDevice, d as isMobileUserAgent, k as isTaskPolling, u as setStyleHeightPx, m as shouldUseInstallationRepos };
102
102
 
103
103
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils/utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Provider } from \"#/types/settings\";\nimport { SuggestedTaskGroup } from \"#/utils/types\";\nimport { ConversationStatus } from \"#/types/conversation-status\";\nimport { GitRepository } from \"#/types/git\";\nimport { sanitizeQuery } from \"#/utils/sanitize-query\";\nimport { PRODUCT_URL } from \"#/utils/constants\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { getTaskStatusI18nKey } from \"#/utils/status\";\nimport type { AppConversationStartTaskStatus } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n OH_STATUS_ERROR_COLOR,\n OH_STATUS_SUCCESS_COLOR,\n} from \"#/constants/status-colors\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Trigger a download for a provided Blob with the given filename\n */\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(url);\n};\n\n/**\n * Get the numeric height value from an element's style property\n * @param el The HTML element to get the height from\n * @param fallback The fallback value to return if style height is invalid\n * @returns The numeric height value in pixels, or the fallback value\n *\n * @example\n * getStyleHeightPx(element, 20) // Returns 20 if element.style.height is \"auto\" or invalid\n * getStyleHeightPx(element, 20) // Returns 100 if element.style.height is \"100px\"\n */\nexport const getStyleHeightPx = (el: HTMLElement, fallback: number): number => {\n const elementHeight = parseFloat(el.style.height || \"\");\n return Number.isFinite(elementHeight) ? elementHeight : fallback;\n};\n\n/**\n * Set the height style property of an element to a specific pixel value\n * @param el The HTML element to set the height for\n * @param height The height value in pixels to set\n *\n * @example\n * setStyleHeightPx(element, 100) // Sets element.style.height to \"100px\"\n * setStyleHeightPx(textarea, 200) // Sets textarea.style.height to \"200px\"\n */\nexport const setStyleHeightPx = (el: HTMLElement, height: number): void => {\n el.style.setProperty(\"height\", `${height}px`);\n};\n\n/**\n * Detect a phone/tablet user agent (Android, iPhone, iPad, …).\n * Unlike isMobileDevice, this ignores touch capability, so a desktop OS with\n * a touchscreen (e.g. a Windows 2-in-1) is NOT matched. Use this when the\n * decision depends on having a physical keyboard rather than on touch input.\n */\nexport const isMobileUserAgent = (): boolean =>\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent,\n );\n\n/**\n * Detect if the user is on a mobile device.\n * Touch support alone is not sufficient — touchscreen laptops have touch\n * but use a mouse/trackpad as primary input. We check that the primary\n * pointing device is coarse (finger) to avoid false positives.\n */\nexport const isMobileDevice = (): boolean => {\n if (isMobileUserAgent()) return true;\n\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n if (!hasTouch) return false;\n\n // If matchMedia is available, check whether the primary pointer is fine\n // (mouse/trackpad). Touchscreen laptops report fine, real mobile devices don't.\n if (typeof window.matchMedia === \"function\") {\n return !window.matchMedia(\"(pointer: fine)\").matches;\n }\n\n // Fallback: touch present but no matchMedia — assume mobile\n return true;\n};\n\n/**\n * Checks if the current domain is the production domain\n * @returns True if the current domain matches the production URL\n */\nexport const isProductionDomain = (): boolean =>\n window.location.origin === PRODUCT_URL.PRODUCTION;\n\ninterface EventActionHistory {\n args?: {\n LLM_API_KEY?: string;\n [key: string]: unknown;\n };\n extras?: {\n open_page_urls: string[];\n active_page_index: number;\n dom_object: Record<string, unknown>;\n axtree_object: Record<string, unknown>;\n extra_element_properties: Record<string, unknown>;\n last_browser_action: string;\n last_browser_action_error: unknown;\n focused_element_bid: string;\n };\n [key: string]: unknown;\n}\n\nexport const removeUnwantedKeys = (\n data: EventActionHistory[],\n): EventActionHistory[] => {\n const UNDESIRED_KEYS = [\n \"open_page_urls\",\n \"active_page_index\",\n \"dom_object\",\n \"axtree_object\",\n \"extra_element_properties\",\n \"last_browser_action\",\n \"last_browser_action_error\",\n \"focused_element_bid\",\n ];\n\n return data\n .filter((item) => {\n // Skip items that have a status key\n if (\"status\" in item) {\n return false;\n }\n return true;\n })\n .map((item) => {\n // Create a shallow copy of item\n const newItem = { ...item };\n\n // Check if extras exists and delete it from a new extras object\n if (newItem.extras) {\n const newExtras = { ...newItem.extras };\n UNDESIRED_KEYS.forEach((key) => {\n delete newExtras[key as keyof typeof newExtras];\n });\n newItem.extras = newExtras;\n }\n\n return newItem;\n });\n};\n\n/**\n * Get file extension from file name in uppercase format\n * @param fileName The file name to extract extension from\n * @returns The file extension in uppercase, or \"FILE\" if no extension found\n *\n * @example\n * getFileExtension(\"document.pdf\") // \"PDF\"\n * getFileExtension(\"image.jpeg\") // \"JPEG\"\n * getFileExtension(\"noextension\") // \"FILE\"\n */\nexport const getFileExtension = (fileName: string): string => {\n const extension = fileName.split(\".\").pop()?.toUpperCase();\n return extension || \"FILE\";\n};\n\n/**\n * Whether to use the installation-scoped repo flow\n * (`/api/v1/git/installations/search` → `/api/v1/git/repositories/search?installation_id=…`)\n * for the given provider/backend combo.\n *\n * Mirrors OpenHands' cloud frontend (parameterized by `app_mode`):\n * - bitbucket / bitbucket_data_center → always installation-based\n * - github → installation-based ONLY when the active backend is cloud\n * - gitlab / azure_devops / forgejo → direct (search) flow\n *\n * `appMode` accepts the active backend `kind` (\"local\" | \"cloud\") so call\n * sites can hand it through directly.\n */\nexport const shouldUseInstallationRepos = (\n provider: Provider | null | undefined,\n appMode?: \"local\" | \"cloud\",\n) => {\n if (!provider) return false;\n\n switch (provider) {\n case \"bitbucket\":\n case \"bitbucket_data_center\":\n return true;\n case \"github\":\n return appMode === \"cloud\";\n default:\n return false;\n }\n};\n\nexport const getGitProviderBaseUrl = (\n gitProvider: Provider,\n host?: string | null,\n): string => {\n // If custom host provided, use it (with https:// prefix if needed)\n if (host && host.trim() !== \"\") {\n return host.startsWith(\"http\") ? host : `https://${host}`;\n }\n\n // Fall back to defaults\n switch (gitProvider) {\n case \"github\":\n return \"https://github.com\";\n case \"gitlab\":\n return \"https://gitlab.com\";\n case \"bitbucket\":\n return \"https://bitbucket.org\";\n case \"azure_devops\":\n return \"https://dev.azure.com\";\n case \"forgejo\":\n // Default UI links to Codeberg unless a custom host is available in settings\n // Note: UI link builders don't currently receive host; consider plumbing settings if needed\n return \"https://codeberg.org\";\n default:\n return \"\";\n }\n};\n\n/**\n * Get the name of the git provider\n * @param gitProvider The git provider\n * @returns The name of the git provider\n */\nexport const getProviderName = (gitProvider: Provider) => {\n if (gitProvider === \"gitlab\") return \"GitLab\";\n if (gitProvider === \"bitbucket\") return \"Bitbucket\";\n if (gitProvider === \"bitbucket_data_center\") return \"Bitbucket Data Center\";\n if (gitProvider === \"azure_devops\") return \"Azure DevOps\";\n if (gitProvider === \"forgejo\") return \"Forgejo\";\n return \"GitHub\";\n};\n\n/**\n * Get the name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The name of the PR\n */\nexport const getPR = (isGitLab: boolean) =>\n isGitLab ? \"merge request\" : \"pull request\";\n\n/**\n * Get the short name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The short name of the PR\n */\nexport const getPRShort = (isGitLab: boolean) => (isGitLab ? \"MR\" : \"PR\");\n\n/**\n * Construct the pull request (merge request) URL for different providers\n * @param prNumber The pull request number\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The pull request URL\n *\n * @example\n * constructPullRequestUrl(123, \"github\", \"owner/repo\") // \"https://github.com/owner/repo/pull/123\"\n * constructPullRequestUrl(456, \"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo/-/merge_requests/456\"\n * constructPullRequestUrl(789, \"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo/pull-requests/789\"\n * constructPullRequestUrl(789, \"bitbucket\", \"PROJECT/repo\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/pull-requests/789\"\n */\nexport const constructPullRequestUrl = (\n prNumber: number,\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/merge_requests/${prNumber}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/pull-requests/${prNumber}`;\n case \"bitbucket_data_center\": {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}/pull-requests/${prNumber}`;\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}/pullrequest/${prNumber}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n/**\n * Construct the repository URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The repository URL\n *\n * @example\n * constructRepositoryUrl(\"github\", \"owner/repo\") // \"https://github.com/owner/repo\"\n * constructRepositoryUrl(\"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo\"\n * constructRepositoryUrl(\"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo\"\n */\nexport const constructRepositoryUrl = (\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n if (provider === \"bitbucket_data_center\") {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}`;\n }\n return `${baseUrl}/${repositoryName}`;\n};\n\n/**\n * Construct the branch URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @param branchName The branch name\n * @param host Optional custom host for self-hosted instances\n * @returns The branch URL\n *\n * @example\n * constructBranchUrl(\"github\", \"owner/repo\", \"main\") // \"https://github.com/owner/repo/tree/main\"\n * constructBranchUrl(\"gitlab\", \"owner/repo\", \"develop\") // \"https://gitlab.com/owner/repo/-/tree/develop\"\n * constructBranchUrl(\"bitbucket\", \"owner/repo\", \"feature\") // \"https://bitbucket.org/owner/repo/src/feature\"\n * constructBranchUrl(\"bitbucket\", \"PROJECT/repo\", \"feature\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/browse?at=refs/heads/feature\"\n */\nexport const constructBranchUrl = (\n provider: Provider,\n repositoryName: string,\n branchName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/tree/${branchName}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/src/branch/${branchName}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/tree/${branchName}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/src/${branchName}`;\n case \"bitbucket_data_center\": {\n // Bitbucket Server format: /projects/{PROJECT}/repos/{repo}/browse?at=refs/heads/{branch}\n const parts = repositoryName.split(\"/\");\n if (parts.length >= 2) {\n const [project, repo] = parts;\n return `${baseUrl}/projects/${project}/repos/${repo}/browse?at=refs/heads/${branchName}`;\n }\n return \"\";\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}?version=GB${branchName}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n// Git Action Prompts\n\nconst GIT_COMMIT_PROMPT =\n \"Please review the current changes and create a git commit with a concise, descriptive message.\";\n\nexport const getGitCommitPrompt = (): string => GIT_COMMIT_PROMPT;\n\n/**\n * Generate a git pull prompt\n * @returns The git pull prompt\n */\nexport const getGitPullPrompt = (): string =>\n \"Please pull the latest code from the repository.\";\n\n/**\n * Generate a git push prompt\n * @param gitProvider The git provider\n * @returns The git push prompt\n */\nexport const getGitPushPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the changes to a remote branch on ${providerName}, but do NOT create a ${pr}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`;\n};\n\n/**\n * Generate a create pull request prompt\n * @param gitProvider The git provider\n * @returns The create PR prompt\n */\nexport const getCreatePRPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n const prShort = getPRShort(gitProvider === \"gitlab\");\n\n return `Please push the changes to ${providerName} and open a ${pr}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.`;\n};\n\n/**\n * Generate a push to existing PR prompt\n * @param gitProvider The git provider\n * @returns The push to PR prompt\n */\nexport const getPushToPRPrompt = (gitProvider: Provider): string => {\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the latest changes to the existing ${pr}.`;\n};\n\n/**\n * Generate a create new branch prompt\n * @returns The create new branch prompt\n */\nexport const getCreateNewBranchPrompt = (): string =>\n \"Please create a new branch with a descriptive name related to the work you plan to do.\";\n\n// Helper functions\nexport function getTotalTaskCount(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n): number {\n if (!suggestedTasks) return 0;\n return suggestedTasks.flatMap((group) => group.tasks).length;\n}\n\nexport function getLimitedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[],\n maxTasks: number,\n): SuggestedTaskGroup[] {\n const limitedGroups: SuggestedTaskGroup[] = [];\n let taskCount = 0;\n\n for (const group of suggestedTasks) {\n if (taskCount >= maxTasks) break;\n\n const remainingTasksNeeded = maxTasks - taskCount;\n const tasksToShow = group.tasks.slice(0, remainingTasksNeeded);\n\n if (tasksToShow.length > 0) {\n limitedGroups.push({\n ...group,\n tasks: tasksToShow,\n });\n taskCount += tasksToShow.length;\n }\n }\n\n return limitedGroups;\n}\n\nexport function getDisplayedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n isExpanded: boolean,\n): SuggestedTaskGroup[] {\n if (!suggestedTasks || suggestedTasks.length === 0) {\n return [];\n }\n\n if (isExpanded) {\n return suggestedTasks;\n }\n\n return getLimitedTaskGroups(suggestedTasks, 3);\n}\n\n/**\n * Get the label for a conversation status\n * @param status The conversation status\n * @returns The localized label for the status\n */\nexport const getConversationStatusLabel = (\n status: ConversationStatus,\n): string => {\n switch (status) {\n case \"STOPPED\":\n return \"COMMON$STOPPED\";\n case \"RUNNING\":\n return \"COMMON$RUNNING\";\n case \"STARTING\":\n return \"COMMON$STARTING\";\n case \"ERROR\":\n return \"COMMON$ERROR\";\n case \"ARCHIVED\":\n return \"COMMON$ARCHIVED\"; // Use STOPPED for archived conversations\n default:\n return \"COMMON$UNKNOWN\";\n }\n};\n\n// Task Tracking Utility Functions\n\n/**\n * Get the status icon for a task status\n * @param status The task status\n * @returns The emoji icon for the status\n */\nexport const getStatusIcon = (status: string) => {\n switch (status) {\n case \"todo\":\n return \"⏳\";\n case \"in_progress\":\n return \"🔄\";\n case \"done\":\n return \"✅\";\n default:\n return \"❓\";\n }\n};\n\n/**\n * Get the CSS class names for a task status badge\n * @param status The task status\n * @returns The CSS class names for styling the status badge\n */\nexport const getStatusClassName = (status: string) => {\n if (status === \"done\") {\n return \"bg-green-800 text-green-200\";\n }\n if (status === \"in_progress\") {\n return \"bg-yellow-800 text-yellow-200\";\n }\n return \"bg-tertiary text-[var(--oh-text-tertiary)]\";\n};\n\n/**\n * Helper function to apply client-side filtering based on search query\n * @param repo The Git repository to check\n * @param searchQuery The search query string\n * @returns True if the repository should be included based on the search query\n */\nexport const shouldIncludeRepository = (\n repo: GitRepository,\n searchQuery: string,\n): boolean => {\n if (!searchQuery.trim()) {\n return true;\n }\n\n const sanitizedQuery = sanitizeQuery(searchQuery);\n const sanitizedRepoName = sanitizeQuery(repo.full_name);\n return sanitizedRepoName.includes(sanitizedQuery);\n};\n\n/**\n * Get the OpenHands query string based on the provider\n * @param provider The git provider\n * @returns The query string for searching OpenHands repositories\n */\nexport const getOpenHandsQuery = (provider: Provider | null): string => {\n const providerRepositorySuffix: Record<string, string> = {\n gitlab: \"openhands-config\",\n azure_devops: \"openhands-config\",\n default: \".openhands\",\n } as const;\n\n return provider && provider in providerRepositorySuffix\n ? providerRepositorySuffix[provider]\n : providerRepositorySuffix.default;\n};\n\n/**\n * Check if a repository has the OpenHands suffix based on the provider\n * @param repo The Git repository to check\n * @param provider The git provider\n * @returns True if the repository has the OpenHands suffix\n */\nexport const hasOpenHandsSuffix = (\n repo: GitRepository,\n provider: Provider | null,\n): boolean => repo.full_name.endsWith(`/${getOpenHandsQuery(provider)}`);\n\n/**\n * Build headers for V1 API requests that require session authentication\n * @param sessionApiKey Session API key for authentication\n * @returns Headers object with X-Session-API-Key if provided\n */\nexport const buildSessionHeaders = (\n sessionApiKey?: string | null,\n): Record<string, string> => {\n const headers: Record<string, string> = {};\n if (sessionApiKey) {\n headers[\"X-Session-API-Key\"] = sessionApiKey;\n }\n return headers;\n};\n\n/**\n * Check if a task is currently being polled (loading state)\n * @param taskStatus The task status string (e.g., \"WORKING\", \"ERROR\", \"READY\")\n * @returns True if the task is in a loading state (not ERROR and not READY)\n *\n * @example\n * isTaskPolling(\"WORKING\") // Returns true\n * isTaskPolling(\"PREPARING_REPOSITORY\") // Returns true\n * isTaskPolling(\"READY\") // Returns false\n * isTaskPolling(\"ERROR\") // Returns false\n * isTaskPolling(null) // Returns false\n * isTaskPolling(undefined) // Returns false\n */\nexport const isTaskPolling = (taskStatus: string | null | undefined): boolean =>\n !!taskStatus && taskStatus !== \"ERROR\" && taskStatus !== \"READY\";\n\n/**\n * Get the appropriate color based on agent status\n * @param options Configuration object for status color calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.isStartingStatus Whether the agent is in a starting state (LOADING or INIT)\n * @param options.isStopStatus Whether the conversation status is STOPPED\n * @param options.curAgentState The current agent state\n * @returns The hex color code for the status\n *\n * @example\n * getStatusColor({\n * isPausing: false,\n * isTask: false,\n * taskStatus: undefined,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"var(--oh-status-success)\"\n */\nexport const getStatusColor = (options: {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n}): string => {\n const {\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n } = options;\n\n // Show pausing status\n if (isPausing) {\n return \"#FFD600\";\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return OH_STATUS_ERROR_COLOR;\n }\n return \"#FFD600\";\n }\n\n if (isStartingStatus) {\n return \"#FFD600\";\n }\n if (isStopStatus) {\n return \"#ffffff\";\n }\n if (curAgentState === AgentState.ERROR) {\n return OH_STATUS_ERROR_COLOR;\n }\n return OH_STATUS_SUCCESS_COLOR;\n};\n\ninterface GetStatusTextArgs {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: AppConversationStartTaskStatus | null;\n taskDetail?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n errorMessage?: string | null;\n t: (t: string) => string;\n}\n\n/**\n * Get the server status text based on agent and task state\n *\n * @param options Configuration object for status text calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.taskDetail Optional task-specific detail text\n * @param options.isStartingStatus Whether the conversation is in STARTING state\n * @param options.isStopStatus Whether the conversation is STOPPED\n * @param options.curAgentState The current agent state\n * @param options.errorMessage Optional agent error message\n * @returns Localized human-readable status text\n *\n * @example\n * getStatusText({\n * isPausing: false,\n * isTask: true,\n * taskStatus: \"STARTING_CONVERSATION\",\n * taskDetail: null,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"Starting conversation\"\n */\nexport function getStatusText({\n isPausing = false,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n}: GetStatusTextArgs): string {\n // Show pausing status\n if (isPausing) {\n return t(I18nKey.COMMON$STOPPING);\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return taskDetail || t(I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION);\n }\n\n if (taskStatus === \"READY\") {\n return t(I18nKey.CONVERSATION$READY);\n }\n\n return taskDetail || t(getTaskStatusI18nKey(taskStatus));\n }\n\n if (isStartingStatus) {\n return t(I18nKey.COMMON$STARTING);\n }\n\n if (isStopStatus) {\n return t(I18nKey.COMMON$SERVER_STOPPED);\n }\n\n if (curAgentState === AgentState.ERROR) {\n return errorMessage || t(I18nKey.COMMON$ERROR);\n }\n\n return t(I18nKey.COMMON$RUNNING);\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.js","names":[],"sources":["../../src/utils/utils.ts"],"sourcesContent":["import { type ClassValue, clsx } from \"clsx\";\nimport { twMerge } from \"tailwind-merge\";\nimport { Provider } from \"#/types/settings\";\nimport { SuggestedTaskGroup } from \"#/utils/types\";\nimport { ConversationStatus } from \"#/types/conversation-status\";\nimport { GitRepository } from \"#/types/git\";\nimport { sanitizeQuery } from \"#/utils/sanitize-query\";\nimport { PRODUCT_URL } from \"#/utils/constants\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { getTaskStatusI18nKey } from \"#/utils/status\";\nimport type { AppConversationStartTaskStatus } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n OH_STATUS_ERROR_COLOR,\n OH_STATUS_SUCCESS_COLOR,\n} from \"#/constants/status-colors\";\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs));\n}\n\n/**\n * Trigger a download for a provided Blob with the given filename\n */\nexport const downloadBlob = (blob: Blob, filename: string): void => {\n const url = window.URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n window.URL.revokeObjectURL(url);\n};\n\n/**\n * Get the numeric height value from an element's style property\n * @param el The HTML element to get the height from\n * @param fallback The fallback value to return if style height is invalid\n * @returns The numeric height value in pixels, or the fallback value\n *\n * @example\n * getStyleHeightPx(element, 20) // Returns 20 if element.style.height is \"auto\" or invalid\n * getStyleHeightPx(element, 20) // Returns 100 if element.style.height is \"100px\"\n */\nexport const getStyleHeightPx = (el: HTMLElement, fallback: number): number => {\n const elementHeight = parseFloat(el.style.height || \"\");\n return Number.isFinite(elementHeight) ? elementHeight : fallback;\n};\n\n/**\n * Set the height style property of an element to a specific pixel value\n * @param el The HTML element to set the height for\n * @param height The height value in pixels to set\n *\n * @example\n * setStyleHeightPx(element, 100) // Sets element.style.height to \"100px\"\n * setStyleHeightPx(textarea, 200) // Sets textarea.style.height to \"200px\"\n */\nexport const setStyleHeightPx = (el: HTMLElement, height: number): void => {\n el.style.setProperty(\"height\", `${height}px`);\n};\n\n/**\n * Detect a phone/tablet user agent (Android, iPhone, iPad, …).\n * Unlike isMobileDevice, this ignores touch capability, so a desktop OS with\n * a touchscreen (e.g. a Windows 2-in-1) is NOT matched. Use this when the\n * decision depends on having a physical keyboard rather than on touch input.\n */\nexport const isMobileUserAgent = (): boolean =>\n /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(\n navigator.userAgent,\n );\n\n/**\n * Detect if the user is on a mobile device.\n * Touch support alone is not sufficient — touchscreen laptops have touch\n * but use a mouse/trackpad as primary input. We check that the primary\n * pointing device is coarse (finger) to avoid false positives.\n */\nexport const isMobileDevice = (): boolean => {\n if (isMobileUserAgent()) return true;\n\n const hasTouch = \"ontouchstart\" in window || navigator.maxTouchPoints > 0;\n if (!hasTouch) return false;\n\n // If matchMedia is available, check whether the primary pointer is fine\n // (mouse/trackpad). Touchscreen laptops report fine, real mobile devices don't.\n if (typeof window.matchMedia === \"function\") {\n return !window.matchMedia(\"(pointer: fine)\").matches;\n }\n\n // Fallback: touch present but no matchMedia — assume mobile\n return true;\n};\n\n/**\n * Checks if the current domain is the production domain\n * @returns True if the current domain matches the production URL\n */\nexport const isProductionDomain = (): boolean =>\n window.location.origin === PRODUCT_URL.PRODUCTION;\n\ninterface EventActionHistory {\n args?: {\n LLM_API_KEY?: string;\n [key: string]: unknown;\n };\n extras?: {\n open_page_urls: string[];\n active_page_index: number;\n dom_object: Record<string, unknown>;\n axtree_object: Record<string, unknown>;\n extra_element_properties: Record<string, unknown>;\n last_browser_action: string;\n last_browser_action_error: unknown;\n focused_element_bid: string;\n };\n [key: string]: unknown;\n}\n\nexport const removeUnwantedKeys = (\n data: EventActionHistory[],\n): EventActionHistory[] => {\n const UNDESIRED_KEYS = [\n \"open_page_urls\",\n \"active_page_index\",\n \"dom_object\",\n \"axtree_object\",\n \"extra_element_properties\",\n \"last_browser_action\",\n \"last_browser_action_error\",\n \"focused_element_bid\",\n ];\n\n return data\n .filter((item) => {\n // Skip items that have a status key\n if (\"status\" in item) {\n return false;\n }\n return true;\n })\n .map((item) => {\n // Create a shallow copy of item\n const newItem = { ...item };\n\n // Check if extras exists and delete it from a new extras object\n if (newItem.extras) {\n const newExtras = { ...newItem.extras };\n UNDESIRED_KEYS.forEach((key) => {\n delete newExtras[key as keyof typeof newExtras];\n });\n newItem.extras = newExtras;\n }\n\n return newItem;\n });\n};\n\n/**\n * Get file extension from file name in uppercase format\n * @param fileName The file name to extract extension from\n * @returns The file extension in uppercase, or \"FILE\" if no extension found\n *\n * @example\n * getFileExtension(\"document.pdf\") // \"PDF\"\n * getFileExtension(\"image.jpeg\") // \"JPEG\"\n * getFileExtension(\"noextension\") // \"FILE\"\n */\nexport const getFileExtension = (fileName: string): string => {\n const extension = fileName.split(\".\").pop()?.toUpperCase();\n return extension || \"FILE\";\n};\n\n/**\n * Whether to use the installation-scoped repo flow\n * (`/api/v1/git/installations/search` → `/api/v1/git/repositories/search?installation_id=…`)\n * for the given provider/backend combo.\n *\n * Mirrors OpenHands' cloud frontend (parameterized by `app_mode`):\n * - bitbucket / bitbucket_data_center → always installation-based\n * - github → installation-based ONLY when the active backend is cloud\n * - gitlab / azure_devops / forgejo → direct (search) flow\n *\n * `appMode` accepts the active backend `kind` (\"local\" | \"cloud\") so call\n * sites can hand it through directly.\n */\nexport const shouldUseInstallationRepos = (\n provider: Provider | null | undefined,\n appMode?: \"local\" | \"cloud\",\n) => {\n if (!provider) return false;\n\n switch (provider) {\n case \"bitbucket\":\n case \"bitbucket_data_center\":\n return true;\n case \"github\":\n return appMode === \"cloud\";\n default:\n return false;\n }\n};\n\nexport const getGitProviderBaseUrl = (\n gitProvider: Provider,\n host?: string | null,\n): string => {\n // If custom host provided, use it (with https:// prefix if needed)\n if (host && host.trim() !== \"\") {\n return host.startsWith(\"http\") ? host : `https://${host}`;\n }\n\n // Fall back to defaults\n switch (gitProvider) {\n case \"github\":\n return \"https://github.com\";\n case \"gitlab\":\n return \"https://gitlab.com\";\n case \"bitbucket\":\n return \"https://bitbucket.org\";\n case \"azure_devops\":\n return \"https://dev.azure.com\";\n case \"forgejo\":\n // Default UI links to Codeberg unless a custom host is available in settings\n // Note: UI link builders don't currently receive host; consider plumbing settings if needed\n return \"https://codeberg.org\";\n default:\n return \"\";\n }\n};\n\n/**\n * Get the name of the git provider\n * @param gitProvider The git provider\n * @returns The name of the git provider\n */\nexport const getProviderName = (gitProvider: Provider) => {\n if (gitProvider === \"gitlab\") return \"GitLab\";\n if (gitProvider === \"bitbucket\") return \"Bitbucket\";\n if (gitProvider === \"bitbucket_data_center\") return \"Bitbucket Data Center\";\n if (gitProvider === \"azure_devops\") return \"Azure DevOps\";\n if (gitProvider === \"forgejo\") return \"Forgejo\";\n return \"GitHub\";\n};\n\n/**\n * Get the name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The name of the PR\n */\nexport const getPR = (isGitLab: boolean) =>\n isGitLab ? \"merge request\" : \"pull request\";\n\n/**\n * Get the short name of the PR\n * @param isGitLab Whether the git provider is GitLab\n * @returns The short name of the PR\n */\nexport const getPRShort = (isGitLab: boolean) => (isGitLab ? \"MR\" : \"PR\");\n\n/**\n * Construct the pull request (merge request) URL for different providers\n * @param prNumber The pull request number\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The pull request URL\n *\n * @example\n * constructPullRequestUrl(123, \"github\", \"owner/repo\") // \"https://github.com/owner/repo/pull/123\"\n * constructPullRequestUrl(456, \"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo/-/merge_requests/456\"\n * constructPullRequestUrl(789, \"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo/pull-requests/789\"\n * constructPullRequestUrl(789, \"bitbucket\", \"PROJECT/repo\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/pull-requests/789\"\n */\nexport const constructPullRequestUrl = (\n prNumber: number,\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/pull/${prNumber}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/merge_requests/${prNumber}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/pull-requests/${prNumber}`;\n case \"bitbucket_data_center\": {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}/pull-requests/${prNumber}`;\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n return `${baseUrl}/${org}/${project}/_git/${repo}/pullrequest/${prNumber}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n/**\n * Construct the repository URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @returns The repository URL\n *\n * @example\n * constructRepositoryUrl(\"github\", \"owner/repo\") // \"https://github.com/owner/repo\"\n * constructRepositoryUrl(\"gitlab\", \"owner/repo\") // \"https://gitlab.com/owner/repo\"\n * constructRepositoryUrl(\"bitbucket\", \"owner/repo\") // \"https://bitbucket.org/owner/repo\"\n */\nexport const constructRepositoryUrl = (\n provider: Provider,\n repositoryName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n if (provider === \"bitbucket_data_center\") {\n const [project, repo] = repositoryName.split(\"/\");\n return `${baseUrl}/projects/${project}/repos/${repo}`;\n }\n return `${baseUrl}/${repositoryName}`;\n};\n\n/**\n * Percent-encode a branch name for use as a URL path component. Each\n * `/`-separated segment is encoded on its own so nested branch names such as\n * `release/1.0` keep their slashes while `#`, `%` and `&` are escaped.\n */\nconst encodeBranchPath = (branchName: string): string =>\n branchName.split(\"/\").map(encodeURIComponent).join(\"/\");\n\n/**\n * Construct the branch URL for different providers\n * @param provider The git provider\n * @param repositoryName The repository name in format \"owner/repo\"\n * @param branchName The branch name\n * @param host Optional custom host for self-hosted instances\n * @returns The branch URL\n *\n * @example\n * constructBranchUrl(\"github\", \"owner/repo\", \"main\") // \"https://github.com/owner/repo/tree/main\"\n * constructBranchUrl(\"gitlab\", \"owner/repo\", \"develop\") // \"https://gitlab.com/owner/repo/-/tree/develop\"\n * constructBranchUrl(\"bitbucket\", \"owner/repo\", \"feature\") // \"https://bitbucket.org/owner/repo/src/feature\"\n * constructBranchUrl(\"bitbucket\", \"PROJECT/repo\", \"feature\", \"server.com\") // \"https://server.com/projects/PROJECT/repos/repo/browse?at=refs/heads/feature\"\n */\nexport const constructBranchUrl = (\n provider: Provider,\n repositoryName: string,\n branchName: string,\n host?: string | null,\n): string => {\n const baseUrl = getGitProviderBaseUrl(provider, host);\n\n switch (provider) {\n case \"github\":\n return `${baseUrl}/${repositoryName}/tree/${encodeBranchPath(branchName)}`;\n case \"forgejo\":\n return `${baseUrl}/${repositoryName}/src/branch/${encodeBranchPath(branchName)}`;\n case \"gitlab\":\n return `${baseUrl}/${repositoryName}/-/tree/${encodeBranchPath(branchName)}`;\n case \"bitbucket\":\n return `${baseUrl}/${repositoryName}/src/${encodeBranchPath(branchName)}`;\n case \"bitbucket_data_center\": {\n // Bitbucket Server format: /projects/{PROJECT}/repos/{repo}/browse?at=refs/heads/{branch}\n const parts = repositoryName.split(\"/\");\n if (parts.length >= 2) {\n const [project, repo] = parts;\n // The branch is one query value here, so encode it whole: an\n // unencoded `&` would start a new query parameter.\n return `${baseUrl}/projects/${project}/repos/${repo}/browse?at=refs/heads/${encodeURIComponent(branchName)}`;\n }\n return \"\";\n }\n case \"azure_devops\": {\n // Azure DevOps format: org/project/repo\n const parts = repositoryName.split(\"/\");\n if (parts.length === 3) {\n const [org, project, repo] = parts;\n // The branch is one query value here, so encode it whole: an\n // unencoded `&` would start a new query parameter.\n return `${baseUrl}/${org}/${project}/_git/${repo}?version=GB${encodeURIComponent(branchName)}`;\n }\n return \"\";\n }\n default:\n return \"\";\n }\n};\n\n// Git Action Prompts\n\nconst GIT_COMMIT_PROMPT =\n \"Please review the current changes and create a git commit with a concise, descriptive message.\";\n\nexport const getGitCommitPrompt = (): string => GIT_COMMIT_PROMPT;\n\n/**\n * Generate a git pull prompt\n * @returns The git pull prompt\n */\nexport const getGitPullPrompt = (): string =>\n \"Please pull the latest code from the repository.\";\n\n/**\n * Generate a git push prompt\n * @param gitProvider The git provider\n * @returns The git push prompt\n */\nexport const getGitPushPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the changes to a remote branch on ${providerName}, but do NOT create a ${pr}. Check your current branch name first - if it's main, master, deploy, or another common default branch name, create a new branch with a descriptive name related to your changes. Otherwise, use the exact SAME branch name as the one you are currently on.`;\n};\n\n/**\n * Generate a create pull request prompt\n * @param gitProvider The git provider\n * @returns The create PR prompt\n */\nexport const getCreatePRPrompt = (gitProvider: Provider): string => {\n const providerName = getProviderName(gitProvider);\n const pr = getPR(gitProvider === \"gitlab\");\n const prShort = getPRShort(gitProvider === \"gitlab\");\n\n return `Please push the changes to ${providerName} and open a ${pr}. If you're on a default branch (e.g., main, master, deploy), create a new branch with a descriptive name otherwise use the current branch. If a ${pr} template exists in the repository, please follow it when creating the ${prShort} description.`;\n};\n\n/**\n * Generate a push to existing PR prompt\n * @param gitProvider The git provider\n * @returns The push to PR prompt\n */\nexport const getPushToPRPrompt = (gitProvider: Provider): string => {\n const pr = getPR(gitProvider === \"gitlab\");\n\n return `Please push the latest changes to the existing ${pr}.`;\n};\n\n/**\n * Generate a create new branch prompt\n * @returns The create new branch prompt\n */\nexport const getCreateNewBranchPrompt = (): string =>\n \"Please create a new branch with a descriptive name related to the work you plan to do.\";\n\n// Helper functions\nexport function getTotalTaskCount(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n): number {\n if (!suggestedTasks) return 0;\n return suggestedTasks.flatMap((group) => group.tasks).length;\n}\n\nexport function getLimitedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[],\n maxTasks: number,\n): SuggestedTaskGroup[] {\n const limitedGroups: SuggestedTaskGroup[] = [];\n let taskCount = 0;\n\n for (const group of suggestedTasks) {\n if (taskCount >= maxTasks) break;\n\n const remainingTasksNeeded = maxTasks - taskCount;\n const tasksToShow = group.tasks.slice(0, remainingTasksNeeded);\n\n if (tasksToShow.length > 0) {\n limitedGroups.push({\n ...group,\n tasks: tasksToShow,\n });\n taskCount += tasksToShow.length;\n }\n }\n\n return limitedGroups;\n}\n\nexport function getDisplayedTaskGroups(\n suggestedTasks: SuggestedTaskGroup[] | undefined,\n isExpanded: boolean,\n): SuggestedTaskGroup[] {\n if (!suggestedTasks || suggestedTasks.length === 0) {\n return [];\n }\n\n if (isExpanded) {\n return suggestedTasks;\n }\n\n return getLimitedTaskGroups(suggestedTasks, 3);\n}\n\n/**\n * Get the label for a conversation status\n * @param status The conversation status\n * @returns The localized label for the status\n */\nexport const getConversationStatusLabel = (\n status: ConversationStatus,\n): string => {\n switch (status) {\n case \"STOPPED\":\n return \"COMMON$STOPPED\";\n case \"RUNNING\":\n return \"COMMON$RUNNING\";\n case \"STARTING\":\n return \"COMMON$STARTING\";\n case \"ERROR\":\n return \"COMMON$ERROR\";\n case \"ARCHIVED\":\n return \"COMMON$ARCHIVED\"; // Use STOPPED for archived conversations\n default:\n return \"COMMON$UNKNOWN\";\n }\n};\n\n// Task Tracking Utility Functions\n\n/**\n * Get the status icon for a task status\n * @param status The task status\n * @returns The emoji icon for the status\n */\nexport const getStatusIcon = (status: string) => {\n switch (status) {\n case \"todo\":\n return \"⏳\";\n case \"in_progress\":\n return \"🔄\";\n case \"done\":\n return \"✅\";\n default:\n return \"❓\";\n }\n};\n\n/**\n * Get the CSS class names for a task status badge\n * @param status The task status\n * @returns The CSS class names for styling the status badge\n */\nexport const getStatusClassName = (status: string) => {\n if (status === \"done\") {\n return \"bg-green-800 text-green-200\";\n }\n if (status === \"in_progress\") {\n return \"bg-yellow-800 text-yellow-200\";\n }\n return \"bg-tertiary text-[var(--oh-text-tertiary)]\";\n};\n\n/**\n * Helper function to apply client-side filtering based on search query\n * @param repo The Git repository to check\n * @param searchQuery The search query string\n * @returns True if the repository should be included based on the search query\n */\nexport const shouldIncludeRepository = (\n repo: GitRepository,\n searchQuery: string,\n): boolean => {\n if (!searchQuery.trim()) {\n return true;\n }\n\n const sanitizedQuery = sanitizeQuery(searchQuery);\n const sanitizedRepoName = sanitizeQuery(repo.full_name);\n return sanitizedRepoName.includes(sanitizedQuery);\n};\n\n/**\n * Get the OpenHands query string based on the provider\n * @param provider The git provider\n * @returns The query string for searching OpenHands repositories\n */\nexport const getOpenHandsQuery = (provider: Provider | null): string => {\n const providerRepositorySuffix: Record<string, string> = {\n gitlab: \"openhands-config\",\n azure_devops: \"openhands-config\",\n default: \".openhands\",\n } as const;\n\n return provider && provider in providerRepositorySuffix\n ? providerRepositorySuffix[provider]\n : providerRepositorySuffix.default;\n};\n\n/**\n * Check if a repository has the OpenHands suffix based on the provider\n * @param repo The Git repository to check\n * @param provider The git provider\n * @returns True if the repository has the OpenHands suffix\n */\nexport const hasOpenHandsSuffix = (\n repo: GitRepository,\n provider: Provider | null,\n): boolean => repo.full_name.endsWith(`/${getOpenHandsQuery(provider)}`);\n\n/**\n * Build headers for V1 API requests that require session authentication\n * @param sessionApiKey Session API key for authentication\n * @returns Headers object with X-Session-API-Key if provided\n */\nexport const buildSessionHeaders = (\n sessionApiKey?: string | null,\n): Record<string, string> => {\n const headers: Record<string, string> = {};\n if (sessionApiKey) {\n headers[\"X-Session-API-Key\"] = sessionApiKey;\n }\n return headers;\n};\n\n/**\n * Check if a task is currently being polled (loading state)\n * @param taskStatus The task status string (e.g., \"WORKING\", \"ERROR\", \"READY\")\n * @returns True if the task is in a loading state (not ERROR and not READY)\n *\n * @example\n * isTaskPolling(\"WORKING\") // Returns true\n * isTaskPolling(\"PREPARING_REPOSITORY\") // Returns true\n * isTaskPolling(\"READY\") // Returns false\n * isTaskPolling(\"ERROR\") // Returns false\n * isTaskPolling(null) // Returns false\n * isTaskPolling(undefined) // Returns false\n */\nexport const isTaskPolling = (taskStatus: string | null | undefined): boolean =>\n !!taskStatus && taskStatus !== \"ERROR\" && taskStatus !== \"READY\";\n\n/**\n * Get the appropriate color based on agent status\n * @param options Configuration object for status color calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.isStartingStatus Whether the agent is in a starting state (LOADING or INIT)\n * @param options.isStopStatus Whether the conversation status is STOPPED\n * @param options.curAgentState The current agent state\n * @returns The hex color code for the status\n *\n * @example\n * getStatusColor({\n * isPausing: false,\n * isTask: false,\n * taskStatus: undefined,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"var(--oh-status-success)\"\n */\nexport const getStatusColor = (options: {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n}): string => {\n const {\n isPausing,\n isTask,\n taskStatus,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n } = options;\n\n // Show pausing status\n if (isPausing) {\n return \"#FFD600\";\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return OH_STATUS_ERROR_COLOR;\n }\n return \"#FFD600\";\n }\n\n if (isStartingStatus) {\n return \"#FFD600\";\n }\n if (isStopStatus) {\n return \"#ffffff\";\n }\n if (curAgentState === AgentState.ERROR) {\n return OH_STATUS_ERROR_COLOR;\n }\n return OH_STATUS_SUCCESS_COLOR;\n};\n\ninterface GetStatusTextArgs {\n isPausing: boolean;\n isTask: boolean;\n taskStatus?: AppConversationStartTaskStatus | null;\n taskDetail?: string | null;\n isStartingStatus: boolean;\n isStopStatus: boolean;\n curAgentState: AgentState;\n errorMessage?: string | null;\n t: (t: string) => string;\n}\n\n/**\n * Get the server status text based on agent and task state\n *\n * @param options Configuration object for status text calculation\n * @param options.isPausing Whether the agent is currently pausing\n * @param options.isTask Whether we're polling a task\n * @param options.taskStatus The task status string (e.g., \"ERROR\", \"READY\")\n * @param options.taskDetail Optional task-specific detail text\n * @param options.isStartingStatus Whether the conversation is in STARTING state\n * @param options.isStopStatus Whether the conversation is STOPPED\n * @param options.curAgentState The current agent state\n * @param options.errorMessage Optional agent error message\n * @returns Localized human-readable status text\n *\n * @example\n * getStatusText({\n * isPausing: false,\n * isTask: true,\n * taskStatus: \"STARTING_CONVERSATION\",\n * taskDetail: null,\n * isStartingStatus: false,\n * isStopStatus: false,\n * curAgentState: AgentState.RUNNING\n * }) // Returns \"Starting conversation\"\n */\nexport function getStatusText({\n isPausing = false,\n isTask,\n taskStatus,\n taskDetail,\n isStartingStatus,\n isStopStatus,\n curAgentState,\n errorMessage,\n t,\n}: GetStatusTextArgs): string {\n // Show pausing status\n if (isPausing) {\n return t(I18nKey.COMMON$STOPPING);\n }\n\n // Show task status if we're polling a task\n if (isTask && taskStatus) {\n if (taskStatus === \"ERROR\") {\n return taskDetail || t(I18nKey.CONVERSATION$ERROR_STARTING_CONVERSATION);\n }\n\n if (taskStatus === \"READY\") {\n return t(I18nKey.CONVERSATION$READY);\n }\n\n return taskDetail || t(getTaskStatusI18nKey(taskStatus));\n }\n\n if (isStartingStatus) {\n return t(I18nKey.COMMON$STARTING);\n }\n\n if (isStopStatus) {\n return t(I18nKey.COMMON$SERVER_STOPPED);\n }\n\n if (curAgentState === AgentState.ERROR) {\n return errorMessage || t(I18nKey.COMMON$ERROR);\n }\n\n return t(I18nKey.COMMON$RUNNING);\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/agent-canvas",
3
- "version": "1.16.0",
3
+ "version": "1.18.0",
4
4
  "description": "Agent Canvas UI for OpenHands - run AI coding agents with a visual interface",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -17,13 +17,13 @@
17
17
  "agent-canvas": "bin/agent-canvas.mjs"
18
18
  },
19
19
  "engines": {
20
- "node": ">=22.12.0"
20
+ "node": ">=24"
21
21
  },
22
22
  "dependencies": {
23
23
  "@heroui/react": "2.8.10",
24
24
  "@microlink/react-json-view": "1.31.25",
25
25
  "@monaco-editor/react": "4.7.0",
26
- "@openhands/extensions": "0.19.0",
26
+ "@openhands/extensions": "0.20.0",
27
27
  "@openhands/typescript-client": "1.39.0",
28
28
  "@react-router/node": "7.18.2",
29
29
  "@react-router/serve": "7.18.2",
@@ -187,7 +187,7 @@
187
187
  "@vercel/static-config": {
188
188
  "ajv": "8.20.0"
189
189
  },
190
- "dompurify": "3.4.12",
190
+ "dompurify": "3.4.14",
191
191
  "typed-rest-client": {
192
192
  "qs": "6.15.3"
193
193
  }
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * Usage:
21
21
  * node scripts/check-sdk-version-sync.mjs
22
- * EXPECTED_SDK_VERSION=1.44.0 node scripts/check-sdk-version-sync.mjs
22
+ * EXPECTED_SDK_VERSION=1.46.0 node scripts/check-sdk-version-sync.mjs
23
23
  * node scripts/check-sdk-version-sync.mjs --check-pypi
24
24
  *
25
25
  * Environment variables:
@@ -78,7 +78,7 @@ Triggering from other repos:
78
78
  -H "Authorization: token \$GITHUB_TOKEN" \\
79
79
  -H "Accept: application/vnd.github.v3+json" \\
80
80
  https://api.github.com/repos/OpenHands/OpenHands/dispatches \\
81
- -d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.44.0"}}'
81
+ -d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.46.0"}}'
82
82
  `);
83
83
  process.exit(0);
84
84
  }
@@ -260,9 +260,9 @@ async function fetchPyPIDependencies(packageName, version) {
260
260
  * Parse PyPI requires_dist array and extract SDK package versions
261
261
  *
262
262
  * PyPI returns dependencies in PEP 508 format like:
263
- * "openhands-sdk>=1.44.0,<2.0.0"
264
- * "openhands-tools==1.44.0"
265
- * "openhands-workspace (>=1.44.0)"
263
+ * "openhands-sdk>=1.46.0,<2.0.0"
264
+ * "openhands-tools==1.46.0"
265
+ * "openhands-workspace (>=1.46.0)"
266
266
  */
267
267
  function parseSdkVersionsFromRequiresDist(requiresDist) {
268
268
  const versions = {};
@@ -276,7 +276,7 @@ function parseSdkVersionsFromRequiresDist(requiresDist) {
276
276
  }
277
277
 
278
278
  // Extract the version number - look for patterns like:
279
- // ">=1.44.0", "==1.44.0", "(>=1.44.0)", "~=1.44.0"
279
+ // ">=1.46.0", "==1.46.0", "(>=1.46.0)", "~=1.46.0"
280
280
  // After the package name and before any comma or closing paren
281
281
  const versionPattern = /[><=~!]+\s*([0-9]+(?:\.[0-9]+)*)/;
282
282
  const match = dep.match(versionPattern);
@@ -421,7 +421,7 @@ export const AGENT_SERVER_IMPORT_MODULES = "canvas_ui_tool";
421
421
  * edits are picked up without a manual reinstall. The agent-server itself
422
422
  * is rebuilt from local source on each invocation (--reinstall).
423
423
  * - OH_AGENT_SERVER_GIT_REF: Git commit SHA or branch name
424
- * - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.44.0")
424
+ * - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.46.0")
425
425
  *
426
426
  * If none are set, defaults to the released version specified by
427
427
  * DEFAULT_AGENT_SERVER_VERSION. Set OH_AGENT_SERVER_GIT_REF to use a