@openhands/agent-canvas 1.0.0-alpha.7 → 1.0.0-alpha.8

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 (691) hide show
  1. package/README.md +9 -18
  2. package/bin/agent-canvas.mjs +27 -1
  3. package/build/assets/{QueryClientProvider-DITRCGAK.js → QueryClientProvider-B7kl84Kj.js} +1 -1
  4. package/build/assets/{Trans-D43bd3yR.js → Trans-1j65oy9O.js} +1 -1
  5. package/build/assets/{acp-providers-SCPK1BIU.js → acp-providers-DauuOsW9.js} +1 -1
  6. package/build/assets/{acp-route-guard-IWlFmS6x.js → acp-route-guard-CQTmeJwM.js} +1 -1
  7. package/build/assets/{active-backend-context-CkP3ZEJs.js → active-backend-context-TVbjnvmP.js} +1 -1
  8. package/build/assets/add-backend-modal-KMmPQNZU.js +1 -0
  9. package/build/assets/{agent-server-client-options-8OJSXbm8.js → agent-server-client-options-DT2GP6VJ.js} +1 -1
  10. package/build/assets/{agent-server-compatibility-DvKtnXHw.js → agent-server-compatibility-2aOx5iWd.js} +1 -1
  11. package/build/assets/{agent-server-conversation-service.api-BdEre_71.js → agent-server-conversation-service.api-DSl9G5UR.js} +3 -3
  12. package/build/assets/{agent-settings-DdisD2Xx.js → agent-settings-B247S9G3.js} +2 -2
  13. package/build/assets/{alert-banner-CvTYN73l.js → alert-banner-BWoqueRw.js} +1 -1
  14. package/build/assets/{analytics-consent-form-modal-BKgT9i2w.js → analytics-consent-form-modal-C7sXfxRh.js} +1 -1
  15. package/build/assets/{app-settings-DcYXtxGP.js → app-settings-BVeSaty9.js} +1 -1
  16. package/build/assets/{automation-detail-D7GEU0vR.js → automation-detail-g5-RZ0da.js} +1 -1
  17. package/build/assets/{automations-list-CkVNsgzm.js → automations-list-DHoq_0MM.js} +1 -1
  18. package/build/assets/{backend-form-modal-KudhWUX8.js → backend-form-modal-K6IMCr3p.js} +1 -1
  19. package/build/assets/{backend-synced-settings-badge-BFy2HylT.js → backend-synced-settings-badge-nAfiUWvM.js} +1 -1
  20. package/build/assets/{base-modal-B4HvlFHE.js → base-modal-CQRvRHu1.js} +1 -1
  21. package/build/assets/{brand-button-8fVVei4i.js → brand-button-C2nEKopC.js} +1 -1
  22. package/build/assets/{browser-vYpdU3CR.js → browser-DKG63inJ.js} +1 -1
  23. package/build/assets/{browser-tab-DTM6RyoV.js → browser-tab-B_BuTvrO.js} +1 -1
  24. package/build/assets/{checkmark-BcvXE9bf.js → checkmark-BJJrZUF8.js} +1 -1
  25. package/build/assets/{chevron-left-small-BqSkXTeq.js → chevron-left-small-CSh-sE9L.js} +1 -1
  26. package/build/assets/{circle-plus-check-toggle-DRvuu-RD.js → circle-plus-check-toggle-qs8Va1cC.js} +1 -1
  27. package/build/assets/{clock-DfoVUZVq.js → clock-ZR4Kn-_Y.js} +1 -1
  28. package/build/assets/{close-SnIy2eLD.js → close-BdmyeRqS.js} +1 -1
  29. package/build/assets/{combobox-caret-BMsz5mQX.js → combobox-caret-B53O9Hsq.js} +1 -1
  30. package/build/assets/{condenser-settings-DduLQcpV.js → condenser-settings-A35V3yng.js} +1 -1
  31. package/build/assets/{confirmation-modal-B-DOYMUH.js → confirmation-modal-C9-La0h3.js} +1 -1
  32. package/build/assets/{context-menu-list-item-DzjPB8aC.js → context-menu-list-item-Buu9nc0q.js} +1 -1
  33. package/build/assets/conversation-BD5WemJI.js +19 -0
  34. package/build/assets/conversation-C47K62n8.js +1 -0
  35. package/build/assets/conversation-panel-Dn-S56Gk.js +1 -0
  36. package/build/assets/{conversation-service.api-YTGTw0pz.js → conversation-service.api-C8pYCyV6.js} +1 -1
  37. package/build/assets/{conversation-tab-empty-state-BtFDbyTe.js → conversation-tab-empty-state-D8dNvo-V.js} +1 -1
  38. package/build/assets/{conversation-websocket-context-DulnrIHh.js → conversation-websocket-context-Ywrxd_9p.js} +1 -1
  39. package/build/assets/{copy-BxgbrjDT.js → copy-C7Ti2d8C.js} +1 -1
  40. package/build/assets/{custom-toast-handlers-BYxhSr3t.js → custom-toast-handlers-BOc3qeQ7.js} +1 -1
  41. package/build/assets/declaration-D378OjpZ.js +1 -0
  42. package/build/assets/{device-verify-CTbXX9CQ.js → device-verify-CMusn8nX.js} +1 -1
  43. package/build/assets/edit-automation-modal-Dnjxbjn7.js +1 -0
  44. package/build/assets/{ellipsis-button-BoU2-xlG.js → ellipsis-button-ugUATsNo.js} +1 -1
  45. package/build/assets/{entry.client-DU7-q4ZU.js → entry.client-D9uR9Blz.js} +2 -2
  46. package/build/assets/{enum-filter-dropdown-BJt-NplD.js → enum-filter-dropdown-1vpOGySB.js} +1 -1
  47. package/build/assets/{environment-switch-overlay-DQ1n6Iu6.js → environment-switch-overlay-CTCTQikP.js} +1 -1
  48. package/build/assets/{extensions-hub-BW1FAKFJ.js → extensions-hub-BSUseHVF.js} +1 -1
  49. package/build/assets/{extensions-navigation-CbPMhSML.js → extensions-navigation-CT1kc1u_.js} +1 -1
  50. package/build/assets/{files-tab-CbJ4s7Ik.js → files-tab-B3A1NDlZ.js} +1 -1
  51. package/build/assets/{folder-CerIk8uG.js → folder-0WSMImNX.js} +1 -1
  52. package/build/assets/git-control-bar-branch-button-CcIpmyfM.js +27 -0
  53. package/build/assets/{git-provider-icon-D8RE4unY.js → git-provider-icon-DYE9n7fs.js} +1 -1
  54. package/build/assets/{home-DR11ejqB.js → home-dIzxi5Dd.js} +1 -1
  55. package/build/assets/{i18n-DkYgs32x.js → i18n-DjAGhTis.js} +1 -1
  56. package/build/assets/install-server-modal-z5VaHeXd.js +1 -0
  57. package/build/assets/{launch-DKCU9uJH.js → launch-hZ0ifhcV.js} +1 -1
  58. package/build/assets/{lesson-plan-CmkRbe6Z.js → lesson-plan-DRYG5SLI.js} +1 -1
  59. package/build/assets/{link-external-CvxB0BmI.js → link-external-Df8J52xI.js} +1 -1
  60. package/build/assets/{llm-client-BpIfxETv.js → llm-client-ChQzg4wX.js} +1 -1
  61. package/build/assets/llm-settings-2036m7Wt.js +1 -0
  62. package/build/assets/{llm-settings-BOJC4vD-.js → llm-settings-CcHqGOYL.js} +1 -1
  63. package/build/assets/{loading-spinner-91b5FiMQ.js → loading-spinner-C04FGh14.js} +1 -1
  64. package/build/assets/{manage-backends-modal-DqpzcxdI.js → manage-backends-modal-rYeyGx7j.js} +1 -1
  65. package/build/assets/{manage-workspaces-modal-eG6XgAvw.js → manage-workspaces-modal-C5EuW8m1.js} +1 -1
  66. package/build/assets/manifest-97e839da.js +1 -0
  67. package/build/assets/{markdown-renderer-wZnLDbA1.js → markdown-renderer-CEX4Becj.js} +1 -1
  68. package/build/assets/mcp-C06YssEI.js +9 -0
  69. package/build/assets/messages-T2ewVkbp.js +36 -0
  70. package/build/assets/{modal-backdrop-B04pVYAD.js → modal-backdrop-DTYGVmOR.js} +1 -1
  71. package/build/assets/{modal-body-CgUoFQA1.js → modal-body-YElmM1dV.js} +1 -1
  72. package/build/assets/{modal-close-button-SM_WXzDY.js → modal-close-button-C_GpQt9F.js} +1 -1
  73. package/build/assets/{model-selector-7id-Uirf.js → model-selector-DeMmw-Xa.js} +1 -1
  74. package/build/assets/{navigation-context-BFjstyH6.js → navigation-context-DeIPtGPp.js} +1 -1
  75. package/build/assets/{navigation-link-DFQ7YcWq.js → navigation-link-C9JD4PYD.js} +1 -1
  76. package/build/assets/{openhands-logo-DkDp75rC.js → openhands-logo-CI5Fhn1W.js} +1 -1
  77. package/build/assets/{option-service.api-DN0ZcGjw.js → option-service.api-DsI1UW7N.js} +1 -1
  78. package/build/assets/{organization-service.api-Ct2dZF8M.js → organization-service.api-COwMPFg5.js} +1 -1
  79. package/build/assets/{path-utils-D1ZtqFC7.js → path-utils-CqJboYxo.js} +1 -1
  80. package/build/assets/{plan-components-gOm-daR3.js → plan-components-DEjMuDDG.js} +1 -1
  81. package/build/assets/{planner-tab-yubfN-6U.js → planner-tab-BrntFmb1.js} +1 -1
  82. package/build/assets/{profiles-client-D4twHRVf.js → profiles-client-BGkKEV9j.js} +1 -1
  83. package/build/assets/{providers-C2T07PM3.js → providers-DXvCAN_u.js} +1 -1
  84. package/build/assets/{proxy-BMZyC45G.js → proxy-CurRmrqf.js} +1 -1
  85. package/build/assets/{query-client-config-CiK0GJJO.js → query-client-config-Ba7qAAoO.js} +1 -1
  86. package/build/assets/recommended-automations-launcher-BI9NhG8Y.js +52 -0
  87. package/build/assets/root-BS1Td78t.js +2 -0
  88. package/build/assets/root-DHeCXo9N.css +1 -0
  89. package/build/assets/{root-layout-B4QioBS6.js → root-layout-BLjAEgle.js} +2 -2
  90. package/build/assets/{sdk-section-page-03k88tIR.js → sdk-section-page-CJW0G04-.js} +1 -1
  91. package/build/assets/{sdk-settings-schema-BY8dOy3a.js → sdk-settings-schema-QBYH-ONX.js} +1 -1
  92. package/build/assets/{search-BCAF9EDS.js → search-Cq_cFrDt.js} +1 -1
  93. package/build/assets/{secrets-service-Z3qtRb_G.js → secrets-service-Bwd5DeUs.js} +1 -1
  94. package/build/assets/{secrets-settings-BnlByuMZ.js → secrets-settings-MLXqOtX2.js} +1 -1
  95. package/build/assets/{server-client-CG1zHqph.js → server-client-C3mC8Hl3.js} +1 -1
  96. package/build/assets/{settings-DyzGLF_d.js → settings-D7E2U5tK.js} +1 -1
  97. package/build/assets/{settings-client-CkXDJwIY.js → settings-client-CwjfwoiB.js} +1 -1
  98. package/build/assets/{settings-dropdown-input-CAQWQgx-.js → settings-dropdown-input-VwAXNrOb.js} +1 -1
  99. package/build/assets/{settings-gear-D4ZkEDGb.js → settings-gear-BJwWR1ej.js} +1 -1
  100. package/build/assets/{settings-index-KtTw49xL.js → settings-index-J-3BNR0W.js} +1 -1
  101. package/build/assets/{settings-input-BWCZt9g2.js → settings-input-DBywAnA7.js} +1 -1
  102. package/build/assets/{settings-list-classes-xMleGkTC.js → settings-list-classes-BOS092DR.js} +1 -1
  103. package/build/assets/{settings-modal-Cv2YWSUY.js → settings-modal-B8vgWDTe.js} +1 -1
  104. package/build/assets/{settings-section-header-context-1wfkgjZZ.js → settings-section-header-context-32x6WTyL.js} +1 -1
  105. package/build/assets/settings-service.api-FvJGK45W.js +1 -0
  106. package/build/assets/{settings-switch-CGap2LtG.js → settings-switch-DTKmHC8F.js} +1 -1
  107. package/build/assets/{settings-utils-BBozxqqi.js → settings-utils-BsvSU3OM.js} +1 -1
  108. package/build/assets/{shared-conversation-DQlzwdpo.js → shared-conversation-a0QV8o99.js} +1 -1
  109. package/build/assets/{sidebar-mobile-menu-toggle-DXplko7u.js → sidebar-mobile-menu-toggle-DTUNI1WQ.js} +1 -1
  110. package/build/assets/{sidebar-nav-link-B4h8naZ7.js → sidebar-nav-link-CnWoZcwc.js} +1 -1
  111. package/build/assets/{skill-card-pill-row-D0oTWx-a.js → skill-card-pill-row-tZ599jli.js} +1 -1
  112. package/build/assets/{skills-BN8atjgW.js → skills-ZyAO5dyK.js} +1 -1
  113. package/build/assets/{skills-plugins-BTnp7QcQ.js → skills-plugins-BSRz041I.js} +1 -1
  114. package/build/assets/{skills-settings-CbOQvzkR.js → skills-settings-DOnMn9q1.js} +2 -2
  115. package/build/assets/{status-DDL-ipIP.js → status-CsatcFbK.js} +1 -1
  116. package/build/assets/{styled-tooltip-Awq4HMw3.js → styled-tooltip-CS3mB_1X.js} +1 -1
  117. package/build/assets/{switch-skeleton-Bv21RGWd.js → switch-skeleton-C-CfhYYV.js} +1 -1
  118. package/build/assets/{task-list-tab-B45ktzHM.js → task-list-tab-Day9nhRT.js} +1 -1
  119. package/build/assets/{terminal-DGuR4559.js → terminal-LNa-iU5c.js} +1 -1
  120. package/build/assets/{terminal-D5pzR9Ru.js → terminal-ro4SNjUU.js} +1 -1
  121. package/build/assets/{toggle-switch-gj6T-wsU.js → toggle-switch-k-IZCDbt.js} +1 -1
  122. package/build/assets/{typography-BbaUAC4V.js → typography-vVUMoNUg.js} +1 -1
  123. package/build/assets/{u-check-circle-DHGiAi-w.js → u-check-circle-DplbarS5.js} +1 -1
  124. package/build/assets/{u-check-circle-half-BPcWtWwv.js → u-check-circle-half-yDuiSZHC.js} +1 -1
  125. package/build/assets/{u-circuit-B_nK9hOu.js → u-circuit-C9tYkpeK.js} +1 -1
  126. package/build/assets/{u-edit-BPFJBd34.js → u-edit-KAUlufD8.js} +1 -1
  127. package/build/assets/{use-active-conversation-Bu5J9iLy.js → use-active-conversation-D15D9GgR.js} +1 -1
  128. package/build/assets/{use-agent-settings-schema-BbtOsR7P.js → use-agent-settings-schema-Bvp5UzV8.js} +1 -1
  129. package/build/assets/{use-agent-state-DN9Nc5pP.js → use-agent-state-DE5dlEXJ.js} +1 -1
  130. package/build/assets/{use-cloud-current-user-id-B_rMUiu8.js → use-cloud-current-user-id-DWVar4st.js} +1 -1
  131. package/build/assets/{use-config-Bcz2JL2t.js → use-config-BSu_53GL.js} +1 -1
  132. package/build/assets/{use-conversation-id-BOaaZahn.js → use-conversation-id-DajhCn2A.js} +1 -1
  133. package/build/assets/{use-create-conversation-BWFA_FId.js → use-create-conversation-DW7AGgLA.js} +1 -1
  134. package/build/assets/{use-handle-plan-click-CgrCGmT1.js → use-handle-plan-click-DpgEQDAV.js} +1 -1
  135. package/build/assets/use-is-authed-hXC8vxgT.js +1 -0
  136. package/build/assets/{use-is-creating-conversation-DhoM7UAB.js → use-is-creating-conversation-DhDeeWfA.js} +1 -1
  137. package/build/assets/{use-launch-skill-in-chat-DOyQsXFO.js → use-launch-skill-in-chat-DVGPFrbI.js} +1 -1
  138. package/build/assets/{use-llm-profiles-CAIzHJDX.js → use-llm-profiles-D3-KXwQ0.js} +1 -1
  139. package/build/assets/use-runtime-is-ready-XFbT16BD.js +1 -0
  140. package/build/assets/{use-save-settings-5m3w89Ph.js → use-save-settings-CEEKSTWG.js} +1 -1
  141. package/build/assets/{use-settings-DzG0C3vO.js → use-settings-DQ7Oo1Hj.js} +1 -1
  142. package/build/assets/{use-settings-nav-items-BIsKeX52.js → use-settings-nav-items-YmrXrjn9.js} +2 -2
  143. package/build/assets/{use-skills-Cn-78xP1.js → use-skills-Xe0vjPMt.js} +1 -1
  144. package/build/assets/{use-unified-vscode-url-C5iI-Z5A.js → use-unified-vscode-url-BOsIOd-b.js} +1 -1
  145. package/build/assets/use-user-conversation-Mc0mQgkl.js +1 -0
  146. package/build/assets/{useMutation-CRJwk4cR.js → useMutation-B4OUESdw.js} +1 -1
  147. package/build/assets/{useTranslation-01pF7z10.js → useTranslation-CpIcQBq6.js} +1 -1
  148. package/build/assets/{utils-Czcl6buL.js → utils-D-HX7JCe.js} +1 -1
  149. package/build/assets/{vendor~conversation-panel~conversation-CbjvWBSu.js → vendor~conversation-panel~conversation-BlCIz9XQ.js} +1 -1
  150. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CofhIDpd.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Ds9quNZ9.js} +1 -1
  151. package/build/assets/vendor~home~mcp~automations-list-C5PoHCy6.js +1 -0
  152. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-BQPOygpY.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-CGlZoBKa.js} +1 -1
  153. package/build/assets/{vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-CyYIBiBk.js → vendor~home~mcp~llm-settings~agent-settings~condenser-settings~verification-settings~app-se~ocm3mykx-DE11mPxp.js} +1 -1
  154. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-CuGq_cxH.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-8b8V5bfO.js} +1 -1
  155. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-CFpDeb9o.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-Dy7L6fMG.js} +1 -1
  156. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-C1p8-pMr.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-D40EXhZx.js} +1 -1
  157. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-CHrEOFl6.js +48 -0
  158. package/build/assets/{vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~kyz9p27j-DlKA6SoO.js → vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~kyz9p27j-CyUbhpbm.js} +1 -1
  159. package/build/assets/{verification-settings-DbziMp4K.js → verification-settings-BtlTiHP8.js} +1 -1
  160. package/build/assets/{vscode-tab-BVhQR2rt.js → vscode-tab-C0ShhiSU.js} +1 -1
  161. package/build/assets/{waiting-for-runtime-message-JotSOBdC.js → waiting-for-runtime-message-DWPl_Yby.js} +1 -1
  162. package/build/assets/{x-mark-CZ57VvRX.js → x-mark-CWI0f9yI.js} +1 -1
  163. package/build/favicon.svg +1 -0
  164. package/build/index.html +4 -4
  165. package/build/locales/ar/openhands.json +8 -0
  166. package/build/locales/ca/openhands.json +8 -0
  167. package/build/locales/de/openhands.json +8 -0
  168. package/build/locales/en/openhands.json +8 -0
  169. package/build/locales/es/openhands.json +8 -0
  170. package/build/locales/fr/openhands.json +8 -0
  171. package/build/locales/it/openhands.json +8 -0
  172. package/build/locales/ja/openhands.json +8 -0
  173. package/build/locales/ko-KR/openhands.json +8 -0
  174. package/build/locales/no/openhands.json +8 -0
  175. package/build/locales/pt/openhands.json +8 -0
  176. package/build/locales/tr/openhands.json +8 -0
  177. package/build/locales/uk/openhands.json +8 -0
  178. package/build/locales/zh-CN/openhands.json +8 -0
  179. package/build/locales/zh-TW/openhands.json +8 -0
  180. package/config/defaults.json +1 -1
  181. package/dist/api/agent-server-config.cjs +1 -1
  182. package/dist/api/agent-server-config.cjs.map +1 -1
  183. package/dist/api/agent-server-config.d.ts +1 -1
  184. package/dist/api/agent-server-config.js +1 -1
  185. package/dist/api/agent-server-config.js.map +1 -1
  186. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  187. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  188. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +12 -0
  189. package/dist/api/conversation-service/agent-server-conversation-service.api.js +4 -0
  190. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  191. package/dist/api/mcp-service/mcp-service.api.cjs +2 -0
  192. package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -0
  193. package/dist/api/mcp-service/mcp-service.api.d.ts +6 -0
  194. package/dist/api/mcp-service/mcp-service.api.js +36 -0
  195. package/dist/api/mcp-service/mcp-service.api.js.map +1 -0
  196. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  197. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  198. package/dist/api/settings-service/settings-service.api.d.ts +1 -0
  199. package/dist/api/settings-service/settings-service.api.js +59 -41
  200. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  201. package/dist/components/features/automations/detail/activity-log-item.d.ts +1 -1
  202. package/dist/components/features/automations/recommended-automations-launcher.d.ts +1 -1
  203. package/dist/components/features/chat/change-agent-button.cjs +1 -1
  204. package/dist/components/features/chat/change-agent-button.cjs.map +1 -1
  205. package/dist/components/features/chat/change-agent-button.js +65 -59
  206. package/dist/components/features/chat/change-agent-button.js.map +1 -1
  207. package/dist/components/features/chat/chat-interface.cjs +2 -2
  208. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  209. package/dist/components/features/chat/chat-interface.js +15 -14
  210. package/dist/components/features/chat/chat-interface.js.map +1 -1
  211. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  212. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  213. package/dist/components/features/chat/components/chat-input-actions.js +127 -149
  214. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  215. package/dist/components/features/chat/components/chat-input-model.cjs +1 -1
  216. package/dist/components/features/chat/components/chat-input-model.cjs.map +1 -1
  217. package/dist/components/features/chat/components/chat-input-model.d.ts +10 -0
  218. package/dist/components/features/chat/components/chat-input-model.js +95 -60
  219. package/dist/components/features/chat/components/chat-input-model.js.map +1 -1
  220. package/dist/components/features/chat/git-control-bar.cjs +1 -1
  221. package/dist/components/features/chat/git-control-bar.cjs.map +1 -1
  222. package/dist/components/features/chat/git-control-bar.js +60 -59
  223. package/dist/components/features/chat/git-control-bar.js.map +1 -1
  224. package/dist/components/features/conversation/conversation-name-with-status.cjs +1 -1
  225. package/dist/components/features/conversation/conversation-name-with-status.cjs.map +1 -1
  226. package/dist/components/features/conversation/conversation-name-with-status.js +2 -2
  227. package/dist/components/features/conversation/conversation-name-with-status.js.map +1 -1
  228. package/dist/components/features/conversation/conversation-name.cjs +1 -1
  229. package/dist/components/features/conversation/conversation-name.cjs.map +1 -1
  230. package/dist/components/features/conversation/conversation-name.js +3 -3
  231. package/dist/components/features/conversation/conversation-name.js.map +1 -1
  232. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs +1 -1
  233. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs.map +1 -1
  234. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js +1 -1
  235. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js.map +1 -1
  236. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
  237. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
  238. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +16 -16
  239. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
  240. package/dist/components/features/mcp-logo-badge.cjs +1 -1
  241. package/dist/components/features/mcp-logo-badge.cjs.map +1 -1
  242. package/dist/components/features/mcp-logo-badge.d.ts +2 -2
  243. package/dist/components/features/mcp-logo-badge.js +1 -1
  244. package/dist/components/features/mcp-logo-badge.js.map +1 -1
  245. package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
  246. package/dist/components/features/mcp-page/custom-server-editor.cjs.map +1 -1
  247. package/dist/components/features/mcp-page/custom-server-editor.js +64 -41
  248. package/dist/components/features/mcp-page/custom-server-editor.js.map +1 -1
  249. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  250. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  251. package/dist/components/features/mcp-page/install-server-modal.d.ts +1 -1
  252. package/dist/components/features/mcp-page/install-server-modal.js +126 -102
  253. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  254. package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
  255. package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
  256. package/dist/components/features/mcp-page/installed-server-card.js +1 -1
  257. package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
  258. package/dist/components/features/mcp-page/marketplace-card.cjs +1 -1
  259. package/dist/components/features/mcp-page/marketplace-card.cjs.map +1 -1
  260. package/dist/components/features/mcp-page/marketplace-card.d.ts +1 -1
  261. package/dist/components/features/mcp-page/marketplace-card.js +27 -25
  262. package/dist/components/features/mcp-page/marketplace-card.js.map +1 -1
  263. package/dist/components/features/mcp-page/marketplace-section.cjs +1 -1
  264. package/dist/components/features/mcp-page/marketplace-section.cjs.map +1 -1
  265. package/dist/components/features/mcp-page/marketplace-section.d.ts +1 -1
  266. package/dist/components/features/mcp-page/marketplace-section.js +1 -1
  267. package/dist/components/features/mcp-page/marketplace-section.js.map +1 -1
  268. package/dist/components/features/mcp-page/mcp-logo-stack-badge.d.ts +2 -2
  269. package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs +7 -7
  270. package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs.map +1 -1
  271. package/dist/components/features/settings/mcp-settings/mcp-server-form.d.ts +8 -12
  272. package/dist/components/features/settings/mcp-settings/mcp-server-form.js +114 -87
  273. package/dist/components/features/settings/mcp-settings/mcp-server-form.js.map +1 -1
  274. package/dist/context/scroll-context.cjs +1 -1
  275. package/dist/context/scroll-context.cjs.map +1 -1
  276. package/dist/context/scroll-context.d.ts +1 -0
  277. package/dist/context/scroll-context.js +4 -1
  278. package/dist/context/scroll-context.js.map +1 -1
  279. package/dist/favicon.svg +1 -0
  280. package/dist/hooks/mutation/use-switch-acp-model.cjs +2 -0
  281. package/dist/hooks/mutation/use-switch-acp-model.cjs.map +1 -0
  282. package/dist/hooks/mutation/use-switch-acp-model.d.ts +23 -0
  283. package/dist/hooks/mutation/use-switch-acp-model.js +26 -0
  284. package/dist/hooks/mutation/use-switch-acp-model.js.map +1 -0
  285. package/dist/hooks/mutation/use-test-mcp-server.cjs +2 -0
  286. package/dist/hooks/mutation/use-test-mcp-server.cjs.map +1 -0
  287. package/dist/hooks/mutation/use-test-mcp-server.d.ts +2 -0
  288. package/dist/hooks/mutation/use-test-mcp-server.js +10 -0
  289. package/dist/hooks/mutation/use-test-mcp-server.js.map +1 -0
  290. package/dist/hooks/query/use-automation-detail.d.ts +3 -2
  291. package/dist/hooks/use-acp-model-context.cjs.map +1 -1
  292. package/dist/hooks/use-acp-model-context.d.ts +3 -4
  293. package/dist/hooks/use-acp-model-context.js.map +1 -1
  294. package/dist/hooks/use-chat-input-model-state.cjs +2 -0
  295. package/dist/hooks/use-chat-input-model-state.cjs.map +1 -0
  296. package/dist/hooks/use-chat-input-model-state.d.ts +12 -0
  297. package/dist/hooks/use-chat-input-model-state.js +29 -0
  298. package/dist/hooks/use-chat-input-model-state.js.map +1 -0
  299. package/dist/i18n/declaration.cjs +1 -1
  300. package/dist/i18n/declaration.cjs.map +1 -1
  301. package/dist/i18n/declaration.d.ts +8 -0
  302. package/dist/i18n/declaration.js +1 -1
  303. package/dist/i18n/declaration.js.map +1 -1
  304. package/dist/i18n/translation.cjs +2 -2
  305. package/dist/i18n/translation.cjs.map +1 -1
  306. package/dist/i18n/translation.js +136 -0
  307. package/dist/i18n/translation.js.map +1 -1
  308. package/dist/locales/ar/openhands.json +8 -0
  309. package/dist/locales/ca/openhands.json +8 -0
  310. package/dist/locales/de/openhands.json +8 -0
  311. package/dist/locales/en/openhands.json +8 -0
  312. package/dist/locales/es/openhands.json +8 -0
  313. package/dist/locales/fr/openhands.json +8 -0
  314. package/dist/locales/it/openhands.json +8 -0
  315. package/dist/locales/ja/openhands.json +8 -0
  316. package/dist/locales/ko-KR/openhands.json +8 -0
  317. package/dist/locales/no/openhands.json +8 -0
  318. package/dist/locales/pt/openhands.json +8 -0
  319. package/dist/locales/tr/openhands.json +8 -0
  320. package/dist/locales/uk/openhands.json +8 -0
  321. package/dist/locales/zh-CN/openhands.json +8 -0
  322. package/dist/locales/zh-TW/openhands.json +8 -0
  323. package/dist/node_modules/@openhands/extensions/integrations/catalog/airtable.cjs +2 -0
  324. package/dist/node_modules/@openhands/extensions/integrations/catalog/airtable.cjs.map +1 -0
  325. package/dist/node_modules/@openhands/extensions/integrations/catalog/airtable.js +37 -0
  326. package/dist/node_modules/@openhands/extensions/integrations/catalog/airtable.js.map +1 -0
  327. package/dist/node_modules/@openhands/extensions/integrations/catalog/apify.cjs +2 -0
  328. package/dist/node_modules/@openhands/extensions/integrations/catalog/apify.cjs.map +1 -0
  329. package/dist/node_modules/@openhands/extensions/integrations/catalog/apify.js +36 -0
  330. package/dist/node_modules/@openhands/extensions/integrations/catalog/apify.js.map +1 -0
  331. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/atlassian.cjs +1 -1
  332. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.cjs.map +1 -0
  333. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/atlassian.js +15 -5
  334. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.js.map +1 -0
  335. package/dist/node_modules/@openhands/extensions/integrations/catalog/brave-search.cjs +2 -0
  336. package/dist/node_modules/@openhands/extensions/integrations/catalog/brave-search.cjs.map +1 -0
  337. package/dist/node_modules/@openhands/extensions/integrations/catalog/brave-search.js +36 -0
  338. package/dist/node_modules/@openhands/extensions/integrations/catalog/brave-search.js.map +1 -0
  339. package/dist/node_modules/@openhands/extensions/integrations/catalog/browser-mcp.cjs +2 -0
  340. package/dist/node_modules/@openhands/extensions/integrations/catalog/browser-mcp.cjs.map +1 -0
  341. package/dist/node_modules/@openhands/extensions/integrations/catalog/browser-mcp.js +31 -0
  342. package/dist/node_modules/@openhands/extensions/integrations/catalog/browser-mcp.js.map +1 -0
  343. package/dist/node_modules/@openhands/extensions/integrations/catalog/clickhouse.cjs +2 -0
  344. package/dist/node_modules/@openhands/extensions/integrations/catalog/clickhouse.cjs.map +1 -0
  345. package/dist/node_modules/@openhands/extensions/integrations/catalog/clickhouse.js +52 -0
  346. package/dist/node_modules/@openhands/extensions/integrations/catalog/clickhouse.js.map +1 -0
  347. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-bindings.cjs +1 -1
  348. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-bindings.cjs.map +1 -0
  349. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-bindings.js +15 -5
  350. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-bindings.js.map +1 -0
  351. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-browser-rendering.cjs +2 -0
  352. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-browser-rendering.cjs.map +1 -0
  353. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-browser-rendering.js +15 -5
  354. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-browser-rendering.js.map +1 -0
  355. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-builds.cjs +1 -1
  356. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-builds.cjs.map +1 -0
  357. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-builds.js +15 -5
  358. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-builds.js.map +1 -0
  359. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-docs.cjs +1 -1
  360. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-docs.cjs.map +1 -0
  361. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-docs.js +15 -5
  362. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-docs.js.map +1 -0
  363. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-observability.cjs +2 -0
  364. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-observability.cjs.map +1 -0
  365. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/cloudflare-observability.js +15 -5
  366. package/dist/node_modules/@openhands/extensions/integrations/catalog/cloudflare-observability.js.map +1 -0
  367. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/deepwiki.cjs +1 -1
  368. package/dist/node_modules/@openhands/extensions/integrations/catalog/deepwiki.cjs.map +1 -0
  369. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/deepwiki.js +15 -5
  370. package/dist/node_modules/@openhands/extensions/integrations/catalog/deepwiki.js.map +1 -0
  371. package/dist/node_modules/@openhands/extensions/integrations/catalog/elevenlabs.cjs +2 -0
  372. package/dist/node_modules/@openhands/extensions/integrations/catalog/elevenlabs.cjs.map +1 -0
  373. package/dist/node_modules/@openhands/extensions/integrations/catalog/elevenlabs.js +36 -0
  374. package/dist/node_modules/@openhands/extensions/integrations/catalog/elevenlabs.js.map +1 -0
  375. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/everything.cjs +1 -1
  376. package/dist/node_modules/@openhands/extensions/integrations/catalog/everything.cjs.map +1 -0
  377. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/everything.js +13 -6
  378. package/dist/node_modules/@openhands/extensions/integrations/catalog/everything.js.map +1 -0
  379. package/dist/node_modules/@openhands/extensions/integrations/catalog/exa.cjs +2 -0
  380. package/dist/node_modules/@openhands/extensions/integrations/catalog/exa.cjs.map +1 -0
  381. package/dist/node_modules/@openhands/extensions/integrations/catalog/exa.js +36 -0
  382. package/dist/node_modules/@openhands/extensions/integrations/catalog/exa.js.map +1 -0
  383. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/fetch.cjs +1 -1
  384. package/dist/node_modules/@openhands/extensions/integrations/catalog/fetch.cjs.map +1 -0
  385. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/fetch.js +13 -6
  386. package/dist/node_modules/@openhands/extensions/integrations/catalog/fetch.js.map +1 -0
  387. package/dist/node_modules/@openhands/extensions/integrations/catalog/figma.cjs +2 -0
  388. package/dist/node_modules/@openhands/extensions/integrations/catalog/figma.cjs.map +1 -0
  389. package/dist/node_modules/@openhands/extensions/integrations/catalog/figma.js +40 -0
  390. package/dist/node_modules/@openhands/extensions/integrations/catalog/figma.js.map +1 -0
  391. package/dist/node_modules/@openhands/extensions/integrations/catalog/filesystem.cjs +2 -0
  392. package/dist/node_modules/@openhands/extensions/integrations/catalog/filesystem.cjs.map +1 -0
  393. package/dist/node_modules/@openhands/extensions/integrations/catalog/filesystem.js +39 -0
  394. package/dist/node_modules/@openhands/extensions/integrations/catalog/filesystem.js.map +1 -0
  395. package/dist/node_modules/@openhands/extensions/integrations/catalog/firecrawl.cjs +2 -0
  396. package/dist/node_modules/@openhands/extensions/integrations/catalog/firecrawl.cjs.map +1 -0
  397. package/dist/node_modules/@openhands/extensions/integrations/catalog/firecrawl.js +36 -0
  398. package/dist/node_modules/@openhands/extensions/integrations/catalog/firecrawl.js.map +1 -0
  399. package/dist/node_modules/@openhands/extensions/integrations/catalog/git.cjs +2 -0
  400. package/dist/node_modules/@openhands/extensions/integrations/catalog/git.cjs.map +1 -0
  401. package/dist/node_modules/@openhands/extensions/integrations/catalog/git.js +40 -0
  402. package/dist/node_modules/@openhands/extensions/integrations/catalog/git.js.map +1 -0
  403. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs +2 -0
  404. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs.map +1 -0
  405. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js +47 -0
  406. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js.map +1 -0
  407. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/huggingface.cjs +1 -1
  408. package/dist/node_modules/@openhands/extensions/integrations/catalog/huggingface.cjs.map +1 -0
  409. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/huggingface.js +15 -5
  410. package/dist/node_modules/@openhands/extensions/integrations/catalog/huggingface.js.map +1 -0
  411. package/dist/node_modules/@openhands/extensions/integrations/catalog/kagi.cjs +2 -0
  412. package/dist/node_modules/@openhands/extensions/integrations/catalog/kagi.cjs.map +1 -0
  413. package/dist/node_modules/@openhands/extensions/integrations/catalog/kagi.js +36 -0
  414. package/dist/node_modules/@openhands/extensions/integrations/catalog/kagi.js.map +1 -0
  415. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/linear.cjs +1 -1
  416. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.cjs.map +1 -0
  417. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/linear.js +15 -5
  418. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.js.map +1 -0
  419. package/dist/node_modules/@openhands/extensions/integrations/catalog/memory.cjs +2 -0
  420. package/dist/node_modules/@openhands/extensions/integrations/catalog/memory.cjs.map +1 -0
  421. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/memory.js +13 -6
  422. package/dist/node_modules/@openhands/extensions/integrations/catalog/memory.js.map +1 -0
  423. package/dist/node_modules/@openhands/extensions/integrations/catalog/mongodb.cjs +2 -0
  424. package/dist/node_modules/@openhands/extensions/integrations/catalog/mongodb.cjs.map +1 -0
  425. package/dist/node_modules/@openhands/extensions/integrations/catalog/mongodb.js +36 -0
  426. package/dist/node_modules/@openhands/extensions/integrations/catalog/mongodb.js.map +1 -0
  427. package/dist/node_modules/@openhands/extensions/integrations/catalog/neon.cjs +2 -0
  428. package/dist/node_modules/@openhands/extensions/integrations/catalog/neon.cjs.map +1 -0
  429. package/dist/node_modules/@openhands/extensions/integrations/catalog/neon.js +40 -0
  430. package/dist/node_modules/@openhands/extensions/integrations/catalog/neon.js.map +1 -0
  431. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs +2 -0
  432. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs.map +1 -0
  433. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js +39 -0
  434. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js.map +1 -0
  435. package/dist/node_modules/@openhands/extensions/integrations/catalog/obsidian.cjs +2 -0
  436. package/dist/node_modules/@openhands/extensions/integrations/catalog/obsidian.cjs.map +1 -0
  437. package/dist/node_modules/@openhands/extensions/integrations/catalog/obsidian.js +38 -0
  438. package/dist/node_modules/@openhands/extensions/integrations/catalog/obsidian.js.map +1 -0
  439. package/dist/node_modules/@openhands/extensions/integrations/catalog/paypal.cjs +2 -0
  440. package/dist/node_modules/@openhands/extensions/integrations/catalog/paypal.cjs.map +1 -0
  441. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/paypal.js +15 -5
  442. package/dist/node_modules/@openhands/extensions/integrations/catalog/paypal.js.map +1 -0
  443. package/dist/node_modules/@openhands/extensions/integrations/catalog/playwright.cjs +2 -0
  444. package/dist/node_modules/@openhands/extensions/integrations/catalog/playwright.cjs.map +1 -0
  445. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/playwright.js +13 -6
  446. package/dist/node_modules/@openhands/extensions/integrations/catalog/playwright.js.map +1 -0
  447. package/dist/node_modules/@openhands/extensions/integrations/catalog/redis.cjs +2 -0
  448. package/dist/node_modules/@openhands/extensions/integrations/catalog/redis.cjs.map +1 -0
  449. package/dist/node_modules/@openhands/extensions/integrations/catalog/redis.js +43 -0
  450. package/dist/node_modules/@openhands/extensions/integrations/catalog/redis.js.map +1 -0
  451. package/dist/node_modules/@openhands/extensions/integrations/catalog/resend.cjs +2 -0
  452. package/dist/node_modules/@openhands/extensions/integrations/catalog/resend.cjs.map +1 -0
  453. package/dist/node_modules/@openhands/extensions/integrations/catalog/resend.js +41 -0
  454. package/dist/node_modules/@openhands/extensions/integrations/catalog/resend.js.map +1 -0
  455. package/dist/node_modules/@openhands/extensions/integrations/catalog/sentry.cjs +2 -0
  456. package/dist/node_modules/@openhands/extensions/integrations/catalog/sentry.cjs.map +1 -0
  457. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/sentry.js +15 -5
  458. package/dist/node_modules/@openhands/extensions/integrations/catalog/sentry.js.map +1 -0
  459. package/dist/node_modules/@openhands/extensions/integrations/catalog/sequential-thinking.cjs +2 -0
  460. package/dist/node_modules/@openhands/extensions/integrations/catalog/sequential-thinking.cjs.map +1 -0
  461. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/sequential-thinking.js +13 -6
  462. package/dist/node_modules/@openhands/extensions/integrations/catalog/sequential-thinking.js.map +1 -0
  463. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.cjs +2 -0
  464. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.cjs.map +1 -0
  465. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.js +45 -0
  466. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.js.map +1 -0
  467. package/dist/node_modules/@openhands/extensions/integrations/catalog/stripe.cjs +2 -0
  468. package/dist/node_modules/@openhands/extensions/integrations/catalog/stripe.cjs.map +1 -0
  469. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/stripe.js +15 -5
  470. package/dist/node_modules/@openhands/extensions/integrations/catalog/stripe.js.map +1 -0
  471. package/dist/node_modules/@openhands/extensions/integrations/catalog/supabase.cjs +2 -0
  472. package/dist/node_modules/@openhands/extensions/integrations/catalog/supabase.cjs.map +1 -0
  473. package/dist/node_modules/@openhands/extensions/integrations/catalog/supabase.js +37 -0
  474. package/dist/node_modules/@openhands/extensions/integrations/catalog/supabase.js.map +1 -0
  475. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/tavily.cjs +1 -1
  476. package/dist/node_modules/@openhands/extensions/integrations/catalog/tavily.cjs.map +1 -0
  477. package/dist/node_modules/@openhands/extensions/integrations/catalog/tavily.js +39 -0
  478. package/dist/node_modules/@openhands/extensions/integrations/catalog/tavily.js.map +1 -0
  479. package/dist/node_modules/@openhands/extensions/integrations/catalog/time.cjs +2 -0
  480. package/dist/node_modules/@openhands/extensions/integrations/catalog/time.cjs.map +1 -0
  481. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/catalog/time.js +13 -6
  482. package/dist/node_modules/@openhands/extensions/integrations/catalog/time.js.map +1 -0
  483. package/dist/node_modules/@openhands/extensions/integrations/index.cjs +2 -0
  484. package/dist/node_modules/@openhands/extensions/integrations/index.cjs.map +1 -0
  485. package/dist/node_modules/@openhands/extensions/integrations/index.js +175 -0
  486. package/dist/node_modules/@openhands/extensions/integrations/index.js.map +1 -0
  487. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/logos.cjs +1 -1
  488. package/dist/node_modules/@openhands/extensions/integrations/logos.cjs.map +1 -0
  489. package/dist/node_modules/@openhands/extensions/{mcps → integrations}/logos.js +2 -2
  490. package/dist/node_modules/@openhands/extensions/integrations/logos.js.map +1 -0
  491. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-catalog.cjs +2 -0
  492. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-catalog.cjs.map +1 -0
  493. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-catalog.js +548 -0
  494. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-catalog.js.map +1 -0
  495. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-registration-defaults.cjs +2 -0
  496. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-registration-defaults.cjs.map +1 -0
  497. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-registration-defaults.js +482 -0
  498. package/dist/node_modules/@openhands/extensions/integrations/oauth-provider-registration-defaults.js.map +1 -0
  499. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.cjs +1 -1
  500. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.cjs.map +1 -1
  501. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.js +3 -0
  502. package/dist/node_modules/@openhands/typescript-client/dist/client/conversation-client.js.map +1 -1
  503. package/dist/node_modules/@openhands/typescript-client/dist/client/mcp-client.cjs +2 -0
  504. package/dist/node_modules/@openhands/typescript-client/dist/client/mcp-client.cjs.map +1 -0
  505. package/dist/node_modules/@openhands/typescript-client/dist/client/mcp-client.js +22 -0
  506. package/dist/node_modules/@openhands/typescript-client/dist/client/mcp-client.js.map +1 -0
  507. package/dist/node_modules/@openhands/typescript-client/dist/index.cjs +1 -1
  508. package/dist/node_modules/@openhands/typescript-client/dist/index.js +1 -0
  509. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  510. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  511. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +3 -0
  512. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  513. package/dist/package.cjs +1 -1
  514. package/dist/package.cjs.map +1 -1
  515. package/dist/package.js +6 -4
  516. package/dist/package.js.map +1 -1
  517. package/dist/routes/mcp.cjs +1 -1
  518. package/dist/routes/mcp.cjs.map +1 -1
  519. package/dist/routes/mcp.js +1 -1
  520. package/dist/routes/mcp.js.map +1 -1
  521. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  522. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  523. package/dist/utils/mcp-marketplace-utils.d.ts +21 -1
  524. package/dist/utils/mcp-marketplace-utils.js +23 -13
  525. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  526. package/dist/utils/settings-utils.cjs.map +1 -1
  527. package/dist/utils/settings-utils.js.map +1 -1
  528. package/package.json +6 -4
  529. package/scripts/check-sdk-version-sync.mjs +6 -6
  530. package/scripts/dev-safe.mjs +25 -7
  531. package/scripts/dev-with-automation.mjs +6 -1
  532. package/tools/canvas_ui_tool.py +129 -0
  533. package/build/assets/add-backend-modal-CqjNjGqY.js +0 -1
  534. package/build/assets/conversation-COZAKz_K.js +0 -1
  535. package/build/assets/conversation-DWcvnmds.js +0 -19
  536. package/build/assets/conversation-panel-CZDStT0b.js +0 -1
  537. package/build/assets/declaration-C9nuq2Dj.js +0 -1
  538. package/build/assets/edit-automation-modal-C3bFxS2f.js +0 -1
  539. package/build/assets/git-control-bar-branch-button-Bm6rzSpo.js +0 -27
  540. package/build/assets/install-server-modal-VB5hOBpW.js +0 -1
  541. package/build/assets/llm-settings-CIdxmimN.js +0 -1
  542. package/build/assets/manifest-f041e61a.js +0 -1
  543. package/build/assets/mcp-BdfyCW1l.js +0 -9
  544. package/build/assets/messages-v-q35ObG.js +0 -36
  545. package/build/assets/recommended-automations-launcher-Cx7svuGE.js +0 -52
  546. package/build/assets/root-D2PVd51i.js +0 -2
  547. package/build/assets/root-DEotKI6b.css +0 -1
  548. package/build/assets/settings-service.api-Z6x0l0GU.js +0 -1
  549. package/build/assets/use-is-authed-BFoh8Ogh.js +0 -1
  550. package/build/assets/use-runtime-is-ready-BQWLEyqa.js +0 -1
  551. package/build/assets/use-user-conversation-BCYpbPT1.js +0 -1
  552. package/build/assets/vendor~home~mcp~automations-list-DRfWZRnF.js +0 -1
  553. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-BJm2mGIp.js +0 -48
  554. package/dist/node_modules/@openhands/extensions/mcps/catalog/airtable.cjs +0 -2
  555. package/dist/node_modules/@openhands/extensions/mcps/catalog/airtable.cjs.map +0 -1
  556. package/dist/node_modules/@openhands/extensions/mcps/catalog/airtable.js +0 -30
  557. package/dist/node_modules/@openhands/extensions/mcps/catalog/airtable.js.map +0 -1
  558. package/dist/node_modules/@openhands/extensions/mcps/catalog/apify.cjs +0 -2
  559. package/dist/node_modules/@openhands/extensions/mcps/catalog/apify.cjs.map +0 -1
  560. package/dist/node_modules/@openhands/extensions/mcps/catalog/apify.js +0 -29
  561. package/dist/node_modules/@openhands/extensions/mcps/catalog/apify.js.map +0 -1
  562. package/dist/node_modules/@openhands/extensions/mcps/catalog/atlassian.cjs.map +0 -1
  563. package/dist/node_modules/@openhands/extensions/mcps/catalog/atlassian.js.map +0 -1
  564. package/dist/node_modules/@openhands/extensions/mcps/catalog/brave-search.cjs +0 -2
  565. package/dist/node_modules/@openhands/extensions/mcps/catalog/brave-search.cjs.map +0 -1
  566. package/dist/node_modules/@openhands/extensions/mcps/catalog/brave-search.js +0 -29
  567. package/dist/node_modules/@openhands/extensions/mcps/catalog/brave-search.js.map +0 -1
  568. package/dist/node_modules/@openhands/extensions/mcps/catalog/browser-mcp.cjs +0 -2
  569. package/dist/node_modules/@openhands/extensions/mcps/catalog/browser-mcp.cjs.map +0 -1
  570. package/dist/node_modules/@openhands/extensions/mcps/catalog/browser-mcp.js +0 -24
  571. package/dist/node_modules/@openhands/extensions/mcps/catalog/browser-mcp.js.map +0 -1
  572. package/dist/node_modules/@openhands/extensions/mcps/catalog/clickhouse.cjs +0 -2
  573. package/dist/node_modules/@openhands/extensions/mcps/catalog/clickhouse.cjs.map +0 -1
  574. package/dist/node_modules/@openhands/extensions/mcps/catalog/clickhouse.js +0 -45
  575. package/dist/node_modules/@openhands/extensions/mcps/catalog/clickhouse.js.map +0 -1
  576. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-bindings.cjs.map +0 -1
  577. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-bindings.js.map +0 -1
  578. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-browser-rendering.cjs +0 -2
  579. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-browser-rendering.cjs.map +0 -1
  580. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-browser-rendering.js.map +0 -1
  581. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-builds.cjs.map +0 -1
  582. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-builds.js.map +0 -1
  583. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-docs.cjs.map +0 -1
  584. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-docs.js.map +0 -1
  585. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-observability.cjs +0 -2
  586. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-observability.cjs.map +0 -1
  587. package/dist/node_modules/@openhands/extensions/mcps/catalog/cloudflare-observability.js.map +0 -1
  588. package/dist/node_modules/@openhands/extensions/mcps/catalog/deepwiki.cjs.map +0 -1
  589. package/dist/node_modules/@openhands/extensions/mcps/catalog/deepwiki.js.map +0 -1
  590. package/dist/node_modules/@openhands/extensions/mcps/catalog/elevenlabs.cjs +0 -2
  591. package/dist/node_modules/@openhands/extensions/mcps/catalog/elevenlabs.cjs.map +0 -1
  592. package/dist/node_modules/@openhands/extensions/mcps/catalog/elevenlabs.js +0 -29
  593. package/dist/node_modules/@openhands/extensions/mcps/catalog/elevenlabs.js.map +0 -1
  594. package/dist/node_modules/@openhands/extensions/mcps/catalog/everything.cjs.map +0 -1
  595. package/dist/node_modules/@openhands/extensions/mcps/catalog/everything.js.map +0 -1
  596. package/dist/node_modules/@openhands/extensions/mcps/catalog/exa.cjs +0 -2
  597. package/dist/node_modules/@openhands/extensions/mcps/catalog/exa.cjs.map +0 -1
  598. package/dist/node_modules/@openhands/extensions/mcps/catalog/exa.js +0 -29
  599. package/dist/node_modules/@openhands/extensions/mcps/catalog/exa.js.map +0 -1
  600. package/dist/node_modules/@openhands/extensions/mcps/catalog/fetch.cjs.map +0 -1
  601. package/dist/node_modules/@openhands/extensions/mcps/catalog/fetch.js.map +0 -1
  602. package/dist/node_modules/@openhands/extensions/mcps/catalog/figma.cjs +0 -2
  603. package/dist/node_modules/@openhands/extensions/mcps/catalog/figma.cjs.map +0 -1
  604. package/dist/node_modules/@openhands/extensions/mcps/catalog/figma.js +0 -33
  605. package/dist/node_modules/@openhands/extensions/mcps/catalog/figma.js.map +0 -1
  606. package/dist/node_modules/@openhands/extensions/mcps/catalog/filesystem.cjs +0 -2
  607. package/dist/node_modules/@openhands/extensions/mcps/catalog/filesystem.cjs.map +0 -1
  608. package/dist/node_modules/@openhands/extensions/mcps/catalog/filesystem.js +0 -32
  609. package/dist/node_modules/@openhands/extensions/mcps/catalog/filesystem.js.map +0 -1
  610. package/dist/node_modules/@openhands/extensions/mcps/catalog/firecrawl.cjs +0 -2
  611. package/dist/node_modules/@openhands/extensions/mcps/catalog/firecrawl.cjs.map +0 -1
  612. package/dist/node_modules/@openhands/extensions/mcps/catalog/firecrawl.js +0 -29
  613. package/dist/node_modules/@openhands/extensions/mcps/catalog/firecrawl.js.map +0 -1
  614. package/dist/node_modules/@openhands/extensions/mcps/catalog/git.cjs +0 -2
  615. package/dist/node_modules/@openhands/extensions/mcps/catalog/git.cjs.map +0 -1
  616. package/dist/node_modules/@openhands/extensions/mcps/catalog/git.js +0 -33
  617. package/dist/node_modules/@openhands/extensions/mcps/catalog/git.js.map +0 -1
  618. package/dist/node_modules/@openhands/extensions/mcps/catalog/github.cjs +0 -2
  619. package/dist/node_modules/@openhands/extensions/mcps/catalog/github.cjs.map +0 -1
  620. package/dist/node_modules/@openhands/extensions/mcps/catalog/github.js +0 -40
  621. package/dist/node_modules/@openhands/extensions/mcps/catalog/github.js.map +0 -1
  622. package/dist/node_modules/@openhands/extensions/mcps/catalog/huggingface.cjs.map +0 -1
  623. package/dist/node_modules/@openhands/extensions/mcps/catalog/huggingface.js.map +0 -1
  624. package/dist/node_modules/@openhands/extensions/mcps/catalog/kagi.cjs +0 -2
  625. package/dist/node_modules/@openhands/extensions/mcps/catalog/kagi.cjs.map +0 -1
  626. package/dist/node_modules/@openhands/extensions/mcps/catalog/kagi.js +0 -29
  627. package/dist/node_modules/@openhands/extensions/mcps/catalog/kagi.js.map +0 -1
  628. package/dist/node_modules/@openhands/extensions/mcps/catalog/linear.cjs.map +0 -1
  629. package/dist/node_modules/@openhands/extensions/mcps/catalog/linear.js.map +0 -1
  630. package/dist/node_modules/@openhands/extensions/mcps/catalog/memory.cjs +0 -2
  631. package/dist/node_modules/@openhands/extensions/mcps/catalog/memory.cjs.map +0 -1
  632. package/dist/node_modules/@openhands/extensions/mcps/catalog/memory.js.map +0 -1
  633. package/dist/node_modules/@openhands/extensions/mcps/catalog/mongodb.cjs +0 -2
  634. package/dist/node_modules/@openhands/extensions/mcps/catalog/mongodb.cjs.map +0 -1
  635. package/dist/node_modules/@openhands/extensions/mcps/catalog/mongodb.js +0 -29
  636. package/dist/node_modules/@openhands/extensions/mcps/catalog/mongodb.js.map +0 -1
  637. package/dist/node_modules/@openhands/extensions/mcps/catalog/neon.cjs +0 -2
  638. package/dist/node_modules/@openhands/extensions/mcps/catalog/neon.cjs.map +0 -1
  639. package/dist/node_modules/@openhands/extensions/mcps/catalog/neon.js +0 -33
  640. package/dist/node_modules/@openhands/extensions/mcps/catalog/neon.js.map +0 -1
  641. package/dist/node_modules/@openhands/extensions/mcps/catalog/notion.cjs +0 -2
  642. package/dist/node_modules/@openhands/extensions/mcps/catalog/notion.cjs.map +0 -1
  643. package/dist/node_modules/@openhands/extensions/mcps/catalog/notion.js +0 -32
  644. package/dist/node_modules/@openhands/extensions/mcps/catalog/notion.js.map +0 -1
  645. package/dist/node_modules/@openhands/extensions/mcps/catalog/obsidian.cjs +0 -2
  646. package/dist/node_modules/@openhands/extensions/mcps/catalog/obsidian.cjs.map +0 -1
  647. package/dist/node_modules/@openhands/extensions/mcps/catalog/obsidian.js +0 -31
  648. package/dist/node_modules/@openhands/extensions/mcps/catalog/obsidian.js.map +0 -1
  649. package/dist/node_modules/@openhands/extensions/mcps/catalog/paypal.cjs +0 -2
  650. package/dist/node_modules/@openhands/extensions/mcps/catalog/paypal.cjs.map +0 -1
  651. package/dist/node_modules/@openhands/extensions/mcps/catalog/paypal.js.map +0 -1
  652. package/dist/node_modules/@openhands/extensions/mcps/catalog/playwright.cjs +0 -2
  653. package/dist/node_modules/@openhands/extensions/mcps/catalog/playwright.cjs.map +0 -1
  654. package/dist/node_modules/@openhands/extensions/mcps/catalog/playwright.js.map +0 -1
  655. package/dist/node_modules/@openhands/extensions/mcps/catalog/redis.cjs +0 -2
  656. package/dist/node_modules/@openhands/extensions/mcps/catalog/redis.cjs.map +0 -1
  657. package/dist/node_modules/@openhands/extensions/mcps/catalog/redis.js +0 -36
  658. package/dist/node_modules/@openhands/extensions/mcps/catalog/redis.js.map +0 -1
  659. package/dist/node_modules/@openhands/extensions/mcps/catalog/resend.cjs +0 -2
  660. package/dist/node_modules/@openhands/extensions/mcps/catalog/resend.cjs.map +0 -1
  661. package/dist/node_modules/@openhands/extensions/mcps/catalog/resend.js +0 -34
  662. package/dist/node_modules/@openhands/extensions/mcps/catalog/resend.js.map +0 -1
  663. package/dist/node_modules/@openhands/extensions/mcps/catalog/sentry.cjs +0 -2
  664. package/dist/node_modules/@openhands/extensions/mcps/catalog/sentry.cjs.map +0 -1
  665. package/dist/node_modules/@openhands/extensions/mcps/catalog/sentry.js.map +0 -1
  666. package/dist/node_modules/@openhands/extensions/mcps/catalog/sequential-thinking.cjs +0 -2
  667. package/dist/node_modules/@openhands/extensions/mcps/catalog/sequential-thinking.cjs.map +0 -1
  668. package/dist/node_modules/@openhands/extensions/mcps/catalog/sequential-thinking.js.map +0 -1
  669. package/dist/node_modules/@openhands/extensions/mcps/catalog/slack.cjs +0 -2
  670. package/dist/node_modules/@openhands/extensions/mcps/catalog/slack.cjs.map +0 -1
  671. package/dist/node_modules/@openhands/extensions/mcps/catalog/slack.js +0 -38
  672. package/dist/node_modules/@openhands/extensions/mcps/catalog/slack.js.map +0 -1
  673. package/dist/node_modules/@openhands/extensions/mcps/catalog/stripe.cjs +0 -2
  674. package/dist/node_modules/@openhands/extensions/mcps/catalog/stripe.cjs.map +0 -1
  675. package/dist/node_modules/@openhands/extensions/mcps/catalog/stripe.js.map +0 -1
  676. package/dist/node_modules/@openhands/extensions/mcps/catalog/supabase.cjs +0 -2
  677. package/dist/node_modules/@openhands/extensions/mcps/catalog/supabase.cjs.map +0 -1
  678. package/dist/node_modules/@openhands/extensions/mcps/catalog/supabase.js +0 -30
  679. package/dist/node_modules/@openhands/extensions/mcps/catalog/supabase.js.map +0 -1
  680. package/dist/node_modules/@openhands/extensions/mcps/catalog/tavily.cjs.map +0 -1
  681. package/dist/node_modules/@openhands/extensions/mcps/catalog/tavily.js +0 -32
  682. package/dist/node_modules/@openhands/extensions/mcps/catalog/tavily.js.map +0 -1
  683. package/dist/node_modules/@openhands/extensions/mcps/catalog/time.cjs +0 -2
  684. package/dist/node_modules/@openhands/extensions/mcps/catalog/time.cjs.map +0 -1
  685. package/dist/node_modules/@openhands/extensions/mcps/catalog/time.js.map +0 -1
  686. package/dist/node_modules/@openhands/extensions/mcps/index.cjs +0 -2
  687. package/dist/node_modules/@openhands/extensions/mcps/index.cjs.map +0 -1
  688. package/dist/node_modules/@openhands/extensions/mcps/index.js +0 -87
  689. package/dist/node_modules/@openhands/extensions/mcps/index.js.map +0 -1
  690. package/dist/node_modules/@openhands/extensions/mcps/logos.cjs.map +0 -1
  691. package/dist/node_modules/@openhands/extensions/mcps/logos.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"settings-service.api.js","names":[],"sources":["../../../src/api/settings-service/settings-service.api.ts"],"sourcesContent":["import { SettingsClient } from \"@openhands/typescript-client/clients\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { Settings, SettingsSchema, SettingsValue } from \"#/types/settings\";\nimport {\n extractAppPreferences,\n readStoredAppPreferences,\n writeStoredAppPreferences,\n} from \"../app-preferences-store\";\nimport { getActiveBackend } from \"../backend-registry/active-store\";\nimport {\n fetchCloudConversationSettingsSchema,\n fetchCloudSettings,\n fetchCloudSettingsSchema,\n saveCloudSettings,\n} from \"../cloud/settings-service.api\";\nimport { getAgentServerClientOptions } from \"../agent-server-client-options\";\n\n/**\n * Response from GET /api/settings\n * Mirrors the SettingsResponse model in the agent server\n */\nexport interface SettingsApiResponse {\n agent_settings: Record<string, SettingsValue>;\n conversation_settings: Record<string, SettingsValue>;\n llm_api_key_is_set: boolean;\n}\n\n/**\n * Request payload for PATCH /api/settings\n */\nexport interface SettingsUpdateRequest {\n agent_settings_diff?: Record<string, SettingsValue>;\n conversation_settings_diff?: Record<string, SettingsValue>;\n disabled_skills?: string[];\n}\n\n/**\n * Secret exposure mode for X-Expose-Secrets header.\n *\n * - undefined: Returns redacted secrets (\"**********\")\n * - \"encrypted\": Returns cipher-encrypted values (safe for frontend to round-trip)\n * - \"plaintext\": Returns raw secret values (backend use only!)\n */\nexport type ExposeSecretsMode = \"encrypted\" | \"plaintext\" | undefined;\n\nconst deepClone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\nconst mergeRecords = (\n base: Record<string, SettingsValue> | null | undefined,\n next: Record<string, SettingsValue> | null | undefined,\n) => ({ ...(base ?? {}), ...(next ?? {}) });\n\n/**\n * Retry helper for API calls with exponential backoff.\n */\nasync function withRetry<T>(\n fn: () => Promise<T>,\n maxRetries: number = 3,\n baseDelayMs: number = 500,\n): Promise<T> {\n for (let attempt = 0; attempt < maxRetries; attempt += 1) {\n try {\n return await fn();\n } catch (error) {\n if (attempt >= maxRetries - 1) {\n throw error;\n }\n\n const delay = baseDelayMs * 2 ** attempt;\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n\n throw new Error(\"Retry attempts exhausted\");\n}\n\n/**\n * In-memory cache for settings to avoid repeated network calls.\n * The cache is invalidated on save operations.\n */\nlet settingsCache: {\n /** Settings with redacted secrets for display */\n redacted: SettingsApiResponse | null;\n /** Settings with encrypted secrets for conversation start */\n encrypted: SettingsApiResponse | null;\n /** Timestamp when the cache was last populated */\n timestamp: number;\n} = {\n redacted: null,\n encrypted: null,\n timestamp: 0,\n};\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nconst isCacheValid = () => Date.now() - settingsCache.timestamp < CACHE_TTL_MS;\n\nconst clearCache = () => {\n settingsCache = { redacted: null, encrypted: null, timestamp: 0 };\n};\n\n/**\n * Transform API response into Settings object with derived fields.\n */\nconst transformApiResponse = (\n response: SettingsApiResponse,\n): Partial<Settings> => {\n const agentSettings = response.agent_settings ?? {};\n const conversationSettings = response.conversation_settings ?? {};\n\n return {\n agent_settings: agentSettings,\n conversation_settings: conversationSettings,\n llm_api_key_set: response.llm_api_key_is_set,\n };\n};\n\n/**\n * Sync derived settings fields from agent_settings and conversation_settings.\n * This ensures backward compatibility with code that reads top-level fields.\n */\nconst syncDerivedSettings = (settings: Partial<Settings>): Settings => {\n const agentSettings = mergeRecords(\n DEFAULT_SETTINGS.agent_settings ?? {},\n settings.agent_settings ?? {},\n );\n const conversationSettings = mergeRecords(\n DEFAULT_SETTINGS.conversation_settings ?? {},\n settings.conversation_settings ?? {},\n );\n\n // App-level user preferences (language, git identity, sound notifications,\n // analytics consent) live in localStorage in local mode. In cloud mode the\n // server response carries them and overrides the local cache.\n const storedAppPrefs = readStoredAppPreferences();\n\n const merged = {\n ...deepClone(DEFAULT_SETTINGS),\n ...storedAppPrefs,\n ...settings,\n provider_tokens_set: {\n ...(DEFAULT_SETTINGS.provider_tokens_set ?? {}),\n ...(settings.provider_tokens_set ?? {}),\n },\n agent_settings: agentSettings,\n conversation_settings: conversationSettings,\n } as Settings;\n\n const llm = agentSettings.llm as Record<string, SettingsValue> | undefined;\n const condenser = agentSettings.condenser as\n | Record<string, SettingsValue>\n | undefined;\n\n if (typeof agentSettings.agent === \"string\") {\n merged.agent = agentSettings.agent;\n }\n if (typeof llm?.model === \"string\" && llm.model.length > 0) {\n merged.llm_model = llm.model;\n }\n if (typeof llm?.base_url === \"string\") {\n merged.llm_base_url = llm.base_url;\n }\n // Note: api_key may be redacted (\"**********\") when fetched without expose header\n // We don't sync it to top-level llm_api_key to avoid overwriting with redacted value\n if (typeof condenser?.enabled === \"boolean\") {\n merged.enable_default_condenser = condenser.enabled;\n }\n if (typeof condenser?.max_size === \"number\") {\n merged.condenser_max_size = condenser.max_size;\n }\n if (agentSettings.mcp_config) {\n merged.mcp_config = agentSettings.mcp_config as Settings[\"mcp_config\"];\n }\n\n if (typeof conversationSettings.confirmation_mode === \"boolean\") {\n merged.confirmation_mode = conversationSettings.confirmation_mode;\n }\n if (\n typeof conversationSettings.security_analyzer === \"string\" ||\n conversationSettings.security_analyzer === null\n ) {\n merged.security_analyzer = conversationSettings.security_analyzer as\n | string\n | null;\n }\n if (typeof conversationSettings.max_iterations === \"number\") {\n merged.max_iterations = conversationSettings.max_iterations;\n }\n\n merged.search_api_key_set = !!merged.search_api_key;\n\n return merged;\n};\n\nclass SettingsService {\n /**\n * Fetch settings from the agent server API with retry logic.\n *\n * @param exposeSecrets - Controls how secrets are returned:\n * - undefined: Secrets are redacted (\"**********\") - safe for display\n * - \"encrypted\": Secrets are cipher-encrypted - safe for round-trip to start conversation\n * - \"plaintext\": Raw secrets - DO NOT USE from frontend\n */\n static async fetchSettingsFromApi(\n exposeSecrets?: ExposeSecretsMode,\n ): Promise<SettingsApiResponse> {\n return withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).getSettings({\n exposeSecrets,\n }),\n ) as Promise<SettingsApiResponse>;\n }\n\n /**\n * Get settings for display (secrets are redacted).\n * Uses in-memory cache for performance.\n */\n static async getSettings(): Promise<Settings> {\n // Cloud uses a different settings shape (flat top-level fields\n // including provider_tokens_set, llm_model, etc.). Branch out before\n // touching the local-only cache: cloud responses bypass the local\n // SettingsApiResponse shape and feed straight into syncDerivedSettings\n // so cloud-native fields like provider_tokens_set reach the GUI's\n // useUserProviders → useAppInstallations → useGitRepositories chain.\n if (getActiveBackend().backend.kind === \"cloud\") {\n try {\n const cloud = await withRetry(() => fetchCloudSettings());\n return syncDerivedSettings(cloud);\n } catch (error) {\n console.warn(\"Failed to fetch cloud settings, using defaults:\", error);\n return syncDerivedSettings({});\n }\n }\n\n // Check cache first\n if (isCacheValid() && settingsCache.redacted) {\n return syncDerivedSettings(transformApiResponse(settingsCache.redacted));\n }\n\n try {\n const response = await this.fetchSettingsFromApi();\n settingsCache.redacted = response;\n settingsCache.timestamp = Date.now();\n return syncDerivedSettings(transformApiResponse(response));\n } catch (error) {\n // If API fails, return defaults\n console.warn(\"Failed to fetch settings from API, using defaults:\", error);\n return syncDerivedSettings({});\n }\n }\n\n /**\n * Get settings with encrypted secrets for starting conversations.\n * The encrypted secrets can be passed to the start conversation API\n * with secrets_encrypted=true for server-side decryption.\n *\n * @throws Error if encrypted settings cannot be fetched - conversations\n * should not start with broken/redacted credentials.\n */\n static async getSettingsForConversation(): Promise<{\n agentSettings: Record<string, SettingsValue>;\n conversationSettings: Record<string, SettingsValue>;\n secretsEncrypted: boolean;\n }> {\n // Check cache first\n if (isCacheValid() && settingsCache.encrypted) {\n return {\n agentSettings: settingsCache.encrypted.agent_settings,\n conversationSettings: settingsCache.encrypted.conversation_settings,\n secretsEncrypted: true,\n };\n }\n\n // Fetch encrypted settings - this MUST succeed for conversations to work.\n // Do not fall back to redacted settings as that would cause auth failures.\n const response = await this.fetchSettingsFromApi(\"encrypted\");\n settingsCache.encrypted = response;\n if (!settingsCache.timestamp) {\n settingsCache.timestamp = Date.now();\n }\n return {\n agentSettings: response.agent_settings,\n conversationSettings: response.conversation_settings,\n secretsEncrypted: true,\n };\n }\n\n static async getSettingsSchema(): Promise<SettingsSchema> {\n if (getActiveBackend().backend.kind === \"cloud\") {\n return (await fetchCloudSettingsSchema()) as SettingsSchema;\n }\n return (await new SettingsClient(\n getAgentServerClientOptions(),\n ).getAgentSchema()) as SettingsSchema;\n }\n\n static async getConversationSettingsSchema(): Promise<SettingsSchema> {\n if (getActiveBackend().backend.kind === \"cloud\") {\n return (await fetchCloudConversationSettingsSchema()) as SettingsSchema;\n }\n return (await new SettingsClient(\n getAgentServerClientOptions(),\n ).getConversationSchema()) as SettingsSchema;\n }\n\n /**\n * Save settings to the agent server API.\n * Uses PATCH for incremental updates.\n */\n static async saveSettings(\n settings: Partial<Settings> & Record<string, unknown>,\n ): Promise<boolean> {\n // Split app-level user-preference fields (language, git identity, sound\n // notifications, analytics consent) off before building the diff payload.\n // The local agent-server's PATCH /api/settings has no schema for these\n // and Pydantic would drop them silently; persist them in localStorage\n // instead, and forward them as flat top-level keys to the cloud POST.\n const { extracted: appPreferences, rest } = extractAppPreferences(\n settings as Record<string, unknown>,\n );\n const hasAppPreferences = Object.keys(appPreferences).length > 0;\n if (hasAppPreferences) {\n writeStoredAppPreferences(appPreferences);\n }\n\n const payload: SettingsUpdateRequest = {};\n\n // Extract agent_settings_diff\n const agentSettingsDiff = rest.agent_settings_diff as\n | Record<string, SettingsValue>\n | undefined;\n if (agentSettingsDiff && Object.keys(agentSettingsDiff).length > 0) {\n payload.agent_settings_diff = agentSettingsDiff;\n }\n\n // Extract conversation_settings_diff\n const conversationSettingsDiff = rest.conversation_settings_diff as\n | Record<string, SettingsValue>\n | undefined;\n if (\n conversationSettingsDiff &&\n Object.keys(conversationSettingsDiff).length > 0\n ) {\n payload.conversation_settings_diff = conversationSettingsDiff;\n }\n\n // Extract disabled_skills (cloud-only — local agent-server has no skills concept)\n const disabledSkills = rest.disabled_skills as string[] | undefined;\n if (Array.isArray(disabledSkills)) {\n payload.disabled_skills = disabledSkills;\n }\n\n const isCloud = getActiveBackend().backend.kind === \"cloud\";\n\n // The backend applies ``agent_settings_diff`` by deep-merging it into the\n // existing ``agent_settings`` dict (see SDK\n // ``openhands.agent_server.persistence.models._deep_merge``). That works\n // for scalar fields but is wrong for ``mcp_config.mcpServers``, which is\n // a name-keyed map: a diff that omits a server cannot remove it (stale\n // key stays), and a diff whose key indices shift (e.g. after deleting\n // index 0, the second server is renumbered) leaves the original keys\n // behind as duplicates pointing to the wrong server config.\n //\n // The only way to make ``mcp_config`` behave like a replace through this\n // API is to first null it out — ``null`` is not a dict, so deep-merge\n // takes the else branch and sets the field to ``None`` outright — and\n // then send the new value in a follow-up call. We do this for every\n // ``mcp_config`` write, including adds (the wasted round-trip is\n // negligible for this user action and avoids divergent code paths).\n const agentDiff = payload.agent_settings_diff;\n // Send a pre-clear PATCH when the diff sets ``mcp_config`` to a non-null\n // value. A second PATCH below then writes the new value. Skipping the\n // pre-clear when the caller is already clearing (``mcp_config: null``)\n // avoids a pointless duplicate request.\n const needsMcpPreClear =\n !!agentDiff && \"mcp_config\" in agentDiff && agentDiff.mcp_config !== null;\n\n // The pre-clear is destructive: if the follow-up write fails after the\n // clear succeeds, the user's MCP config is left empty. Snapshot the\n // previous value (in raw SDK shape, NOT the GUI's parsed MCPConfig)\n // before pre-clearing so we can attempt a best-effort rollback. The\n // original write error is always re-thrown to the caller regardless\n // of rollback success — the GUI's react-query mutations surface that\n // as an error toast so the user knows to retry.\n //\n // Snapshot must be the SDK shape (``{ mcpServers: { name: cfg }}``)\n // because that is what the backend expects on the rollback PATCH.\n // ``SettingsService.getSettings`` returns a GUI Settings object whose\n // ``mcp_config`` is typed as the parsed frontend MCPConfig and\n // defaults to empty arrays when nothing is installed, so it is not\n // suitable for round-tripping back to the backend.\n let mcpConfigSnapshot: unknown = undefined;\n if (needsMcpPreClear) {\n try {\n if (isCloud) {\n const raw = (await fetchCloudSettings()) as {\n agent_settings?: { mcp_config?: unknown };\n };\n mcpConfigSnapshot = raw?.agent_settings?.mcp_config;\n } else {\n const raw = (await SettingsService.fetchSettingsFromApi()) as {\n agent_settings?: { mcp_config?: unknown };\n };\n mcpConfigSnapshot = raw?.agent_settings?.mcp_config;\n }\n } catch {\n // Snapshot failed (network blip, etc.). Continue without rollback\n // ability — the original write error will still surface.\n }\n }\n\n if (isCloud) {\n const hasCloudWork =\n !!payload.agent_settings_diff ||\n !!payload.conversation_settings_diff ||\n payload.disabled_skills !== undefined ||\n hasAppPreferences;\n if (!hasCloudWork) {\n return true;\n }\n if (needsMcpPreClear) {\n await withRetry(() =>\n saveCloudSettings({\n agent_settings_diff: { mcp_config: null },\n }),\n );\n }\n try {\n await withRetry(() =>\n saveCloudSettings({\n ...payload,\n ...(hasAppPreferences ? { app_preferences: appPreferences } : {}),\n }),\n );\n } catch (err) {\n if (needsMcpPreClear && mcpConfigSnapshot) {\n // Best-effort rollback. We deliberately do not wrap in withRetry:\n // the user's session is already in a degraded state and we want\n // to surface the original error promptly. Swallowing the restore\n // error preserves the original failure context for the caller.\n try {\n await saveCloudSettings({\n agent_settings_diff: {\n mcp_config: mcpConfigSnapshot as SettingsValue,\n },\n });\n } catch {\n // Rollback failed; the original error takes precedence.\n }\n }\n throw err;\n }\n } else {\n // The local agent-server PATCH /api/settings rejects unknown fields and\n // requires at least one of the two diff fields. Strip disabled_skills\n // and skip the request entirely if no diffs remain. App preferences\n // are persisted to localStorage above and never sent to this endpoint.\n const localPayload = { ...payload };\n delete localPayload.disabled_skills;\n const hasLocalDiffs =\n !!localPayload.agent_settings_diff ||\n !!localPayload.conversation_settings_diff;\n if (!hasLocalDiffs) {\n if (hasAppPreferences) {\n // The localStorage write changed user-visible settings; clear the\n // in-memory cache so the next getSettings() re-derives from disk.\n clearCache();\n }\n return true;\n }\n if (needsMcpPreClear) {\n await withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).updateSettings({\n agent_settings_diff: { mcp_config: null },\n }),\n );\n }\n try {\n await withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).updateSettings(\n localPayload,\n ),\n );\n } catch (err) {\n if (needsMcpPreClear && mcpConfigSnapshot) {\n // See cloud branch above for rationale.\n try {\n await new SettingsClient(\n getAgentServerClientOptions(),\n ).updateSettings({\n agent_settings_diff: {\n mcp_config: mcpConfigSnapshot as SettingsValue,\n },\n });\n } catch {\n // Rollback failed; the original error takes precedence.\n }\n }\n throw err;\n }\n }\n\n // Invalidate cache after successful save\n clearCache();\n\n return true;\n }\n\n /**\n * Invalidate the settings cache.\n * Call this when settings may have changed externally.\n */\n static invalidateCache(): void {\n clearCache();\n }\n}\n\nexport default SettingsService;\n"],"mappings":"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"}
1
+ {"version":3,"file":"settings-service.api.js","names":[],"sources":["../../../src/api/settings-service/settings-service.api.ts"],"sourcesContent":["import { SettingsClient } from \"@openhands/typescript-client/clients\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { Settings, SettingsSchema, SettingsValue } from \"#/types/settings\";\nimport {\n extractAppPreferences,\n readStoredAppPreferences,\n writeStoredAppPreferences,\n} from \"../app-preferences-store\";\nimport { getActiveBackend } from \"../backend-registry/active-store\";\nimport {\n fetchCloudConversationSettingsSchema,\n fetchCloudSettings,\n fetchCloudSettingsSchema,\n saveCloudSettings,\n} from \"../cloud/settings-service.api\";\nimport { getAgentServerClientOptions } from \"../agent-server-client-options\";\n\n/**\n * Response from GET /api/settings\n * Mirrors the SettingsResponse model in the agent server\n */\nexport interface SettingsApiResponse {\n agent_settings: Record<string, SettingsValue>;\n conversation_settings: Record<string, SettingsValue>;\n llm_api_key_is_set: boolean;\n}\n\n/**\n * Request payload for PATCH /api/settings\n */\nexport interface SettingsUpdateRequest {\n agent_settings_diff?: Record<string, SettingsValue>;\n conversation_settings_diff?: Record<string, SettingsValue>;\n disabled_skills?: string[];\n}\n\n/**\n * Secret exposure mode for X-Expose-Secrets header.\n *\n * - undefined: Returns redacted secrets (\"**********\")\n * - \"encrypted\": Returns cipher-encrypted values (safe for frontend to round-trip)\n * - \"plaintext\": Returns raw secret values (backend use only!)\n */\nexport type ExposeSecretsMode = \"encrypted\" | \"plaintext\" | undefined;\n\nconst deepClone = <T>(value: T): T => JSON.parse(JSON.stringify(value)) as T;\n\n// disabled_skills is not persisted by the local agent-server, so we mirror\n// the app-preferences pattern: write to localStorage on save, read back on fetch.\nexport const DISABLED_SKILLS_STORAGE_KEY =\n \"openhands-agent-server-disabled-skills\";\n\nconst readStoredDisabledSkills = (): string[] | null => {\n if (typeof window === \"undefined\") return null;\n try {\n const raw = window.localStorage.getItem(DISABLED_SKILLS_STORAGE_KEY);\n if (!raw) return null;\n const parsed: unknown = JSON.parse(raw);\n if (!Array.isArray(parsed)) return null;\n return parsed.filter((v): v is string => typeof v === \"string\");\n } catch {\n return null;\n }\n};\n\nconst writeStoredDisabledSkills = (skills: string[]): void => {\n if (typeof window === \"undefined\") return;\n try {\n window.localStorage.setItem(\n DISABLED_SKILLS_STORAGE_KEY,\n JSON.stringify(skills),\n );\n } catch {\n // ignore write failures (e.g. private-browsing quota exceeded)\n }\n};\n\nconst mergeRecords = (\n base: Record<string, SettingsValue> | null | undefined,\n next: Record<string, SettingsValue> | null | undefined,\n) => ({ ...(base ?? {}), ...(next ?? {}) });\n\n/**\n * Retry helper for API calls with exponential backoff.\n */\nasync function withRetry<T>(\n fn: () => Promise<T>,\n maxRetries: number = 3,\n baseDelayMs: number = 500,\n): Promise<T> {\n for (let attempt = 0; attempt < maxRetries; attempt += 1) {\n try {\n return await fn();\n } catch (error) {\n if (attempt >= maxRetries - 1) {\n throw error;\n }\n\n const delay = baseDelayMs * 2 ** attempt;\n\n await new Promise<void>((resolve) => {\n setTimeout(resolve, delay);\n });\n }\n }\n\n throw new Error(\"Retry attempts exhausted\");\n}\n\n/**\n * In-memory cache for settings to avoid repeated network calls.\n * The cache is invalidated on save operations.\n */\nlet settingsCache: {\n /** Settings with redacted secrets for display */\n redacted: SettingsApiResponse | null;\n /** Settings with encrypted secrets for conversation start */\n encrypted: SettingsApiResponse | null;\n /** Timestamp when the cache was last populated */\n timestamp: number;\n} = {\n redacted: null,\n encrypted: null,\n timestamp: 0,\n};\n\nconst CACHE_TTL_MS = 5 * 60 * 1000; // 5 minutes\n\nconst isCacheValid = () => Date.now() - settingsCache.timestamp < CACHE_TTL_MS;\n\nconst clearCache = () => {\n settingsCache = { redacted: null, encrypted: null, timestamp: 0 };\n};\n\n/**\n * Transform API response into Settings object with derived fields.\n */\nconst transformApiResponse = (\n response: SettingsApiResponse,\n): Partial<Settings> => {\n const agentSettings = response.agent_settings ?? {};\n const conversationSettings = response.conversation_settings ?? {};\n\n const partial: Partial<Settings> = {\n agent_settings: agentSettings,\n conversation_settings: conversationSettings,\n llm_api_key_set: response.llm_api_key_is_set,\n };\n\n // The local agent-server never returns disabled_skills; read it from\n // localStorage where saveSettings writes it for local-mode clients.\n const stored = readStoredDisabledSkills();\n if (stored !== null) {\n partial.disabled_skills = stored;\n }\n\n return partial;\n};\n\n/**\n * Sync derived settings fields from agent_settings and conversation_settings.\n * This ensures backward compatibility with code that reads top-level fields.\n */\nconst syncDerivedSettings = (settings: Partial<Settings>): Settings => {\n const agentSettings = mergeRecords(\n DEFAULT_SETTINGS.agent_settings ?? {},\n settings.agent_settings ?? {},\n );\n const conversationSettings = mergeRecords(\n DEFAULT_SETTINGS.conversation_settings ?? {},\n settings.conversation_settings ?? {},\n );\n\n // App-level user preferences (language, git identity, sound notifications,\n // analytics consent) live in localStorage in local mode. In cloud mode the\n // server response carries them and overrides the local cache.\n const storedAppPrefs = readStoredAppPreferences();\n\n const merged = {\n ...deepClone(DEFAULT_SETTINGS),\n ...storedAppPrefs,\n ...settings,\n provider_tokens_set: {\n ...(DEFAULT_SETTINGS.provider_tokens_set ?? {}),\n ...(settings.provider_tokens_set ?? {}),\n },\n agent_settings: agentSettings,\n conversation_settings: conversationSettings,\n } as Settings;\n\n const llm = agentSettings.llm as Record<string, SettingsValue> | undefined;\n const condenser = agentSettings.condenser as\n | Record<string, SettingsValue>\n | undefined;\n\n if (typeof agentSettings.agent === \"string\") {\n merged.agent = agentSettings.agent;\n }\n if (typeof llm?.model === \"string\" && llm.model.length > 0) {\n merged.llm_model = llm.model;\n }\n if (typeof llm?.base_url === \"string\") {\n merged.llm_base_url = llm.base_url;\n }\n // Note: api_key may be redacted (\"**********\") when fetched without expose header\n // We don't sync it to top-level llm_api_key to avoid overwriting with redacted value\n if (typeof condenser?.enabled === \"boolean\") {\n merged.enable_default_condenser = condenser.enabled;\n }\n if (typeof condenser?.max_size === \"number\") {\n merged.condenser_max_size = condenser.max_size;\n }\n if (agentSettings.mcp_config) {\n merged.mcp_config = agentSettings.mcp_config as Settings[\"mcp_config\"];\n }\n\n if (typeof conversationSettings.confirmation_mode === \"boolean\") {\n merged.confirmation_mode = conversationSettings.confirmation_mode;\n }\n if (\n typeof conversationSettings.security_analyzer === \"string\" ||\n conversationSettings.security_analyzer === null\n ) {\n merged.security_analyzer = conversationSettings.security_analyzer as\n | string\n | null;\n }\n if (typeof conversationSettings.max_iterations === \"number\") {\n merged.max_iterations = conversationSettings.max_iterations;\n }\n\n merged.search_api_key_set = !!merged.search_api_key;\n\n return merged;\n};\n\nclass SettingsService {\n /**\n * Fetch settings from the agent server API with retry logic.\n *\n * @param exposeSecrets - Controls how secrets are returned:\n * - undefined: Secrets are redacted (\"**********\") - safe for display\n * - \"encrypted\": Secrets are cipher-encrypted - safe for round-trip to start conversation\n * - \"plaintext\": Raw secrets - DO NOT USE from frontend\n */\n static async fetchSettingsFromApi(\n exposeSecrets?: ExposeSecretsMode,\n ): Promise<SettingsApiResponse> {\n return withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).getSettings({\n exposeSecrets,\n }),\n ) as Promise<SettingsApiResponse>;\n }\n\n /**\n * Get settings for display (secrets are redacted).\n * Uses in-memory cache for performance.\n */\n static async getSettings(): Promise<Settings> {\n // Cloud uses a different settings shape (flat top-level fields\n // including provider_tokens_set, llm_model, etc.). Branch out before\n // touching the local-only cache: cloud responses bypass the local\n // SettingsApiResponse shape and feed straight into syncDerivedSettings\n // so cloud-native fields like provider_tokens_set reach the GUI's\n // useUserProviders → useAppInstallations → useGitRepositories chain.\n if (getActiveBackend().backend.kind === \"cloud\") {\n try {\n const cloud = await withRetry(() => fetchCloudSettings());\n return syncDerivedSettings(cloud);\n } catch (error) {\n console.warn(\"Failed to fetch cloud settings, using defaults:\", error);\n return syncDerivedSettings({});\n }\n }\n\n // Check cache first\n if (isCacheValid() && settingsCache.redacted) {\n return syncDerivedSettings(transformApiResponse(settingsCache.redacted));\n }\n\n try {\n const response = await this.fetchSettingsFromApi();\n settingsCache.redacted = response;\n settingsCache.timestamp = Date.now();\n return syncDerivedSettings(transformApiResponse(response));\n } catch (error) {\n // If API fails, return defaults\n console.warn(\"Failed to fetch settings from API, using defaults:\", error);\n return syncDerivedSettings({});\n }\n }\n\n /**\n * Get settings with encrypted secrets for starting conversations.\n * The encrypted secrets can be passed to the start conversation API\n * with secrets_encrypted=true for server-side decryption.\n *\n * @throws Error if encrypted settings cannot be fetched - conversations\n * should not start with broken/redacted credentials.\n */\n static async getSettingsForConversation(): Promise<{\n agentSettings: Record<string, SettingsValue>;\n conversationSettings: Record<string, SettingsValue>;\n secretsEncrypted: boolean;\n }> {\n // Check cache first\n if (isCacheValid() && settingsCache.encrypted) {\n return {\n agentSettings: settingsCache.encrypted.agent_settings,\n conversationSettings: settingsCache.encrypted.conversation_settings,\n secretsEncrypted: true,\n };\n }\n\n // Fetch encrypted settings - this MUST succeed for conversations to work.\n // Do not fall back to redacted settings as that would cause auth failures.\n const response = await this.fetchSettingsFromApi(\"encrypted\");\n settingsCache.encrypted = response;\n if (!settingsCache.timestamp) {\n settingsCache.timestamp = Date.now();\n }\n return {\n agentSettings: response.agent_settings,\n conversationSettings: response.conversation_settings,\n secretsEncrypted: true,\n };\n }\n\n static async getSettingsSchema(): Promise<SettingsSchema> {\n if (getActiveBackend().backend.kind === \"cloud\") {\n return (await fetchCloudSettingsSchema()) as SettingsSchema;\n }\n return (await new SettingsClient(\n getAgentServerClientOptions(),\n ).getAgentSchema()) as SettingsSchema;\n }\n\n static async getConversationSettingsSchema(): Promise<SettingsSchema> {\n if (getActiveBackend().backend.kind === \"cloud\") {\n return (await fetchCloudConversationSettingsSchema()) as SettingsSchema;\n }\n return (await new SettingsClient(\n getAgentServerClientOptions(),\n ).getConversationSchema()) as SettingsSchema;\n }\n\n /**\n * Save settings to the agent server API.\n * Uses PATCH for incremental updates.\n */\n static async saveSettings(\n settings: Partial<Settings> & Record<string, unknown>,\n ): Promise<boolean> {\n // Split app-level user-preference fields (language, git identity, sound\n // notifications, analytics consent) off before building the diff payload.\n // The local agent-server's PATCH /api/settings has no schema for these\n // and Pydantic would drop them silently; persist them in localStorage\n // instead, and forward them as flat top-level keys to the cloud POST.\n const { extracted: appPreferences, rest } = extractAppPreferences(\n settings as Record<string, unknown>,\n );\n const hasAppPreferences = Object.keys(appPreferences).length > 0;\n if (hasAppPreferences) {\n writeStoredAppPreferences(appPreferences);\n }\n\n const payload: SettingsUpdateRequest = {};\n\n // Extract agent_settings_diff\n const agentSettingsDiff = rest.agent_settings_diff as\n | Record<string, SettingsValue>\n | undefined;\n if (agentSettingsDiff && Object.keys(agentSettingsDiff).length > 0) {\n payload.agent_settings_diff = agentSettingsDiff;\n }\n\n // Extract conversation_settings_diff\n const conversationSettingsDiff = rest.conversation_settings_diff as\n | Record<string, SettingsValue>\n | undefined;\n if (\n conversationSettingsDiff &&\n Object.keys(conversationSettingsDiff).length > 0\n ) {\n payload.conversation_settings_diff = conversationSettingsDiff;\n }\n\n // Extract disabled_skills (cloud-only — local agent-server has no skills concept)\n const disabledSkills = rest.disabled_skills as string[] | undefined;\n if (Array.isArray(disabledSkills)) {\n payload.disabled_skills = disabledSkills;\n }\n\n const isCloud = getActiveBackend().backend.kind === \"cloud\";\n\n // The backend applies ``agent_settings_diff`` by deep-merging it into the\n // existing ``agent_settings`` dict (see SDK\n // ``openhands.agent_server.persistence.models._deep_merge``). That works\n // for scalar fields but is wrong for ``mcp_config.mcpServers``, which is\n // a name-keyed map: a diff that omits a server cannot remove it (stale\n // key stays), and a diff whose key indices shift (e.g. after deleting\n // index 0, the second server is renumbered) leaves the original keys\n // behind as duplicates pointing to the wrong server config.\n //\n // The only way to make ``mcp_config`` behave like a replace through this\n // API is to first null it out — ``null`` is not a dict, so deep-merge\n // takes the else branch and sets the field to ``None`` outright — and\n // then send the new value in a follow-up call. We do this for every\n // ``mcp_config`` write, including adds (the wasted round-trip is\n // negligible for this user action and avoids divergent code paths).\n const agentDiff = payload.agent_settings_diff;\n // Send a pre-clear PATCH when the diff sets ``mcp_config`` to a non-null\n // value. A second PATCH below then writes the new value. Skipping the\n // pre-clear when the caller is already clearing (``mcp_config: null``)\n // avoids a pointless duplicate request.\n const needsMcpPreClear =\n !!agentDiff && \"mcp_config\" in agentDiff && agentDiff.mcp_config !== null;\n\n // The pre-clear is destructive: if the follow-up write fails after the\n // clear succeeds, the user's MCP config is left empty. Snapshot the\n // previous value (in raw SDK shape, NOT the GUI's parsed MCPConfig)\n // before pre-clearing so we can attempt a best-effort rollback. The\n // original write error is always re-thrown to the caller regardless\n // of rollback success — the GUI's react-query mutations surface that\n // as an error toast so the user knows to retry.\n //\n // Snapshot must be the SDK shape (``{ mcpServers: { name: cfg }}``)\n // because that is what the backend expects on the rollback PATCH.\n // ``SettingsService.getSettings`` returns a GUI Settings object whose\n // ``mcp_config`` is typed as the parsed frontend MCPConfig and\n // defaults to empty arrays when nothing is installed, so it is not\n // suitable for round-tripping back to the backend.\n let mcpConfigSnapshot: unknown = undefined;\n if (needsMcpPreClear) {\n try {\n if (isCloud) {\n const raw = (await fetchCloudSettings()) as {\n agent_settings?: { mcp_config?: unknown };\n };\n mcpConfigSnapshot = raw?.agent_settings?.mcp_config;\n } else {\n const raw = (await SettingsService.fetchSettingsFromApi()) as {\n agent_settings?: { mcp_config?: unknown };\n };\n mcpConfigSnapshot = raw?.agent_settings?.mcp_config;\n }\n } catch {\n // Snapshot failed (network blip, etc.). Continue without rollback\n // ability — the original write error will still surface.\n }\n }\n\n if (isCloud) {\n const hasCloudWork =\n !!payload.agent_settings_diff ||\n !!payload.conversation_settings_diff ||\n payload.disabled_skills !== undefined ||\n hasAppPreferences;\n if (!hasCloudWork) {\n return true;\n }\n if (needsMcpPreClear) {\n await withRetry(() =>\n saveCloudSettings({\n agent_settings_diff: { mcp_config: null },\n }),\n );\n }\n try {\n await withRetry(() =>\n saveCloudSettings({\n ...payload,\n ...(hasAppPreferences ? { app_preferences: appPreferences } : {}),\n }),\n );\n } catch (err) {\n if (needsMcpPreClear && mcpConfigSnapshot) {\n // Best-effort rollback. We deliberately do not wrap in withRetry:\n // the user's session is already in a degraded state and we want\n // to surface the original error promptly. Swallowing the restore\n // error preserves the original failure context for the caller.\n try {\n await saveCloudSettings({\n agent_settings_diff: {\n mcp_config: mcpConfigSnapshot as SettingsValue,\n },\n });\n } catch {\n // Rollback failed; the original error takes precedence.\n }\n }\n throw err;\n }\n } else {\n // The local agent-server PATCH /api/settings rejects unknown fields and\n // requires at least one of the two diff fields. Strip disabled_skills\n // and skip the request entirely if no diffs remain. App preferences\n // are persisted to localStorage above and never sent to this endpoint.\n if (Array.isArray(disabledSkills)) {\n writeStoredDisabledSkills(disabledSkills);\n }\n const localPayload = { ...payload };\n delete localPayload.disabled_skills;\n const hasLocalDiffs =\n !!localPayload.agent_settings_diff ||\n !!localPayload.conversation_settings_diff;\n if (!hasLocalDiffs) {\n if (hasAppPreferences) {\n // The localStorage write changed user-visible settings; clear the\n // in-memory cache so the next getSettings() re-derives from disk.\n clearCache();\n }\n return true;\n }\n if (needsMcpPreClear) {\n await withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).updateSettings({\n agent_settings_diff: { mcp_config: null },\n }),\n );\n }\n try {\n await withRetry(() =>\n new SettingsClient(getAgentServerClientOptions()).updateSettings(\n localPayload,\n ),\n );\n } catch (err) {\n if (needsMcpPreClear && mcpConfigSnapshot) {\n // See cloud branch above for rationale.\n try {\n await new SettingsClient(\n getAgentServerClientOptions(),\n ).updateSettings({\n agent_settings_diff: {\n mcp_config: mcpConfigSnapshot as SettingsValue,\n },\n });\n } catch {\n // Rollback failed; the original error takes precedence.\n }\n }\n throw err;\n }\n }\n\n // Invalidate cache after successful save\n clearCache();\n\n return true;\n }\n\n /**\n * Invalidate the settings cache.\n * Call this when settings may have changed externally.\n */\n static invalidateCache(): void {\n clearCache();\n }\n}\n\nexport default SettingsService;\n"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- import type { AutomationRun } from "#/types/automation";
1
+ import { type AutomationRun } from "#/types/automation";
2
2
  interface ActivityLogItemProps {
3
3
  run: AutomationRun;
4
4
  }
@@ -3,5 +3,5 @@ interface RecommendedAutomationsLauncherProps {
3
3
  onLaunched?: () => void;
4
4
  }
5
5
  export declare function buildAutomationPrompt(basePrompt: string, backendKind: "local" | "cloud", backendHost?: string): string;
6
- export declare function RecommendedAutomationsLauncher({ query, onLaunched, }: RecommendedAutomationsLauncherProps): import("react/jsx-runtime").JSX.Element;
6
+ export declare function RecommendedAutomationsLauncher({ query, onLaunched, }: RecommendedAutomationsLauncherProps): import("react/jsx-runtime").JSX.Element | null;
7
7
  export {};
@@ -1,2 +1,2 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../types/agent-state.cjs`),i=require(`../../../utils/utils.cjs`),a=require(`../../../stores/conversation-store.cjs`),o=require(`../../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs`),s=require(`../../../hooks/query/use-active-conversation.cjs`),c=require(`../../../hooks/use-agent-state.cjs`),l=require(`../../../hooks/use-unified-websocket-status.cjs`),u=require(`../../../hooks/query/use-sub-conversation-task-polling.cjs`),d=require(`../../../ui/typography.cjs`),f=require(`../../../ui/combobox-caret.cjs`),p=require(`../../../icons/lesson-plan.cjs`),m=require(`../../../icons/code-pill.cjs`),h=require(`./change-agent-context-menu.cjs`),g=require(`../../../hooks/use-handle-plan-click.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function y(){let[e,y]=(0,_.useState)(!1),{conversationMode:b,setConversationMode:x,subConversationTaskId:S}=a.useConversationStore(),C=l.useUnifiedWebSocketStatus()===`OPEN`,{curAgentState:w}=c.useAgentState(),{t:T}=t.useTranslation(`openhands`),E=w===r.AgentState.RUNNING,{data:D}=s.useActiveConversation(),O=o.useQueryClient(),k=(0,_.useRef)(null),{taskStatus:A,subConversationId:j}=u.useSubConversationTaskPolling(S,D?.id||null);(0,_.useEffect)(()=>{A===`READY`&&j&&D?.id&&S&&k.current!==S&&(k.current=S,O.invalidateQueries({queryKey:[`user`,`conversation`,D.id]}))},[A,j,D?.id,S,O]);let{handlePlanClick:M,isCreatingConversation:N}=g.useHandlePlanClick();(0,_.useEffect)(()=>{(E||!C)&&e&&y(!1)},[E,e,C]);let P=E||N||!C;(0,_.useEffect)(()=>{if(P)return;let e=e=>{if(e.shiftKey&&e.key===`Tab`){e.preventDefault(),e.stopPropagation();let t=b===`code`?`plan`:`code`;t===`plan`?M(e):x(t)}};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[P,b,x,M]);let F=t=>{t.preventDefault(),t.stopPropagation(),y(!e)},I=e=>{e.preventDefault(),e.stopPropagation(),x(`code`)},L=b===`code`,R=(0,_.useMemo)(()=>T(L?n.I18nKey.COMMON$CODE:n.I18nKey.COMMON$PLAN),[L,T]),z=(0,_.useMemo)(()=>L?(0,v.jsx)(m.CodePillIcon,{className:`h-[11px] w-[11px] shrink-0`}):(0,v.jsx)(p.default,{width:18,height:18,color:`currentColor`}),[L]);return(0,v.jsxs)(`div`,{className:`relative`,children:[(0,v.jsxs)(`button`,{type:`button`,onClick:F,disabled:P,className:i.cn(`flex items-center rounded-[100px] transition-[border-color,color,opacity]`,L?`border border-transparent text-[var(--oh-muted)]`:`border border-[#597FF4] bg-[#4A67BD]`,!P&&L&&`cursor-pointer hover:text-white hover:bg-white/10`,!P&&!L&&`cursor-pointer text-white hover:bg-white/10`,P&&i.cn(`opacity-50 cursor-not-allowed`,L&&`border-transparent`)),children:[(0,v.jsxs)(`div`,{className:`flex items-center gap-1 pl-1.5`,children:[z,(0,v.jsx)(d.Typography.Text,{className:`text-2.75 not-italic font-normal leading-5`,children:R})]}),(0,v.jsx)(f.ComboboxCaretInline,{isOpen:e})]}),e&&(0,v.jsx)(h.ChangeAgentContextMenu,{onClose:()=>y(!1),onCodeClick:I,onPlanClick:M})]})}exports.ChangeAgentButton=y;
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../types/agent-state.cjs`),i=require(`../../../utils/utils.cjs`),a=require(`../../../hooks/use-conversation-id.cjs`),o=require(`../../../stores/conversation-store.cjs`),s=require(`../../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs`),c=require(`../../../hooks/query/use-active-conversation.cjs`),l=require(`../../../hooks/use-agent-state.cjs`),u=require(`../../shared/buttons/styled-tooltip.cjs`),d=require(`../../../hooks/use-unified-websocket-status.cjs`),f=require(`../../../hooks/query/use-sub-conversation-task-polling.cjs`),p=require(`../../../ui/typography.cjs`),m=require(`../../../ui/combobox-caret.cjs`),h=require(`../../../icons/lesson-plan.cjs`),g=require(`../../../icons/code-pill.cjs`),_=require(`./change-agent-context-menu.cjs`),v=require(`../../../hooks/use-handle-plan-click.cjs`);let y=require(`react`);y=e.__toESM(y,1);let b=require(`react/jsx-runtime`);function x(){let[e,x]=(0,y.useState)(!1),{conversationMode:S,setConversationMode:C,subConversationTaskId:w}=o.useConversationStore(),{conversationId:T}=a.useOptionalConversationId(),E=!T,D=d.useUnifiedWebSocketStatus()===`OPEN`,{curAgentState:O}=l.useAgentState(),{t:k}=t.useTranslation(`openhands`),A=O===r.AgentState.RUNNING,{data:j}=c.useActiveConversation(),M=s.useQueryClient(),N=(0,y.useRef)(null),{taskStatus:P,subConversationId:F}=f.useSubConversationTaskPolling(w,j?.id||null);(0,y.useEffect)(()=>{P===`READY`&&F&&j?.id&&w&&N.current!==w&&(N.current=w,M.invalidateQueries({queryKey:[`user`,`conversation`,j.id]}))},[P,F,j?.id,w,M]);let{handlePlanClick:I,isCreatingConversation:L}=v.useHandlePlanClick();(0,y.useEffect)(()=>{(A||!D)&&e&&x(!1)},[A,e,D]);let R=E||A||L||!D;(0,y.useEffect)(()=>{if(R)return;let e=e=>{if(e.shiftKey&&e.key===`Tab`){e.preventDefault(),e.stopPropagation();let t=S===`code`?`plan`:`code`;t===`plan`?I(e):C(t)}};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[R,S,C,I]);let z=t=>{t.preventDefault(),t.stopPropagation(),x(!e)},B=e=>{e.preventDefault(),e.stopPropagation(),C(`code`)},V=S===`code`,H=(0,y.useMemo)(()=>k(V?n.I18nKey.COMMON$CODE:n.I18nKey.COMMON$PLAN),[V,k]),U=(0,y.useMemo)(()=>V?(0,b.jsx)(g.CodePillIcon,{className:`h-[11px] w-[11px] shrink-0`}):(0,b.jsx)(h.default,{width:18,height:18,color:`currentColor`}),[V]),W=(0,b.jsxs)(`div`,{className:`relative`,children:[(0,b.jsxs)(`button`,{type:`button`,onClick:z,disabled:R,className:i.cn(`flex items-center rounded-[100px] transition-[border-color,color,opacity]`,V?`border border-transparent text-[var(--oh-muted)]`:`border border-[#597FF4] bg-[#4A67BD]`,!R&&V&&`cursor-pointer hover:text-white hover:bg-white/10`,!R&&!V&&`cursor-pointer text-white hover:bg-white/10`,R&&i.cn(`opacity-50 cursor-not-allowed`,V&&`border-transparent`)),children:[(0,b.jsxs)(`div`,{className:`flex items-center gap-1 pl-1.5`,children:[U,(0,b.jsx)(p.Typography.Text,{className:`text-2.75 not-italic font-normal leading-5`,children:H})]}),(0,b.jsx)(m.ComboboxCaretInline,{isOpen:e})]}),e&&(0,b.jsx)(_.ChangeAgentContextMenu,{onClose:()=>x(!1),onCodeClick:B,onPlanClick:I})]});return E?(0,b.jsx)(u.StyledTooltip,{content:k(n.I18nKey.CHANGE_AGENT$SWITCH_AFTER_CONVERSATION),placement:`top`,children:W}):W}exports.ChangeAgentButton=x;
2
2
  //# sourceMappingURL=change-agent-button.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"change-agent-button.cjs","names":[],"sources":["../../../../src/components/features/chat/change-agent-button.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Typography } from \"#/ui/typography\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ComboboxCaretInline } from \"#/ui/combobox-caret\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { ChangeAgentContextMenu } from \"./change-agent-context-menu\";\nimport { cn } from \"#/utils/utils\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useSubConversationTaskPolling } from \"#/hooks/query/use-sub-conversation-task-polling\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\n\nexport function ChangeAgentButton() {\n const [contextMenuOpen, setContextMenuOpen] = useState<boolean>(false);\n\n const { conversationMode, setConversationMode, subConversationTaskId } =\n useConversationStore();\n\n const webSocketStatus = useUnifiedWebSocketStatus();\n\n const isWebSocketConnected = webSocketStatus === \"OPEN\";\n\n const { curAgentState } = useAgentState();\n\n const { t } = useTranslation(\"openhands\");\n\n const isAgentRunning = curAgentState === AgentState.RUNNING;\n\n const { data: conversation } = useActiveConversation();\n\n const queryClient = useQueryClient();\n\n // Track the last invalidated task ID to prevent duplicate invalidations\n const lastInvalidatedTaskIdRef = useRef<string | null>(null);\n\n // Poll sub-conversation task status\n const { taskStatus, subConversationId } = useSubConversationTaskPolling(\n subConversationTaskId,\n conversation?.id || null,\n );\n\n // Invalidate parent conversation cache when task is ready (only once per task)\n useEffect(() => {\n if (\n taskStatus === \"READY\" &&\n subConversationId &&\n conversation?.id &&\n subConversationTaskId &&\n lastInvalidatedTaskIdRef.current !== subConversationTaskId\n ) {\n // Mark this task as invalidated to prevent duplicate calls\n lastInvalidatedTaskIdRef.current = subConversationTaskId;\n // Invalidate the parent conversation to refetch with updated sub_conversation_ids\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversation\", conversation.id],\n });\n }\n }, [\n taskStatus,\n subConversationId,\n conversation?.id,\n subConversationTaskId,\n queryClient,\n ]);\n\n // Get handlePlanClick and isCreatingConversation from custom hook\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n // Close context menu when agent starts running\n useEffect(() => {\n if ((isAgentRunning || !isWebSocketConnected) && contextMenuOpen) {\n setContextMenuOpen(false);\n }\n }, [isAgentRunning, contextMenuOpen, isWebSocketConnected]);\n\n const isButtonDisabled =\n isAgentRunning || isCreatingConversation || !isWebSocketConnected;\n\n // Handle Shift + Tab keyboard shortcut to cycle through modes\n useEffect(() => {\n if (isButtonDisabled) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Shift + Tab combination\n if (event.shiftKey && event.key === \"Tab\") {\n // Prevent default tab navigation behavior\n event.preventDefault();\n event.stopPropagation();\n\n // Cycle between modes: code -> plan -> code\n const nextMode = conversationMode === \"code\" ? \"plan\" : \"code\";\n if (nextMode === \"plan\") {\n handlePlanClick(event);\n } else {\n setConversationMode(nextMode);\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isButtonDisabled,\n conversationMode,\n setConversationMode,\n handlePlanClick,\n ]);\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setContextMenuOpen(!contextMenuOpen);\n };\n\n const handleCodeClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n };\n\n const isExecutionAgent = conversationMode === \"code\";\n\n const buttonLabel = useMemo(() => {\n if (isExecutionAgent) {\n return t(I18nKey.COMMON$CODE);\n }\n return t(I18nKey.COMMON$PLAN);\n }, [isExecutionAgent, t]);\n\n const buttonIcon = useMemo(() => {\n if (isExecutionAgent) {\n return <CodePillIcon className=\"h-[11px] w-[11px] shrink-0\" />;\n }\n return <LessonPlanIcon width={18} height={18} color=\"currentColor\" />;\n }, [isExecutionAgent]);\n\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleButtonClick}\n disabled={isButtonDisabled}\n className={cn(\n \"flex items-center rounded-[100px] transition-[border-color,color,opacity]\",\n isExecutionAgent\n ? \"border border-transparent text-[var(--oh-muted)]\"\n : \"border border-[#597FF4] bg-[#4A67BD]\",\n !isButtonDisabled &&\n isExecutionAgent &&\n \"cursor-pointer hover:text-white hover:bg-white/10\",\n !isButtonDisabled &&\n !isExecutionAgent &&\n \"cursor-pointer text-white hover:bg-white/10\",\n isButtonDisabled &&\n cn(\n \"opacity-50 cursor-not-allowed\",\n isExecutionAgent && \"border-transparent\",\n ),\n )}\n >\n <div className=\"flex items-center gap-1 pl-1.5\">\n {buttonIcon}\n <Typography.Text className=\"text-2.75 not-italic font-normal leading-5\">\n {buttonLabel}\n </Typography.Text>\n </div>\n <ComboboxCaretInline isOpen={contextMenuOpen} />\n </button>\n {contextMenuOpen && (\n <ChangeAgentContextMenu\n onClose={() => setContextMenuOpen(false)}\n onCodeClick={handleCodeClick}\n onPlanClick={handlePlanClick}\n />\n )}\n </div>\n );\n}\n"],"mappings":"i+BAkBA,SAAgB,GAAoB,CAClC,GAAM,CAAC,EAAiB,IAAA,EAAA,EAAA,UAAwC,GAAM,CAEhE,CAAE,mBAAkB,sBAAqB,yBAC7C,EAAA,sBAAsB,CAIlB,EAFkB,EAAA,2BAEK,GAAoB,OAE3C,CAAE,iBAAkB,EAAA,eAAe,CAEnC,CAAE,KAAM,EAAA,eAAe,YAAY,CAEnC,EAAiB,IAAkB,EAAA,WAAW,QAE9C,CAAE,KAAM,GAAiB,EAAA,uBAAuB,CAEhD,EAAc,EAAA,gBAAgB,CAG9B,GAAA,EAAA,EAAA,QAAiD,KAAK,CAGtD,CAAE,aAAY,qBAAsB,EAAA,8BACxC,EACA,GAAc,IAAM,KACrB,EAGD,EAAA,EAAA,eAAgB,CAEZ,IAAe,SACf,GACA,GAAc,IACd,GACA,EAAyB,UAAY,IAGrC,EAAyB,QAAU,EAEnC,EAAY,kBAAkB,CAC5B,SAAU,CAAC,OAAQ,eAAgB,EAAa,GAAG,CACpD,CAAC,GAEH,CACD,EACA,EACA,GAAc,GACd,EACA,EACD,CAAC,CAGF,GAAM,CAAE,kBAAiB,0BAA2B,EAAA,oBAAoB,EAGxE,EAAA,EAAA,eAAgB,EACT,GAAkB,CAAC,IAAyB,GAC/C,EAAmB,GAAM,EAE1B,CAAC,EAAgB,EAAiB,EAAqB,CAAC,CAE3D,IAAM,EACJ,GAAkB,GAA0B,CAAC,GAG/C,EAAA,EAAA,eAAgB,CACd,GAAI,EACF,OAGF,IAAM,EAAiB,GAAyB,CAE9C,GAAI,EAAM,UAAY,EAAM,MAAQ,MAAO,CAEzC,EAAM,gBAAgB,CACtB,EAAM,iBAAiB,CAGvB,IAAM,EAAW,IAAqB,OAAS,OAAS,OACpD,IAAa,OACf,EAAgB,EAAM,CAEtB,EAAoB,EAAS,GAOnC,OAFA,SAAS,iBAAiB,UAAW,EAAc,KAEtC,CACX,SAAS,oBAAoB,UAAW,EAAc,GAEvD,CACD,EACA,EACA,EACA,EACD,CAAC,CAEF,IAAM,EAAqB,GAA+C,CACxE,EAAM,gBAAgB,CACtB,EAAM,iBAAiB,CACvB,EAAmB,CAAC,EAAgB,EAGhC,EAAmB,GAA+C,CACtE,EAAM,gBAAgB,CACtB,EAAM,iBAAiB,CACvB,EAAoB,OAAO,EAGvB,EAAmB,IAAqB,OAExC,GAAA,EAAA,EAAA,aAEK,EADL,EACO,EAAA,QAAQ,YAEV,EAAA,QAAQ,YAFc,CAG9B,CAAC,EAAkB,EAAE,CAAC,CAEnB,GAAA,EAAA,EAAA,aACA,GACK,EAAA,EAAA,KAAC,EAAA,aAAD,CAAc,UAAU,6BAA+B,CAAA,EAEzD,EAAA,EAAA,KAAC,EAAA,QAAD,CAAgB,MAAO,GAAI,OAAQ,GAAI,MAAM,eAAiB,CAAA,CACpE,CAAC,EAAiB,CAAC,CAEtB,OACE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,oBAAf,EACE,EAAA,EAAA,MAAC,SAAD,CACE,KAAK,SACL,QAAS,EACT,SAAU,EACV,UAAW,EAAA,GACT,4EACA,EACI,mDACA,uCACJ,CAAC,GACC,GACA,oDACF,CAAC,GACC,CAAC,GACD,8CACF,GACE,EAAA,GACE,gCACA,GAAoB,qBACrB,CACJ,UApBH,EAsBE,EAAA,EAAA,MAAC,MAAD,CAAK,UAAU,0CAAf,CACG,GACD,EAAA,EAAA,KAAC,EAAA,WAAW,KAAZ,CAAiB,UAAU,sDACxB,EACe,CAAA,CACd,IACN,EAAA,EAAA,KAAC,EAAA,oBAAD,CAAqB,OAAQ,EAAmB,CAAA,CACzC,GACR,IACC,EAAA,EAAA,KAAC,EAAA,uBAAD,CACE,YAAe,EAAmB,GAAM,CACxC,YAAa,EACb,YAAa,EACb,CAAA,CAEA"}
1
+ {"version":3,"file":"change-agent-button.cjs","names":[],"sources":["../../../../src/components/features/chat/change-agent-button.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Typography } from \"#/ui/typography\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ComboboxCaretInline } from \"#/ui/combobox-caret\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { ChangeAgentContextMenu } from \"./change-agent-context-menu\";\nimport { cn } from \"#/utils/utils\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useSubConversationTaskPolling } from \"#/hooks/query/use-sub-conversation-task-polling\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\n\nexport function ChangeAgentButton() {\n const [contextMenuOpen, setContextMenuOpen] = useState<boolean>(false);\n\n const { conversationMode, setConversationMode, subConversationTaskId } =\n useConversationStore();\n\n const { conversationId } = useOptionalConversationId();\n\n const isHomePage = !conversationId;\n\n const webSocketStatus = useUnifiedWebSocketStatus();\n\n const isWebSocketConnected = webSocketStatus === \"OPEN\";\n\n const { curAgentState } = useAgentState();\n\n const { t } = useTranslation(\"openhands\");\n\n const isAgentRunning = curAgentState === AgentState.RUNNING;\n\n const { data: conversation } = useActiveConversation();\n\n const queryClient = useQueryClient();\n\n // Track the last invalidated task ID to prevent duplicate invalidations\n const lastInvalidatedTaskIdRef = useRef<string | null>(null);\n\n // Poll sub-conversation task status\n const { taskStatus, subConversationId } = useSubConversationTaskPolling(\n subConversationTaskId,\n conversation?.id || null,\n );\n\n // Invalidate parent conversation cache when task is ready (only once per task)\n useEffect(() => {\n if (\n taskStatus === \"READY\" &&\n subConversationId &&\n conversation?.id &&\n subConversationTaskId &&\n lastInvalidatedTaskIdRef.current !== subConversationTaskId\n ) {\n // Mark this task as invalidated to prevent duplicate calls\n lastInvalidatedTaskIdRef.current = subConversationTaskId;\n // Invalidate the parent conversation to refetch with updated sub_conversation_ids\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversation\", conversation.id],\n });\n }\n }, [\n taskStatus,\n subConversationId,\n conversation?.id,\n subConversationTaskId,\n queryClient,\n ]);\n\n // Get handlePlanClick and isCreatingConversation from custom hook\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n // Close context menu when agent starts running\n useEffect(() => {\n if ((isAgentRunning || !isWebSocketConnected) && contextMenuOpen) {\n setContextMenuOpen(false);\n }\n }, [isAgentRunning, contextMenuOpen, isWebSocketConnected]);\n\n const isButtonDisabled =\n isHomePage ||\n isAgentRunning ||\n isCreatingConversation ||\n !isWebSocketConnected;\n\n // Handle Shift + Tab keyboard shortcut to cycle through modes\n useEffect(() => {\n if (isButtonDisabled) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Shift + Tab combination\n if (event.shiftKey && event.key === \"Tab\") {\n // Prevent default tab navigation behavior\n event.preventDefault();\n event.stopPropagation();\n\n // Cycle between modes: code -> plan -> code\n const nextMode = conversationMode === \"code\" ? \"plan\" : \"code\";\n if (nextMode === \"plan\") {\n handlePlanClick(event);\n } else {\n setConversationMode(nextMode);\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isButtonDisabled,\n conversationMode,\n setConversationMode,\n handlePlanClick,\n ]);\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setContextMenuOpen(!contextMenuOpen);\n };\n\n const handleCodeClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n };\n\n const isExecutionAgent = conversationMode === \"code\";\n\n const buttonLabel = useMemo(() => {\n if (isExecutionAgent) {\n return t(I18nKey.COMMON$CODE);\n }\n return t(I18nKey.COMMON$PLAN);\n }, [isExecutionAgent, t]);\n\n const buttonIcon = useMemo(() => {\n if (isExecutionAgent) {\n return <CodePillIcon className=\"h-[11px] w-[11px] shrink-0\" />;\n }\n return <LessonPlanIcon width={18} height={18} color=\"currentColor\" />;\n }, [isExecutionAgent]);\n\n const button = (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleButtonClick}\n disabled={isButtonDisabled}\n className={cn(\n \"flex items-center rounded-[100px] transition-[border-color,color,opacity]\",\n isExecutionAgent\n ? \"border border-transparent text-[var(--oh-muted)]\"\n : \"border border-[#597FF4] bg-[#4A67BD]\",\n !isButtonDisabled &&\n isExecutionAgent &&\n \"cursor-pointer hover:text-white hover:bg-white/10\",\n !isButtonDisabled &&\n !isExecutionAgent &&\n \"cursor-pointer text-white hover:bg-white/10\",\n isButtonDisabled &&\n cn(\n \"opacity-50 cursor-not-allowed\",\n isExecutionAgent && \"border-transparent\",\n ),\n )}\n >\n <div className=\"flex items-center gap-1 pl-1.5\">\n {buttonIcon}\n <Typography.Text className=\"text-2.75 not-italic font-normal leading-5\">\n {buttonLabel}\n </Typography.Text>\n </div>\n <ComboboxCaretInline isOpen={contextMenuOpen} />\n </button>\n {contextMenuOpen && (\n <ChangeAgentContextMenu\n onClose={() => setContextMenuOpen(false)}\n onCodeClick={handleCodeClick}\n onPlanClick={handlePlanClick}\n />\n )}\n </div>\n );\n\n if (isHomePage) {\n return (\n <StyledTooltip\n content={t(I18nKey.CHANGE_AGENT$SWITCH_AFTER_CONVERSATION)}\n placement=\"top\"\n >\n {button}\n </StyledTooltip>\n );\n }\n\n return button;\n}\n"],"mappings":"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"}
@@ -2,94 +2,100 @@ import { useTranslation as e } from "../../../node_modules/react-i18next/dist/es
2
2
  import { I18nKey as t } from "../../../i18n/declaration.js";
3
3
  import { AgentState as n } from "../../../types/agent-state.js";
4
4
  import { cn as r } from "../../../utils/utils.js";
5
- import { useConversationStore as i } from "../../../stores/conversation-store.js";
6
- import { useQueryClient as a } from "../../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js";
7
- import { useActiveConversation as o } from "../../../hooks/query/use-active-conversation.js";
8
- import { useAgentState as s } from "../../../hooks/use-agent-state.js";
9
- import { useUnifiedWebSocketStatus as c } from "../../../hooks/use-unified-websocket-status.js";
10
- import { useSubConversationTaskPolling as l } from "../../../hooks/query/use-sub-conversation-task-polling.js";
11
- import { Typography as u } from "../../../ui/typography.js";
12
- import { ComboboxCaretInline as d } from "../../../ui/combobox-caret.js";
13
- import f from "../../../icons/lesson-plan.js";
14
- import { CodePillIcon as p } from "../../../icons/code-pill.js";
15
- import { ChangeAgentContextMenu as m } from "./change-agent-context-menu.js";
16
- import { useHandlePlanClick as h } from "../../../hooks/use-handle-plan-click.js";
17
- import { useEffect as g, useMemo as _, useRef as v, useState as y } from "react";
18
- import { jsx as b, jsxs as x } from "react/jsx-runtime";
5
+ import { useOptionalConversationId as i } from "../../../hooks/use-conversation-id.js";
6
+ import { useConversationStore as a } from "../../../stores/conversation-store.js";
7
+ import { useQueryClient as o } from "../../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.js";
8
+ import { useActiveConversation as s } from "../../../hooks/query/use-active-conversation.js";
9
+ import { useAgentState as c } from "../../../hooks/use-agent-state.js";
10
+ import { StyledTooltip as l } from "../../shared/buttons/styled-tooltip.js";
11
+ import { useUnifiedWebSocketStatus as u } from "../../../hooks/use-unified-websocket-status.js";
12
+ import { useSubConversationTaskPolling as d } from "../../../hooks/query/use-sub-conversation-task-polling.js";
13
+ import { Typography as f } from "../../../ui/typography.js";
14
+ import { ComboboxCaretInline as p } from "../../../ui/combobox-caret.js";
15
+ import m from "../../../icons/lesson-plan.js";
16
+ import { CodePillIcon as h } from "../../../icons/code-pill.js";
17
+ import { ChangeAgentContextMenu as g } from "./change-agent-context-menu.js";
18
+ import { useHandlePlanClick as _ } from "../../../hooks/use-handle-plan-click.js";
19
+ import { useEffect as v, useMemo as y, useRef as b, useState as x } from "react";
20
+ import { jsx as S, jsxs as C } from "react/jsx-runtime";
19
21
  //#region src/components/features/chat/change-agent-button.tsx
20
- function S() {
21
- let [S, C] = y(!1), { conversationMode: w, setConversationMode: T, subConversationTaskId: E } = i(), D = c() === "OPEN", { curAgentState: O } = s(), { t: k } = e("openhands"), A = O === n.RUNNING, { data: j } = o(), M = a(), N = v(null), { taskStatus: P, subConversationId: F } = l(E, j?.id || null);
22
- g(() => {
23
- P === "READY" && F && j?.id && E && N.current !== E && (N.current = E, M.invalidateQueries({ queryKey: [
22
+ function w() {
23
+ let [w, T] = x(!1), { conversationMode: E, setConversationMode: D, subConversationTaskId: O } = a(), { conversationId: k } = i(), A = !k, j = u() === "OPEN", { curAgentState: M } = c(), { t: N } = e("openhands"), P = M === n.RUNNING, { data: F } = s(), I = o(), L = b(null), { taskStatus: R, subConversationId: z } = d(O, F?.id || null);
24
+ v(() => {
25
+ R === "READY" && z && F?.id && O && L.current !== O && (L.current = O, I.invalidateQueries({ queryKey: [
24
26
  "user",
25
27
  "conversation",
26
- j.id
28
+ F.id
27
29
  ] }));
28
30
  }, [
29
- P,
30
- F,
31
- j?.id,
32
- E,
33
- M
31
+ R,
32
+ z,
33
+ F?.id,
34
+ O,
35
+ I
34
36
  ]);
35
- let { handlePlanClick: I, isCreatingConversation: L } = h();
36
- g(() => {
37
- (A || !D) && S && C(!1);
37
+ let { handlePlanClick: B, isCreatingConversation: V } = _();
38
+ v(() => {
39
+ (P || !j) && w && T(!1);
38
40
  }, [
39
- A,
40
- S,
41
- D
41
+ P,
42
+ w,
43
+ j
42
44
  ]);
43
- let R = A || L || !D;
44
- g(() => {
45
- if (R) return;
45
+ let H = A || P || V || !j;
46
+ v(() => {
47
+ if (H) return;
46
48
  let e = (e) => {
47
49
  if (e.shiftKey && e.key === "Tab") {
48
50
  e.preventDefault(), e.stopPropagation();
49
- let t = w === "code" ? "plan" : "code";
50
- t === "plan" ? I(e) : T(t);
51
+ let t = E === "code" ? "plan" : "code";
52
+ t === "plan" ? B(e) : D(t);
51
53
  }
52
54
  };
53
55
  return document.addEventListener("keydown", e), () => {
54
56
  document.removeEventListener("keydown", e);
55
57
  };
56
58
  }, [
57
- R,
58
- w,
59
- T,
60
- I
59
+ H,
60
+ E,
61
+ D,
62
+ B
61
63
  ]);
62
- let z = (e) => {
63
- e.preventDefault(), e.stopPropagation(), C(!S);
64
- }, B = (e) => {
65
- e.preventDefault(), e.stopPropagation(), T("code");
66
- }, V = w === "code", H = _(() => k(V ? t.COMMON$CODE : t.COMMON$PLAN), [V, k]), U = _(() => V ? /* @__PURE__ */ b(p, { className: "h-[11px] w-[11px] shrink-0" }) : /* @__PURE__ */ b(f, {
64
+ let U = (e) => {
65
+ e.preventDefault(), e.stopPropagation(), T(!w);
66
+ }, W = (e) => {
67
+ e.preventDefault(), e.stopPropagation(), D("code");
68
+ }, G = E === "code", K = y(() => N(G ? t.COMMON$CODE : t.COMMON$PLAN), [G, N]), q = y(() => G ? /* @__PURE__ */ S(h, { className: "h-[11px] w-[11px] shrink-0" }) : /* @__PURE__ */ S(m, {
67
69
  width: 18,
68
70
  height: 18,
69
71
  color: "currentColor"
70
- }), [V]);
71
- return /* @__PURE__ */ x("div", {
72
+ }), [G]), J = /* @__PURE__ */ C("div", {
72
73
  className: "relative",
73
- children: [/* @__PURE__ */ x("button", {
74
+ children: [/* @__PURE__ */ C("button", {
74
75
  type: "button",
75
- onClick: z,
76
- disabled: R,
77
- className: r("flex items-center rounded-[100px] transition-[border-color,color,opacity]", V ? "border border-transparent text-[var(--oh-muted)]" : "border border-[#597FF4] bg-[#4A67BD]", !R && V && "cursor-pointer hover:text-white hover:bg-white/10", !R && !V && "cursor-pointer text-white hover:bg-white/10", R && r("opacity-50 cursor-not-allowed", V && "border-transparent")),
78
- children: [/* @__PURE__ */ x("div", {
76
+ onClick: U,
77
+ disabled: H,
78
+ className: r("flex items-center rounded-[100px] transition-[border-color,color,opacity]", G ? "border border-transparent text-[var(--oh-muted)]" : "border border-[#597FF4] bg-[#4A67BD]", !H && G && "cursor-pointer hover:text-white hover:bg-white/10", !H && !G && "cursor-pointer text-white hover:bg-white/10", H && r("opacity-50 cursor-not-allowed", G && "border-transparent")),
79
+ children: [/* @__PURE__ */ C("div", {
79
80
  className: "flex items-center gap-1 pl-1.5",
80
- children: [U, /* @__PURE__ */ b(u.Text, {
81
+ children: [q, /* @__PURE__ */ S(f.Text, {
81
82
  className: "text-2.75 not-italic font-normal leading-5",
82
- children: H
83
+ children: K
83
84
  })]
84
- }), /* @__PURE__ */ b(d, { isOpen: S })]
85
- }), S && /* @__PURE__ */ b(m, {
86
- onClose: () => C(!1),
87
- onCodeClick: B,
88
- onPlanClick: I
85
+ }), /* @__PURE__ */ S(p, { isOpen: w })]
86
+ }), w && /* @__PURE__ */ S(g, {
87
+ onClose: () => T(!1),
88
+ onCodeClick: W,
89
+ onPlanClick: B
89
90
  })]
90
91
  });
92
+ return A ? /* @__PURE__ */ S(l, {
93
+ content: N(t.CHANGE_AGENT$SWITCH_AFTER_CONVERSATION),
94
+ placement: "top",
95
+ children: J
96
+ }) : J;
91
97
  }
92
98
  //#endregion
93
- export { S as ChangeAgentButton };
99
+ export { w as ChangeAgentButton };
94
100
 
95
101
  //# sourceMappingURL=change-agent-button.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"change-agent-button.js","names":[],"sources":["../../../../src/components/features/chat/change-agent-button.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Typography } from \"#/ui/typography\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ComboboxCaretInline } from \"#/ui/combobox-caret\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { ChangeAgentContextMenu } from \"./change-agent-context-menu\";\nimport { cn } from \"#/utils/utils\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useSubConversationTaskPolling } from \"#/hooks/query/use-sub-conversation-task-polling\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\n\nexport function ChangeAgentButton() {\n const [contextMenuOpen, setContextMenuOpen] = useState<boolean>(false);\n\n const { conversationMode, setConversationMode, subConversationTaskId } =\n useConversationStore();\n\n const webSocketStatus = useUnifiedWebSocketStatus();\n\n const isWebSocketConnected = webSocketStatus === \"OPEN\";\n\n const { curAgentState } = useAgentState();\n\n const { t } = useTranslation(\"openhands\");\n\n const isAgentRunning = curAgentState === AgentState.RUNNING;\n\n const { data: conversation } = useActiveConversation();\n\n const queryClient = useQueryClient();\n\n // Track the last invalidated task ID to prevent duplicate invalidations\n const lastInvalidatedTaskIdRef = useRef<string | null>(null);\n\n // Poll sub-conversation task status\n const { taskStatus, subConversationId } = useSubConversationTaskPolling(\n subConversationTaskId,\n conversation?.id || null,\n );\n\n // Invalidate parent conversation cache when task is ready (only once per task)\n useEffect(() => {\n if (\n taskStatus === \"READY\" &&\n subConversationId &&\n conversation?.id &&\n subConversationTaskId &&\n lastInvalidatedTaskIdRef.current !== subConversationTaskId\n ) {\n // Mark this task as invalidated to prevent duplicate calls\n lastInvalidatedTaskIdRef.current = subConversationTaskId;\n // Invalidate the parent conversation to refetch with updated sub_conversation_ids\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversation\", conversation.id],\n });\n }\n }, [\n taskStatus,\n subConversationId,\n conversation?.id,\n subConversationTaskId,\n queryClient,\n ]);\n\n // Get handlePlanClick and isCreatingConversation from custom hook\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n // Close context menu when agent starts running\n useEffect(() => {\n if ((isAgentRunning || !isWebSocketConnected) && contextMenuOpen) {\n setContextMenuOpen(false);\n }\n }, [isAgentRunning, contextMenuOpen, isWebSocketConnected]);\n\n const isButtonDisabled =\n isAgentRunning || isCreatingConversation || !isWebSocketConnected;\n\n // Handle Shift + Tab keyboard shortcut to cycle through modes\n useEffect(() => {\n if (isButtonDisabled) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Shift + Tab combination\n if (event.shiftKey && event.key === \"Tab\") {\n // Prevent default tab navigation behavior\n event.preventDefault();\n event.stopPropagation();\n\n // Cycle between modes: code -> plan -> code\n const nextMode = conversationMode === \"code\" ? \"plan\" : \"code\";\n if (nextMode === \"plan\") {\n handlePlanClick(event);\n } else {\n setConversationMode(nextMode);\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isButtonDisabled,\n conversationMode,\n setConversationMode,\n handlePlanClick,\n ]);\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setContextMenuOpen(!contextMenuOpen);\n };\n\n const handleCodeClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n };\n\n const isExecutionAgent = conversationMode === \"code\";\n\n const buttonLabel = useMemo(() => {\n if (isExecutionAgent) {\n return t(I18nKey.COMMON$CODE);\n }\n return t(I18nKey.COMMON$PLAN);\n }, [isExecutionAgent, t]);\n\n const buttonIcon = useMemo(() => {\n if (isExecutionAgent) {\n return <CodePillIcon className=\"h-[11px] w-[11px] shrink-0\" />;\n }\n return <LessonPlanIcon width={18} height={18} color=\"currentColor\" />;\n }, [isExecutionAgent]);\n\n return (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleButtonClick}\n disabled={isButtonDisabled}\n className={cn(\n \"flex items-center rounded-[100px] transition-[border-color,color,opacity]\",\n isExecutionAgent\n ? \"border border-transparent text-[var(--oh-muted)]\"\n : \"border border-[#597FF4] bg-[#4A67BD]\",\n !isButtonDisabled &&\n isExecutionAgent &&\n \"cursor-pointer hover:text-white hover:bg-white/10\",\n !isButtonDisabled &&\n !isExecutionAgent &&\n \"cursor-pointer text-white hover:bg-white/10\",\n isButtonDisabled &&\n cn(\n \"opacity-50 cursor-not-allowed\",\n isExecutionAgent && \"border-transparent\",\n ),\n )}\n >\n <div className=\"flex items-center gap-1 pl-1.5\">\n {buttonIcon}\n <Typography.Text className=\"text-2.75 not-italic font-normal leading-5\">\n {buttonLabel}\n </Typography.Text>\n </div>\n <ComboboxCaretInline isOpen={contextMenuOpen} />\n </button>\n {contextMenuOpen && (\n <ChangeAgentContextMenu\n onClose={() => setContextMenuOpen(false)}\n onCodeClick={handleCodeClick}\n onPlanClick={handlePlanClick}\n />\n )}\n </div>\n );\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;AAkBA,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAiB,KAAsB,EAAkB,GAAM,EAEhE,EAAE,qBAAkB,wBAAqB,6BAC7C,GAAsB,EAIlB,IAFkB,GAEK,KAAoB,QAE3C,EAAE,qBAAkB,GAAe,EAEnC,EAAE,SAAM,EAAe,YAAY,EAEnC,IAAiB,MAAkB,EAAW,SAE9C,EAAE,MAAM,MAAiB,GAAuB,EAEhD,IAAc,GAAgB,EAG9B,IAA2B,EAAsB,KAAK,EAGtD,EAAE,eAAY,yBAAsB,EACxC,GACA,GAAc,MAAM,KACrB;AAGD,SAAgB;AACd,EACE,MAAe,WACf,KACA,GAAc,MACd,KACA,EAAyB,YAAY,MAGrC,EAAyB,UAAU,GAEnC,EAAY,kBAAkB,EAC5B,UAAU;GAAC;GAAQ;GAAgB,EAAa;GAAG,EACpD,CAAC;IAEH;EACD;EACA;EACA,GAAc;EACd;EACA;EACD,CAAC;CAGF,IAAM,EAAE,oBAAiB,8BAA2B,GAAoB;AAGxE,SAAgB;AACd,GAAK,KAAkB,CAAC,MAAyB,KAC/C,EAAmB,GAAM;IAE1B;EAAC;EAAgB;EAAiB;EAAqB,CAAC;CAE3D,IAAM,IACJ,KAAkB,KAA0B,CAAC;AAG/C,SAAgB;AACd,MAAI,EACF;EAGF,IAAM,KAAiB,MAAyB;AAE9C,OAAI,EAAM,YAAY,EAAM,QAAQ,OAAO;AAGzC,IADA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB;IAGvB,IAAM,IAAW,MAAqB,SAAS,SAAS;AACxD,IAAI,MAAa,SACf,EAAgB,EAAM,GAEtB,EAAoB,EAAS;;;AAOnC,SAFA,SAAS,iBAAiB,WAAW,EAAc,QAEtC;AACX,YAAS,oBAAoB,WAAW,EAAc;;IAEvD;EACD;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAM,KAAqB,MAA+C;AAGxE,EAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,EAAmB,CAAC,EAAgB;IAGhC,KAAmB,MAA+C;AAGtE,EAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,EAAoB,OAAO;IAGvB,IAAmB,MAAqB,QAExC,IAAc,QAET,EADL,IACO,EAAQ,cAEV,EAAQ,YAFc,EAG9B,CAAC,GAAkB,EAAE,CAAC,EAEnB,IAAa,QACb,IACK,kBAAC,GAAD,EAAc,WAAU,8BAA+B,CAAA,GAEzD,kBAAC,GAAD;EAAgB,OAAO;EAAI,QAAQ;EAAI,OAAM;EAAiB,CAAA,EACpE,CAAC,EAAiB,CAAC;AAEtB,QACE,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,UAAU;GACV,WAAW,EACT,6EACA,IACI,qDACA,wCACJ,CAAC,KACC,KACA,qDACF,CAAC,KACC,CAAC,KACD,+CACF,KACE,EACE,iCACA,KAAoB,qBACrB,CACJ;aApBH,CAsBE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,GACD,kBAAC,EAAW,MAAZ;KAAiB,WAAU;eACxB;KACe,CAAA,CACd;OACN,kBAAC,GAAD,EAAqB,QAAQ,GAAmB,CAAA,CACzC;MACR,KACC,kBAAC,GAAD;GACE,eAAe,EAAmB,GAAM;GACxC,aAAa;GACb,aAAa;GACb,CAAA,CAEA"}
1
+ {"version":3,"file":"change-agent-button.js","names":[],"sources":["../../../../src/components/features/chat/change-agent-button.tsx"],"sourcesContent":["import React, { useMemo, useEffect, useState, useRef } from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { Typography } from \"#/ui/typography\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { ComboboxCaretInline } from \"#/ui/combobox-caret\";\nimport LessonPlanIcon from \"#/icons/lesson-plan.svg?react\";\nimport { CodePillIcon } from \"#/icons/code-pill\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { ChangeAgentContextMenu } from \"./change-agent-context-menu\";\nimport { cn } from \"#/utils/utils\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { useActiveConversation } from \"#/hooks/query/use-active-conversation\";\nimport { useUnifiedWebSocketStatus } from \"#/hooks/use-unified-websocket-status\";\nimport { useSubConversationTaskPolling } from \"#/hooks/query/use-sub-conversation-task-polling\";\nimport { useHandlePlanClick } from \"#/hooks/use-handle-plan-click\";\nimport { useOptionalConversationId } from \"#/hooks/use-conversation-id\";\nimport { StyledTooltip } from \"#/components/shared/buttons/styled-tooltip\";\n\nexport function ChangeAgentButton() {\n const [contextMenuOpen, setContextMenuOpen] = useState<boolean>(false);\n\n const { conversationMode, setConversationMode, subConversationTaskId } =\n useConversationStore();\n\n const { conversationId } = useOptionalConversationId();\n\n const isHomePage = !conversationId;\n\n const webSocketStatus = useUnifiedWebSocketStatus();\n\n const isWebSocketConnected = webSocketStatus === \"OPEN\";\n\n const { curAgentState } = useAgentState();\n\n const { t } = useTranslation(\"openhands\");\n\n const isAgentRunning = curAgentState === AgentState.RUNNING;\n\n const { data: conversation } = useActiveConversation();\n\n const queryClient = useQueryClient();\n\n // Track the last invalidated task ID to prevent duplicate invalidations\n const lastInvalidatedTaskIdRef = useRef<string | null>(null);\n\n // Poll sub-conversation task status\n const { taskStatus, subConversationId } = useSubConversationTaskPolling(\n subConversationTaskId,\n conversation?.id || null,\n );\n\n // Invalidate parent conversation cache when task is ready (only once per task)\n useEffect(() => {\n if (\n taskStatus === \"READY\" &&\n subConversationId &&\n conversation?.id &&\n subConversationTaskId &&\n lastInvalidatedTaskIdRef.current !== subConversationTaskId\n ) {\n // Mark this task as invalidated to prevent duplicate calls\n lastInvalidatedTaskIdRef.current = subConversationTaskId;\n // Invalidate the parent conversation to refetch with updated sub_conversation_ids\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversation\", conversation.id],\n });\n }\n }, [\n taskStatus,\n subConversationId,\n conversation?.id,\n subConversationTaskId,\n queryClient,\n ]);\n\n // Get handlePlanClick and isCreatingConversation from custom hook\n const { handlePlanClick, isCreatingConversation } = useHandlePlanClick();\n\n // Close context menu when agent starts running\n useEffect(() => {\n if ((isAgentRunning || !isWebSocketConnected) && contextMenuOpen) {\n setContextMenuOpen(false);\n }\n }, [isAgentRunning, contextMenuOpen, isWebSocketConnected]);\n\n const isButtonDisabled =\n isHomePage ||\n isAgentRunning ||\n isCreatingConversation ||\n !isWebSocketConnected;\n\n // Handle Shift + Tab keyboard shortcut to cycle through modes\n useEffect(() => {\n if (isButtonDisabled) {\n return undefined;\n }\n\n const handleKeyDown = (event: KeyboardEvent) => {\n // Check for Shift + Tab combination\n if (event.shiftKey && event.key === \"Tab\") {\n // Prevent default tab navigation behavior\n event.preventDefault();\n event.stopPropagation();\n\n // Cycle between modes: code -> plan -> code\n const nextMode = conversationMode === \"code\" ? \"plan\" : \"code\";\n if (nextMode === \"plan\") {\n handlePlanClick(event);\n } else {\n setConversationMode(nextMode);\n }\n }\n };\n\n document.addEventListener(\"keydown\", handleKeyDown);\n\n return () => {\n document.removeEventListener(\"keydown\", handleKeyDown);\n };\n }, [\n isButtonDisabled,\n conversationMode,\n setConversationMode,\n handlePlanClick,\n ]);\n\n const handleButtonClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setContextMenuOpen(!contextMenuOpen);\n };\n\n const handleCodeClick = (event: React.MouseEvent<HTMLButtonElement>) => {\n event.preventDefault();\n event.stopPropagation();\n setConversationMode(\"code\");\n };\n\n const isExecutionAgent = conversationMode === \"code\";\n\n const buttonLabel = useMemo(() => {\n if (isExecutionAgent) {\n return t(I18nKey.COMMON$CODE);\n }\n return t(I18nKey.COMMON$PLAN);\n }, [isExecutionAgent, t]);\n\n const buttonIcon = useMemo(() => {\n if (isExecutionAgent) {\n return <CodePillIcon className=\"h-[11px] w-[11px] shrink-0\" />;\n }\n return <LessonPlanIcon width={18} height={18} color=\"currentColor\" />;\n }, [isExecutionAgent]);\n\n const button = (\n <div className=\"relative\">\n <button\n type=\"button\"\n onClick={handleButtonClick}\n disabled={isButtonDisabled}\n className={cn(\n \"flex items-center rounded-[100px] transition-[border-color,color,opacity]\",\n isExecutionAgent\n ? \"border border-transparent text-[var(--oh-muted)]\"\n : \"border border-[#597FF4] bg-[#4A67BD]\",\n !isButtonDisabled &&\n isExecutionAgent &&\n \"cursor-pointer hover:text-white hover:bg-white/10\",\n !isButtonDisabled &&\n !isExecutionAgent &&\n \"cursor-pointer text-white hover:bg-white/10\",\n isButtonDisabled &&\n cn(\n \"opacity-50 cursor-not-allowed\",\n isExecutionAgent && \"border-transparent\",\n ),\n )}\n >\n <div className=\"flex items-center gap-1 pl-1.5\">\n {buttonIcon}\n <Typography.Text className=\"text-2.75 not-italic font-normal leading-5\">\n {buttonLabel}\n </Typography.Text>\n </div>\n <ComboboxCaretInline isOpen={contextMenuOpen} />\n </button>\n {contextMenuOpen && (\n <ChangeAgentContextMenu\n onClose={() => setContextMenuOpen(false)}\n onCodeClick={handleCodeClick}\n onPlanClick={handlePlanClick}\n />\n )}\n </div>\n );\n\n if (isHomePage) {\n return (\n <StyledTooltip\n content={t(I18nKey.CHANGE_AGENT$SWITCH_AFTER_CONVERSATION)}\n placement=\"top\"\n >\n {button}\n </StyledTooltip>\n );\n }\n\n return button;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAoBA,SAAgB,IAAoB;CAClC,IAAM,CAAC,GAAiB,KAAsB,EAAkB,GAAM,EAEhE,EAAE,qBAAkB,wBAAqB,6BAC7C,GAAsB,EAElB,EAAE,sBAAmB,GAA2B,EAEhD,IAAa,CAAC,GAId,IAFkB,GAEK,KAAoB,QAE3C,EAAE,qBAAkB,GAAe,EAEnC,EAAE,SAAM,EAAe,YAAY,EAEnC,IAAiB,MAAkB,EAAW,SAE9C,EAAE,MAAM,MAAiB,GAAuB,EAEhD,IAAc,GAAgB,EAG9B,IAA2B,EAAsB,KAAK,EAGtD,EAAE,eAAY,yBAAsB,EACxC,GACA,GAAc,MAAM,KACrB;AAGD,SAAgB;AACd,EACE,MAAe,WACf,KACA,GAAc,MACd,KACA,EAAyB,YAAY,MAGrC,EAAyB,UAAU,GAEnC,EAAY,kBAAkB,EAC5B,UAAU;GAAC;GAAQ;GAAgB,EAAa;GAAG,EACpD,CAAC;IAEH;EACD;EACA;EACA,GAAc;EACd;EACA;EACD,CAAC;CAGF,IAAM,EAAE,oBAAiB,8BAA2B,GAAoB;AAGxE,SAAgB;AACd,GAAK,KAAkB,CAAC,MAAyB,KAC/C,EAAmB,GAAM;IAE1B;EAAC;EAAgB;EAAiB;EAAqB,CAAC;CAE3D,IAAM,IACJ,KACA,KACA,KACA,CAAC;AAGH,SAAgB;AACd,MAAI,EACF;EAGF,IAAM,KAAiB,MAAyB;AAE9C,OAAI,EAAM,YAAY,EAAM,QAAQ,OAAO;AAGzC,IADA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB;IAGvB,IAAM,IAAW,MAAqB,SAAS,SAAS;AACxD,IAAI,MAAa,SACf,EAAgB,EAAM,GAEtB,EAAoB,EAAS;;;AAOnC,SAFA,SAAS,iBAAiB,WAAW,EAAc,QAEtC;AACX,YAAS,oBAAoB,WAAW,EAAc;;IAEvD;EACD;EACA;EACA;EACA;EACD,CAAC;CAEF,IAAM,KAAqB,MAA+C;AAGxE,EAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,EAAmB,CAAC,EAAgB;IAGhC,KAAmB,MAA+C;AAGtE,EAFA,EAAM,gBAAgB,EACtB,EAAM,iBAAiB,EACvB,EAAoB,OAAO;IAGvB,IAAmB,MAAqB,QAExC,IAAc,QAET,EADL,IACO,EAAQ,cAEV,EAAQ,YAFc,EAG9B,CAAC,GAAkB,EAAE,CAAC,EAEnB,IAAa,QACb,IACK,kBAAC,GAAD,EAAc,WAAU,8BAA+B,CAAA,GAEzD,kBAAC,GAAD;EAAgB,OAAO;EAAI,QAAQ;EAAI,OAAM;EAAiB,CAAA,EACpE,CAAC,EAAiB,CAAC,EAEhB,IACJ,kBAAC,OAAD;EAAK,WAAU;YAAf,CACE,kBAAC,UAAD;GACE,MAAK;GACL,SAAS;GACT,UAAU;GACV,WAAW,EACT,6EACA,IACI,qDACA,wCACJ,CAAC,KACC,KACA,qDACF,CAAC,KACC,CAAC,KACD,+CACF,KACE,EACE,iCACA,KAAoB,qBACrB,CACJ;aApBH,CAsBE,kBAAC,OAAD;IAAK,WAAU;cAAf,CACG,GACD,kBAAC,EAAW,MAAZ;KAAiB,WAAU;eACxB;KACe,CAAA,CACd;OACN,kBAAC,GAAD,EAAqB,QAAQ,GAAmB,CAAA,CACzC;MACR,KACC,kBAAC,GAAD;GACE,eAAe,EAAmB,GAAM;GACxC,aAAa;GACb,aAAa;GACb,CAAA,CAEA;;AAcR,QAXI,IAEA,kBAAC,GAAD;EACE,SAAS,EAAE,EAAQ,uCAAuC;EAC1D,WAAU;YAET;EACa,CAAA,GAIb"}
@@ -1,4 +1,4 @@
1
- const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../types/agent-state.cjs`),i=require(`../../../utils/utils.cjs`),a=require(`../../../hooks/use-conversation-id.cjs`),o=require(`../../../stores/conversation-store.cjs`),s=require(`../../../utils/custom-toast-handlers.cjs`),c=require(`../../../node_modules/posthog-js/react/dist/esm/index.cjs`);require(`../../../constants/server-connection-error.cjs`);const l=require(`../../../stores/error-message-store.cjs`),u=require(`../../../stores/optimistic-user-message-store.cjs`),d=require(`../../../stores/model-store.cjs`),f=require(`../../../contexts/conversation-websocket-context.cjs`),p=require(`../../../hooks/use-send-message.cjs`),m=require(`../../../hooks/query/use-active-conversation.cjs`),h=require(`../../../hooks/use-agent-state.cjs`),ee=require(`../../../utils/convert-image-to-base-64.cjs`),te=require(`../../../utils/file-validation.cjs`),g=require(`../../../utils/pending-task-message-link.cjs`),ne=require(`../../../hooks/query/use-task-polling.cjs`),re=require(`../../../services/chat-service.cjs`),ie=require(`./btw-messages.cjs`),ae=require(`./model-messages.cjs`),oe=require(`../../shared/loading-spinner.cjs`),se=require(`./interactive-chat-box.cjs`),ce=require(`../../../hooks/use-filtered-events.cjs`),le=require(`../../../hooks/use-scroll-to-bottom.cjs`),ue=require(`../../../hooks/use-load-older-events.cjs`),de=require(`./typing-indicator.cjs`),fe=require(`./chat-suggestions.cjs`),pe=require(`../../../context/scroll-context.cjs`),me=require(`../../../stores/initial-query-store.cjs`),he=require(`../../../hooks/use-handle-build-plan-click.cjs`),ge=require(`../../shared/buttons/scroll-to-bottom-button.cjs`),_e=require(`./chat-messages-skeleton.cjs`),ve=require(`./error-message-banner.cjs`),ye=require(`../../conversation-events/chat/messages.cjs`),be=require(`./pending-user-messages.cjs`),xe=require(`../../../hooks/mutation/use-unified-upload-files.cjs`),Se=require(`./confirmation-mode-enabled.cjs`),Ce=require(`./chat-status-indicator.cjs`),we=require(`../../../hooks/mutation/use-new-conversation-command.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function Te(e,t){return e?`github`:t?`replay`:`direct`}function y(){let e=c.usePostHog(),{setMessageToSend:y}=o.useConversationStore(),{errorMessage:b,removeErrorMessage:Ee,setErrorMessage:x}=l.useErrorMessageStore(),{isTask:S,taskStatus:C,taskDetail:De}=ne.useTaskPolling(),w=S,T=f.useConversationWebSocket(),{send:Oe}=p.useSendMessage(),{renderableEvents:E,allConversationEvents:ke,totalEvents:D,hasSubstantiveAgentActions:Ae,userEventsExist:je}=ce.useFilteredEvents(),Me=u.useOptimisticUserMessageStore(e=>e.enqueuePendingMessage),Ne=u.useOptimisticUserMessageStore(e=>e.markPendingMessageError),O=u.useOptimisticUserMessageStore(e=>e.pendingMessages),{t:k}=t.useTranslation(`openhands`),A=_.default.useRef(null),{scrollDomToBottom:j,onChatBodyScroll:M,hitBottom:N,autoScroll:P,setAutoScroll:Pe,setHitBottom:Fe}=le.useScrollToBottom(A),{mutate:Ie,isPending:F}=we.useNewConversationCommand(),{curAgentState:I}=h.useAgentState(),{handleBuildPlanClick:L}=he.useHandleBuildPlanClick(),{data:Le}=m.useActiveConversation(),R=Le?.sandbox_status??null,z=R===`MISSING`||R===`ERROR`,B=I===r.AgentState.RUNNING||I===r.AgentState.LOADING;_.default.useEffect(()=>{if(B)return;let e=e=>{(e.metaKey||e.ctrlKey)&&e.key===`Enter`&&(e.preventDefault(),e.stopPropagation(),L(e),j())};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[B,L,j]);let{selectedRepository:Re,replayJson:V}=me.useInitialQueryStore(),{conversationId:H}=a.useOptionalConversationId(),{mutateAsync:ze}=xe.useUnifiedUploadFiles(),{isLoading:U,hasMore:W,loadOlder:G}=ue.useLoadOlderEvents(H),K=_.default.useRef(null),q=_.default.useCallback(e=>{if(w||U||!W)return;let t=e.scrollTop<=80,r=e.scrollHeight<=e.clientHeight+80;!t&&!r||(K.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop},G().catch(e=>{K.current=null,x(e instanceof Error&&e.message?e.message:k(n.I18nKey.ERROR$GENERIC))}))},[W,U,w,G,x,k]),Be=_.default.useCallback(e=>{e.deltaY<0&&e.currentTarget.scrollTop<=0&&q(e.currentTarget)},[q]),J=_.default.useMemo(()=>H?O.some(e=>g.matchesPendingConversationId(H,e.conversationId)):!1,[O,H]),Y=ke.length>0||J||!T?.isLoadingHistory,X=!!H,Z=!Y&&!S,Ve=d.useModelStore(e=>H?(e.entriesByConversation[H]?.length??0)>0:!1),He=async(t,r,i)=>{if(t.trim()===`/new`){if(!H){s.displayErrorToast(k(n.I18nKey.CONVERSATION$CLEAR_NO_ID));return}if(D===0){s.displayErrorToast(k(n.I18nKey.CONVERSATION$CLEAR_EMPTY));return}if(F)return;Ie();return}let a=[...r],o=[...i];D===0?e.capture(`initial_query_submitted`,{entry_point:Te(Re!==null,V!==null),query_character_length:t.length,replay_json_size:V?.length}):e.capture(`user_message_sent`,{session_message_count:D,current_message_length:t.length});let c=te.validateFiles([...a,...o]);if(!c.isValid){s.displayErrorToast(`Error: ${c.errorMessage}`);return}let l=a.map(e=>ee.convertImageToBase64(e)),u=await Promise.all(l),d=new Date().toISOString(),{skipped_files:f,uploaded_files:p}=o.length>0?await ze({conversationId:H,files:o}):{skipped_files:[],uploaded_files:[]};f.forEach(e=>s.displayErrorToast(e.reason));let m=`${k(`CHAT_INTERFACE$AUGMENTED_PROMPT_FILES_TITLE`)}: ${p.join(`
1
+ const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../node_modules/react-i18next/dist/es/useTranslation.cjs`),n=require(`../../../i18n/declaration.cjs`),r=require(`../../../types/agent-state.cjs`),i=require(`../../../utils/utils.cjs`),a=require(`../../../hooks/use-conversation-id.cjs`),o=require(`../../../stores/conversation-store.cjs`),s=require(`../../../utils/custom-toast-handlers.cjs`),c=require(`../../../node_modules/posthog-js/react/dist/esm/index.cjs`);require(`../../../constants/server-connection-error.cjs`);const l=require(`../../../stores/error-message-store.cjs`),u=require(`../../../stores/optimistic-user-message-store.cjs`),d=require(`../../../stores/model-store.cjs`),f=require(`../../../contexts/conversation-websocket-context.cjs`),p=require(`../../../hooks/use-send-message.cjs`),m=require(`../../../hooks/query/use-active-conversation.cjs`),h=require(`../../../hooks/use-agent-state.cjs`),ee=require(`../../../utils/convert-image-to-base-64.cjs`),te=require(`../../../utils/file-validation.cjs`),g=require(`../../../utils/pending-task-message-link.cjs`),ne=require(`../../../hooks/query/use-task-polling.cjs`),re=require(`../../../services/chat-service.cjs`),ie=require(`./btw-messages.cjs`),ae=require(`./model-messages.cjs`),oe=require(`../../shared/loading-spinner.cjs`),se=require(`../../../hooks/use-scroll-to-bottom.cjs`),ce=require(`../../../context/scroll-context.cjs`),le=require(`./interactive-chat-box.cjs`),ue=require(`../../../hooks/use-filtered-events.cjs`),de=require(`../../../hooks/use-load-older-events.cjs`),fe=require(`./typing-indicator.cjs`),pe=require(`./chat-suggestions.cjs`),me=require(`../../../stores/initial-query-store.cjs`),he=require(`../../../hooks/use-handle-build-plan-click.cjs`),ge=require(`../../shared/buttons/scroll-to-bottom-button.cjs`),_e=require(`./chat-messages-skeleton.cjs`),ve=require(`./error-message-banner.cjs`),ye=require(`../../conversation-events/chat/messages.cjs`),be=require(`./pending-user-messages.cjs`),xe=require(`../../../hooks/mutation/use-unified-upload-files.cjs`),Se=require(`./confirmation-mode-enabled.cjs`),Ce=require(`./chat-status-indicator.cjs`),we=require(`../../../hooks/mutation/use-new-conversation-command.cjs`);let _=require(`react`);_=e.__toESM(_,1);let v=require(`react/jsx-runtime`);function Te(e,t){return e?`github`:t?`replay`:`direct`}function y(){let e=c.usePostHog(),{setMessageToSend:y}=o.useConversationStore(),{errorMessage:b,removeErrorMessage:Ee,setErrorMessage:x}=l.useErrorMessageStore(),{isTask:S,taskStatus:C,taskDetail:De}=ne.useTaskPolling(),w=S,T=f.useConversationWebSocket(),{send:Oe}=p.useSendMessage(),{renderableEvents:E,allConversationEvents:ke,totalEvents:D,hasSubstantiveAgentActions:Ae,userEventsExist:je}=ue.useFilteredEvents(),Me=u.useOptimisticUserMessageStore(e=>e.enqueuePendingMessage),Ne=u.useOptimisticUserMessageStore(e=>e.markPendingMessageError),O=u.useOptimisticUserMessageStore(e=>e.pendingMessages),{t:k}=t.useTranslation(`openhands`),A=_.default.useRef(null),{scrollDomToBottom:j,onChatBodyScroll:M,hitBottom:N,autoScroll:P,setAutoScroll:Pe,setHitBottom:Fe}=se.useScrollToBottom(A),{mutate:Ie,isPending:F}=we.useNewConversationCommand(),{curAgentState:I}=h.useAgentState(),{handleBuildPlanClick:L}=he.useHandleBuildPlanClick(),{data:Le}=m.useActiveConversation(),R=Le?.sandbox_status??null,z=R===`MISSING`||R===`ERROR`,B=I===r.AgentState.RUNNING||I===r.AgentState.LOADING;_.default.useEffect(()=>{if(B)return;let e=e=>{(e.metaKey||e.ctrlKey)&&e.key===`Enter`&&(e.preventDefault(),e.stopPropagation(),L(e),j())};return document.addEventListener(`keydown`,e),()=>{document.removeEventListener(`keydown`,e)}},[B,L,j]);let{selectedRepository:Re,replayJson:V}=me.useInitialQueryStore(),{conversationId:H}=a.useOptionalConversationId(),{mutateAsync:ze}=xe.useUnifiedUploadFiles(),{isLoading:U,hasMore:W,loadOlder:G}=de.useLoadOlderEvents(H),K=_.default.useRef(null),q=_.default.useCallback(e=>{if(w||U||!W)return;let t=e.scrollTop<=80,r=e.scrollHeight<=e.clientHeight+80;!t&&!r||(K.current={scrollHeight:e.scrollHeight,scrollTop:e.scrollTop},G().catch(e=>{K.current=null,x(e instanceof Error&&e.message?e.message:k(n.I18nKey.ERROR$GENERIC))}))},[W,U,w,G,x,k]),Be=_.default.useCallback(e=>{e.deltaY<0&&e.currentTarget.scrollTop<=0&&q(e.currentTarget)},[q]),J=_.default.useMemo(()=>H?O.some(e=>g.matchesPendingConversationId(H,e.conversationId)):!1,[O,H]),Y=ke.length>0||J||!T?.isLoadingHistory,X=!!H,Z=!Y&&!S,Ve=d.useModelStore(e=>H?(e.entriesByConversation[H]?.length??0)>0:!1),He=async(t,r,i)=>{if(t.trim()===`/new`){if(!H){s.displayErrorToast(k(n.I18nKey.CONVERSATION$CLEAR_NO_ID));return}if(D===0){s.displayErrorToast(k(n.I18nKey.CONVERSATION$CLEAR_EMPTY));return}if(F)return;Ie();return}let a=[...r],o=[...i];D===0?e.capture(`initial_query_submitted`,{entry_point:Te(Re!==null,V!==null),query_character_length:t.length,replay_json_size:V?.length}):e.capture(`user_message_sent`,{session_message_count:D,current_message_length:t.length});let c=te.validateFiles([...a,...o]);if(!c.isValid){s.displayErrorToast(`Error: ${c.errorMessage}`);return}let l=a.map(e=>ee.convertImageToBase64(e)),u=await Promise.all(l),d=new Date().toISOString(),{skipped_files:f,uploaded_files:p}=o.length>0?await ze({conversationId:H,files:o}):{skipped_files:[],uploaded_files:[]};f.forEach(e=>s.displayErrorToast(e.reason));let m=`${k(`CHAT_INTERFACE$AUGMENTED_PROMPT_FILES_TITLE`)}: ${p.join(`
2
2
 
3
- `)}`,h=p.length>0?`${t}\n\n${m}`:t,g=Me({conversationId:H,text:t,content:h,imageUrls:u,fileUrls:p,timestamp:d});y(``);try{await Oe(re.createChatMessage(h,u,p,d))}catch(e){Ne(g,e instanceof Error?e.message:`Failed to send message`)}};_.default.useEffect(()=>{if(K.current&&A.current){let{scrollHeight:e,scrollTop:t}=K.current,n=A.current,r=n.scrollHeight-e;r>0&&(n.scrollTop=t+r),K.current=null;return}P&&j()},[E.length,J,j]);let Ue=_.default.useRef(q);_.default.useEffect(()=>{Ue.current=q}),_.default.useEffect(()=>{let e=A.current;e&&Ue.current(e)},[E.length,W]);let We={scrollRef:A,autoScroll:P,setAutoScroll:Pe,scrollDomToBottom:j,hitBottom:N,setHitBottom:Fe,onChatBodyScroll:M},Q=I===r.AgentState.LOADING||I===r.AgentState.INIT,Ge=I===r.AgentState.STOPPED,$=I===r.AgentState.PAUSED,Ke=i.getStatusColor({isPausing:$,isTask:S,taskStatus:C,isStartingStatus:Q,isStopStatus:Ge,curAgentState:I}),qe=i.getStatusText({isPausing:$,isTask:S,taskStatus:C,taskDetail:De,isStartingStatus:Q,isStopStatus:Ge,curAgentState:I,errorMessage:b,t:k});return(0,v.jsx)(pe.ScrollProvider,{value:We,children:(0,v.jsxs)(`div`,{className:`relative flex h-full flex-col justify-between px-4`,"data-testid":`chat-interface`,children:[!Ae&&!J&&!je&&!Ve&&!Z&&!w&&D===0&&!z&&(0,v.jsx)(fe.ChatSuggestions,{onSuggestionsClick:e=>y(e)}),(0,v.jsxs)(`div`,{ref:A,onScroll:e=>{M(e.currentTarget),q(e.currentTarget)},onWheel:Be,className:`custom-scrollbar-always flex min-h-0 grow flex-col gap-2 overflow-x-hidden overflow-y-auto px-0 pt-4 pb-8 md:px-4`,children:[Z&&X&&(0,v.jsx)(_e.ChatMessagesSkeleton,{}),Z&&!X&&(0,v.jsx)(`div`,{className:`flex justify-center`,"data-testid":`loading-spinner`,children:(0,v.jsx)(oe.LoadingSpinner,{size:`small`})}),U&&(0,v.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-3 text-sm text-neutral-400`,"data-testid":`loading-older-events`,children:[(0,v.jsx)(oe.LoadingSpinner,{size:`small`}),(0,v.jsx)(`span`,{children:k(n.I18nKey.CHAT_INTERFACE$FETCHING_OLDER_MESSAGES)})]}),(0,v.jsx)(ae.ModelMessages,{conversationId:H,anchorEventId:null}),Y&&E.length>0&&(0,v.jsx)(ye.Messages,{messages:E,allEvents:ke}),(0,v.jsx)(be.PendingUserMessages,{})]}),(0,v.jsxs)(`div`,{className:`flex shrink-0 flex-col gap-[6px]`,children:[(0,v.jsx)(ie.BtwMessages,{conversationId:H}),b&&(0,v.jsx)(ve.ErrorMessageBanner,{message:b,onDismiss:Ee,onRetry:b===`Unable to connect to server`?()=>T?.reconnect():void 0}),z?(0,v.jsxs)(`div`,{"data-testid":`archived-conversation-banner`,className:`mx-1 px-4 py-3 rounded-lg bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)]`,children:[(0,v.jsx)(`p`,{className:`text-xs font-semibold text-[var(--oh-foreground)]`,children:k(R===`ERROR`?n.I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_TITLE:n.I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_TITLE)}),(0,v.jsx)(`p`,{className:`text-xs text-[var(--oh-muted)] mt-0.5`,children:k(R===`ERROR`?n.I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_DESCRIPTION:n.I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_DESCRIPTION)})]}):(0,v.jsxs)(`div`,{className:`relative`,children:[(0,v.jsx)(`div`,{className:`pointer-events-none absolute inset-x-0 bottom-full mb-1 z-20`,children:(0,v.jsxs)(`div`,{className:`flex justify-between relative`,children:[(0,v.jsxs)(`div`,{className:`flex items-end gap-1 pointer-events-auto`,children:[(0,v.jsx)(Se.default,{}),Q&&(0,v.jsx)(Ce.default,{statusColor:Ke,status:qe})]}),N?I===r.AgentState.RUNNING&&(0,v.jsx)(`div`,{className:`absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto`,children:(0,v.jsx)(de.TypingIndicator,{})}):(0,v.jsx)(`div`,{className:`absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto`,children:(0,v.jsx)(ge.ScrollToBottomButton,{onClick:j})})]})}),(0,v.jsx)(se.InteractiveChatBox,{onSubmit:He,disabled:F})]})]})]})})}exports.ChatInterface=y;
3
+ `)}`,h=p.length>0?`${t}\n\n${m}`:t,g=Me({conversationId:H,text:t,content:h,imageUrls:u,fileUrls:p,timestamp:d});j(),y(``);try{await Oe(re.createChatMessage(h,u,p,d))}catch(e){Ne(g,e instanceof Error?e.message:`Failed to send message`)}};_.default.useEffect(()=>{if(K.current&&A.current){let{scrollHeight:e,scrollTop:t}=K.current,n=A.current,r=n.scrollHeight-e;r>0&&(n.scrollTop=t+r),K.current=null;return}P&&j()},[E.length,J,j]);let Ue=_.default.useRef(q);_.default.useEffect(()=>{Ue.current=q}),_.default.useEffect(()=>{let e=A.current;e&&Ue.current(e)},[E.length,W]);let We={scrollRef:A,autoScroll:P,setAutoScroll:Pe,scrollDomToBottom:j,hitBottom:N,setHitBottom:Fe,onChatBodyScroll:M},Q=I===r.AgentState.LOADING||I===r.AgentState.INIT,Ge=I===r.AgentState.STOPPED,$=I===r.AgentState.PAUSED,Ke=i.getStatusColor({isPausing:$,isTask:S,taskStatus:C,isStartingStatus:Q,isStopStatus:Ge,curAgentState:I}),qe=i.getStatusText({isPausing:$,isTask:S,taskStatus:C,taskDetail:De,isStartingStatus:Q,isStopStatus:Ge,curAgentState:I,errorMessage:b,t:k});return(0,v.jsx)(ce.ScrollProvider,{value:We,children:(0,v.jsxs)(`div`,{className:`relative flex h-full flex-col justify-between px-4`,"data-testid":`chat-interface`,children:[!Ae&&!J&&!je&&!Ve&&!Z&&!w&&D===0&&!z&&(0,v.jsx)(pe.ChatSuggestions,{onSuggestionsClick:e=>y(e)}),(0,v.jsxs)(`div`,{ref:A,"data-testid":`chat-scroll-container`,onScroll:e=>{M(e.currentTarget),q(e.currentTarget)},onWheel:Be,className:`custom-scrollbar-always flex min-h-0 grow flex-col gap-2 overflow-x-hidden overflow-y-auto px-0 pt-4 pb-8 md:px-4`,children:[Z&&X&&(0,v.jsx)(_e.ChatMessagesSkeleton,{}),Z&&!X&&(0,v.jsx)(`div`,{className:`flex justify-center`,"data-testid":`loading-spinner`,children:(0,v.jsx)(oe.LoadingSpinner,{size:`small`})}),U&&(0,v.jsxs)(`div`,{className:`flex items-center justify-center gap-2 py-3 text-sm text-neutral-400`,"data-testid":`loading-older-events`,children:[(0,v.jsx)(oe.LoadingSpinner,{size:`small`}),(0,v.jsx)(`span`,{children:k(n.I18nKey.CHAT_INTERFACE$FETCHING_OLDER_MESSAGES)})]}),(0,v.jsx)(ae.ModelMessages,{conversationId:H,anchorEventId:null}),Y&&E.length>0&&(0,v.jsx)(ye.Messages,{messages:E,allEvents:ke}),(0,v.jsx)(be.PendingUserMessages,{})]}),(0,v.jsxs)(`div`,{className:`flex shrink-0 flex-col gap-[6px]`,children:[(0,v.jsx)(ie.BtwMessages,{conversationId:H}),b&&(0,v.jsx)(ve.ErrorMessageBanner,{message:b,onDismiss:Ee,onRetry:b===`Unable to connect to server`?()=>T?.reconnect():void 0}),z?(0,v.jsxs)(`div`,{"data-testid":`archived-conversation-banner`,className:`mx-1 px-4 py-3 rounded-lg bg-[var(--oh-surface)] border border-[var(--oh-border-subtle)]`,children:[(0,v.jsx)(`p`,{className:`text-xs font-semibold text-[var(--oh-foreground)]`,children:k(R===`ERROR`?n.I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_TITLE:n.I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_TITLE)}),(0,v.jsx)(`p`,{className:`text-xs text-[var(--oh-muted)] mt-0.5`,children:k(R===`ERROR`?n.I18nKey.CHAT_INTERFACE$ERROR_SANDBOX_DESCRIPTION:n.I18nKey.CHAT_INTERFACE$ARCHIVED_SANDBOX_DESCRIPTION)})]}):(0,v.jsxs)(`div`,{className:`relative`,children:[(0,v.jsx)(`div`,{className:`pointer-events-none absolute inset-x-0 bottom-full mb-1 z-20`,children:(0,v.jsxs)(`div`,{className:`flex justify-between relative`,children:[(0,v.jsxs)(`div`,{className:`flex items-end gap-1 pointer-events-auto`,children:[(0,v.jsx)(Se.default,{}),Q&&(0,v.jsx)(Ce.default,{statusColor:Ke,status:qe})]}),N?I===r.AgentState.RUNNING&&(0,v.jsx)(`div`,{className:`absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto`,children:(0,v.jsx)(fe.TypingIndicator,{})}):(0,v.jsx)(`div`,{className:`absolute left-1/2 transform -translate-x-1/2 bottom-0 pointer-events-auto`,children:(0,v.jsx)(ge.ScrollToBottomButton,{onClick:j})})]})}),(0,v.jsx)(le.InteractiveChatBox,{onSubmit:He,disabled:F})]})]})]})})}exports.ChatInterface=y;
4
4
  //# sourceMappingURL=chat-interface.cjs.map