@openhands/agent-canvas 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (674) hide show
  1. package/README.md +11 -12
  2. package/README.windows.md +4 -2
  3. package/build/assets/{Trans-Ch-OVWEA.js → Trans-DqF_OEZB.js} +1 -1
  4. package/build/assets/{acp-providers-Sy2LKyMa.js → acp-providers-BQwoUBpe.js} +1 -1
  5. package/build/assets/active-backend-context-BCXRv5pm.js +1 -0
  6. package/build/assets/{add-backend-modal-Bgy8anoR.js → add-backend-modal-BKHzq5no.js} +1 -1
  7. package/build/assets/{agent-profiles-settings-3mmXljZX.js → agent-profiles-settings-CEp5CIz8.js} +1 -1
  8. package/build/assets/agent-server-compatibility-D6DSanlx.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-cylugfDz.js +41 -0
  10. package/build/assets/{agent-settings-B-W10B4E.js → agent-settings-C-tann3x.js} +2 -2
  11. package/build/assets/agent-settings-DGUKNSnU.js +1 -0
  12. package/build/assets/{alert-banner-Bpf2NQJq.js → alert-banner-Dd4woGbm.js} +1 -1
  13. package/build/assets/api-key-entry-screen-Bll_CkuQ.js +1 -0
  14. package/build/assets/app-settings-BpViWwpC.js +1 -0
  15. package/build/assets/automation-detail-Z9dN2f5l.js +2 -0
  16. package/build/assets/automation-export-D5uL0Tf8.js +2 -0
  17. package/build/assets/automations-list-BMOWcqzZ.js +1 -0
  18. package/build/assets/{back-nav-button-CUS2mi-U.js → back-nav-button-ZUDTMOTB.js} +1 -1
  19. package/build/assets/backend-form-modal-DhBT8P4g.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-D9hZx05w.js → backend-synced-settings-badge-Db_REKVw.js} +1 -1
  21. package/build/assets/{base-modal-BuF2f0YL.js → base-modal-Cb0C6m0Q.js} +1 -1
  22. package/build/assets/{bot-D7eUAuSB.js → bot-C-EA6685.js} +1 -1
  23. package/build/assets/{brand-button-vMVuyDXu.js → brand-button-2Tp3aXk1.js} +1 -1
  24. package/build/assets/{browser-BifzWIBd.js → browser-QYLVr1kS.js} +2 -2
  25. package/build/assets/browser-store-BGXOGo-R.js +1 -0
  26. package/build/assets/{browser-tab-CkkqJnqw.js → browser-tab-BTTP1OE2.js} +1 -1
  27. package/build/assets/canvas-ui-BhxjoA1f.js +1 -0
  28. package/build/assets/{chat-send-button-B_jc5z5C.js → chat-send-button-BZHG2hAW.js} +1 -1
  29. package/build/assets/check-TbOEtHKj.js +1 -0
  30. package/build/assets/{chevron-down-CI8A6tet.js → chevron-down-D7IvFMBM.js} +1 -1
  31. package/build/assets/{circle-plus-check-toggle-C5YxjxvI.js → circle-plus-check-toggle-QQJKUnF6.js} +1 -1
  32. package/build/assets/client-source-Biqarhgr.js +1 -0
  33. package/build/assets/cloud-funnel-analytics-D-27T5Ao.js +1 -0
  34. package/build/assets/{combobox-caret-DoObzqf4.js → combobox-caret-A5jMVHgE.js} +1 -1
  35. package/build/assets/{command-menu-D-JV3Wqm.js → command-menu-BMVEme4H.js} +1 -1
  36. package/build/assets/{command-store-D8J5LMW1.js → command-store-jrWX_CcW.js} +1 -1
  37. package/build/assets/condenser-settings-CIa6eyB4.js +1 -0
  38. package/build/assets/{confirmation-modal-BGLu6XJN.js → confirmation-modal-4n_944zq.js} +1 -1
  39. package/build/assets/{context-menu-list-item-BD5_EaKN.js → context-menu-list-item-DvfFic5I.js} +1 -1
  40. package/build/assets/conversation-CHnEXGRM.js +1 -0
  41. package/build/assets/conversation-D4o4dG2t.js +89 -0
  42. package/build/assets/conversation-panel-11FjLCId.js +1 -0
  43. package/build/assets/conversation-service.api-0JG_fF9d.js +1 -0
  44. package/build/assets/conversation-state-store-JiUQp02U.js +1 -0
  45. package/build/assets/{conversation-store-LptbOvKa.js → conversation-store-BCSpLuEf.js} +1 -1
  46. package/build/assets/{conversation-tab-empty-state-B2dDYEwf.js → conversation-tab-empty-state-BUhKVwoi.js} +1 -1
  47. package/build/assets/{copy-3JCw_XFB.js → copy-D1YGjP-C.js} +1 -1
  48. package/build/assets/{copy-to-clipboard-button-CaR52OnA.js → copy-to-clipboard-button-Dp4Q_hXi.js} +1 -1
  49. package/build/assets/declaration-BGsHjPEW.js +1 -0
  50. package/build/assets/{device-verify-Dj6I4Iqe.js → device-verify-CM086oKs.js} +1 -1
  51. package/build/assets/{dropdown-classes-DJSXeLqc.js → dropdown-classes-DZq0BQjb.js} +1 -1
  52. package/build/assets/{ellipsis-button-C5Z-tPFp.js → ellipsis-button-TPJdyxnK.js} +1 -1
  53. package/build/assets/{entry.client-2_3ePAm0.js → entry.client-DypYHsYm.js} +2 -2
  54. package/build/assets/{enum-filter-dropdown-DXbVwiNZ.js → enum-filter-dropdown-CW_mBg0e.js} +1 -1
  55. package/build/assets/{environment-switch-overlay-DZQfRGTx.js → environment-switch-overlay-6laJch-9.js} +1 -1
  56. package/build/assets/{extensions-hub-CLjmOGqS.js → extensions-hub-BJFbFfKS.js} +1 -1
  57. package/build/assets/{extensions-navigation-B5MeUiMq.js → extensions-navigation-DqSl7CJw.js} +1 -1
  58. package/build/assets/{external-link-D2zJjE9G.js → external-link-lhdITPqq.js} +1 -1
  59. package/build/assets/files-tab-c316dKAq.js +1 -0
  60. package/build/assets/{files-tab-store-DJsPxYdg.js → files-tab-store-tfCav6bB.js} +1 -1
  61. package/build/assets/{form-control-classes-ChpNBe9n.js → form-control-classes-CvUaw8_L.js} +1 -1
  62. package/build/assets/format-time-delta-DhDbkD7V.js +1 -0
  63. package/build/assets/git-provider-icon-DUb2pEos.js +1 -0
  64. package/build/assets/home-BQ-DC06B.js +1 -0
  65. package/build/assets/home-D7ds4Jyw.js +1 -0
  66. package/build/assets/index-home-BRFDWs_4.js +1 -0
  67. package/build/assets/install-server-modal-CvHvzZj3.js +1 -0
  68. package/build/assets/{launch-V_3Rp7WS.js → launch-A47TK2Me.js} +1 -1
  69. package/build/assets/lesson-plan-DSf95kQ2.js +4 -0
  70. package/build/assets/{list-todo-BfH6yYUK.js → list-todo-CAtvkzcy.js} +1 -1
  71. package/build/assets/llm-not-configured-banner-B7DR8SPs.js +27 -0
  72. package/build/assets/llm-settings-Ch4ZbW3c.js +1 -0
  73. package/build/assets/llm-settings-Cvi7q4ox.js +1 -0
  74. package/build/assets/llm-subscription-service-Cz1yBBvE.js +1 -0
  75. package/build/assets/loader-circle-CsAsfLIs.js +1 -0
  76. package/build/assets/{loading-spinner-BLdswi6q.js → loading-spinner-Txxx1I1p.js} +1 -1
  77. package/build/assets/manage-backends-modal-DAVhNUCe.js +1 -0
  78. package/build/assets/manifest-958c741a.js +1 -0
  79. package/build/assets/{markdown-renderer-DMT7Zkpd.js → markdown-renderer-B6cJJ_MN.js} +1 -1
  80. package/build/assets/{mcp-BQufNPZ6.js → mcp-BwssgMpg.js} +2 -2
  81. package/build/assets/{messages-BnJCqnFf.js → messages-D8MeG2ZQ.js} +26 -26
  82. package/build/assets/{modal-body-DouOE5KH.js → modal-body-CDZdZ-eT.js} +1 -1
  83. package/build/assets/{modal-classes-Cf-4tbHz.js → modal-classes-B4oit_4c.js} +1 -1
  84. package/build/assets/{modal-close-button-B6CfxxJj.js → modal-close-button-DQElFMPo.js} +1 -1
  85. package/build/assets/model-selector-BxTbbKub.js +1 -0
  86. package/build/assets/{navigation-link-D8hfwvxI.js → navigation-link-imH6S7h2.js} +1 -1
  87. package/build/assets/onboarding-CyicadGt.js +1 -0
  88. package/build/assets/onboarding-modal-DQwihcTz.js +1 -0
  89. package/build/assets/option-service.api-DAkg_F7p.js +1 -0
  90. package/build/assets/organization-service.api-QknzKDh3.js +1 -0
  91. package/build/assets/package-Dk_VtWJ4.js +1 -0
  92. package/build/assets/path-utils-BECGet7G.js +1 -0
  93. package/build/assets/{pencil-Dj2XjKmI.js → pencil-CWhApFIT.js} +1 -1
  94. package/build/assets/{plan-components-CcCBmCzW.js → plan-components-CgPHWz3E.js} +1 -1
  95. package/build/assets/{planner-tab-Dhyqcqmb.js → planner-tab-DPKHmMAX.js} +1 -1
  96. package/build/assets/plugins-management-service-DuTTxquo.js +1 -0
  97. package/build/assets/profiles-service.api-D6jvZOWu.js +1 -0
  98. package/build/assets/providers-B8pxQK8x.js +1 -0
  99. package/build/assets/proxy-CuhhyjdX.js +1 -0
  100. package/build/assets/{recommended-automations-launcher-CTcGTl4d.js → recommended-automations-launcher-DA6DWS7V.js} +1 -1
  101. package/build/assets/{refresh-cw-DM82EIyx.js → refresh-cw-B9B7-vsy.js} +1 -1
  102. package/build/assets/root-BhP6bTBB.js +2 -0
  103. package/build/assets/root-CYWeTrWE.css +1 -0
  104. package/build/assets/root-layout-oBaHHW4s.js +2 -0
  105. package/build/assets/{runtime-waiting-state-BsXLElvN.js → runtime-waiting-state-DeD9G7yJ.js} +1 -1
  106. package/build/assets/schema-field-BOLdL_U7.js +1 -0
  107. package/build/assets/sdk-section-page-ZIltERxf.js +1 -0
  108. package/build/assets/{sdk-settings-schema-Cv7M9eSe.js → sdk-settings-schema-Ba_Pa9uf.js} +1 -1
  109. package/build/assets/search-dIQ09UY2.js +1 -0
  110. package/build/assets/secrets-service-50DElFny.js +1 -0
  111. package/build/assets/{secrets-settings-Cx6yJlyb.js → secrets-settings-Digl7JLe.js} +1 -1
  112. package/build/assets/{segmented-toggle-DyQqPhoD.js → segmented-toggle-C1-ZVyr5.js} +1 -1
  113. package/build/assets/settings-BfgLHsgd.js +1 -0
  114. package/build/assets/{settings-dropdown-input-BjunGXJb.js → settings-dropdown-input-BD5C9fBn.js} +1 -1
  115. package/build/assets/{settings-index-D6sxWSRk.js → settings-index-CODBijya.js} +1 -1
  116. package/build/assets/{settings-input-bD6npsDm.js → settings-input-CzNOq-Pj.js} +1 -1
  117. package/build/assets/{settings-list-classes-Dp_OgzN0.js → settings-list-classes-CuxH9ZoR.js} +1 -1
  118. package/build/assets/settings-modal-BcSWpKI0.js +1 -0
  119. package/build/assets/settings-service.api-Bsu1PvYO.js +1 -0
  120. package/build/assets/{settings-switch-5cuxl8Dc.js → settings-switch-DqmHAfWJ.js} +1 -1
  121. package/build/assets/{settings-utils-BMdG65By.js → settings-utils-B1lBErvV.js} +1 -1
  122. package/build/assets/shared-conversation-CDskdmrb.js +1 -0
  123. package/build/assets/{sidebar-layout-DuzJMMw-.js → sidebar-layout-Cn6eQaEI.js} +1 -1
  124. package/build/assets/{sidebar-mobile-menu-toggle-VvvX32iF.js → sidebar-mobile-menu-toggle-DmCmZgq1.js} +1 -1
  125. package/build/assets/{sidebar-nav-link-5AlTT5-j.js → sidebar-nav-link-DMc8AIe7.js} +1 -1
  126. package/build/assets/{sidebar-store-CZuE8AH5.js → sidebar-store-DXWSCFpa.js} +1 -1
  127. package/build/assets/{skill-card-pill-row-eTIkE4nz.js → skill-card-pill-row-DOIWjiMF.js} +1 -1
  128. package/build/assets/skills-plugins-DLgINYuY.js +1 -0
  129. package/build/assets/{skills-settings-BUv04tSc.js → skills-settings-DPiCWalR.js} +2 -2
  130. package/build/assets/{square-chevron-right-Czw3FFWc.js → square-chevron-right-DE-JN-2Z.js} +1 -1
  131. package/build/assets/{styled-tooltip-CgX2oRaK.js → styled-tooltip-CvsjHuIf.js} +1 -1
  132. package/build/assets/{suspense-BO1Tl_Vf.js → suspense-DmZ3MSSh.js} +1 -1
  133. package/build/assets/{task-list-tab-0k9PhuqO.js → task-list-tab-CuhcDycu.js} +1 -1
  134. package/build/assets/telemetry-DHiTRl5_.js +2 -0
  135. package/build/assets/{terminal-B61WrJdS.js → terminal-CVqF-tCD.js} +1 -1
  136. package/build/assets/{toggle-switch-2j5wwR85.js → toggle-switch-CS0ClOy2.js} +1 -1
  137. package/build/assets/{trash-2-Be_7y3Mn.js → trash-2-BU6BlW4i.js} +1 -1
  138. package/build/assets/{typography-D8mHDeNG.js → typography-CQn9wyEX.js} +1 -1
  139. package/build/assets/use-acp-credential-form-LlfZO_DD.js +1 -0
  140. package/build/assets/{use-activate-agent-profile-20v1xU0H.js → use-activate-agent-profile-DpQjsXM0.js} +1 -1
  141. package/build/assets/{use-active-agent-profile-BkJN7Xsy.js → use-active-agent-profile-Gw_uJujV.js} +1 -1
  142. package/build/assets/{use-active-conversation-DOZVSVtU.js → use-active-conversation-B0sttISF.js} +1 -1
  143. package/build/assets/use-agent-profiles-LHZEK0Gg.js +1 -0
  144. package/build/assets/{use-agent-state-DHZO1obC.js → use-agent-state-C5uA0bRj.js} +1 -1
  145. package/build/assets/use-backends-health-DKt1SmiU.js +1 -0
  146. package/build/assets/{use-can-manage-org-profiles-C-OhWVBS.js → use-can-manage-org-profiles-BAtqsbCi.js} +1 -1
  147. package/build/assets/use-cloud-current-user-id-C8m1oBy4.js +1 -0
  148. package/build/assets/use-config-CXy4vD0H.js +1 -0
  149. package/build/assets/use-create-conversation-CWQGNQ9J.js +1 -0
  150. package/build/assets/{use-create-secret-D0HCOZFu.js → use-create-secret-BIrsX3OB.js} +1 -1
  151. package/build/assets/{use-event-store-DjgU4wfO.js → use-event-store-CsD17BKu.js} +1 -1
  152. package/build/assets/{use-handle-plan-click-DyRASKMC.js → use-handle-plan-click-DEuO7Byo.js} +1 -1
  153. package/build/assets/use-is-authed-Dvbpiu3B.js +1 -0
  154. package/build/assets/{use-launch-skill-in-chat-BnoDgvAy.js → use-launch-skill-in-chat-CdHOoQGM.js} +1 -1
  155. package/build/assets/use-llm-profiles-DQpQxVOD.js +1 -0
  156. package/build/assets/use-plugins-marketplace-Bkk07W_M.js +1 -0
  157. package/build/assets/use-runtime-is-ready-D4pkGT6E.js +1 -0
  158. package/build/assets/use-save-agent-profile-BxEsjnW0.js +1 -0
  159. package/build/assets/{use-save-settings-BGlSfuYn.js → use-save-settings-ioSUMqbE.js} +1 -1
  160. package/build/assets/use-settings-DNJC4gqu.js +1 -0
  161. package/build/assets/use-settings-nav-items-BUsrDbVR.js +16 -0
  162. package/build/assets/use-skills-BsmKm23M.js +1 -0
  163. package/build/assets/{use-task-list-AVBLbbcR.js → use-task-list-rs7Xbzl8.js} +1 -1
  164. package/build/assets/{use-tracking-BIXzagSI.js → use-tracking-poxQHMwR.js} +1 -1
  165. package/build/assets/{use-user-conversation-zpx7I18x.js → use-user-conversation-B337kUKB.js} +1 -1
  166. package/build/assets/{useMutation-DrCAVg5w.js → useMutation-u8UgRgDK.js} +1 -1
  167. package/build/assets/{useMutationState-D8QN0ZCy.js → useMutationState-OnAsCLA_.js} +1 -1
  168. package/build/assets/{useQueries-DClgUTc7.js → useQueries-BpIlT8Mt.js} +1 -1
  169. package/build/assets/{useQuery-CRBP8vIS.js → useQuery-D5pEZbY0.js} +1 -1
  170. package/build/assets/{useTranslation-CDRCKC9-.js → useTranslation-Cbf5AGg_.js} +1 -1
  171. package/build/assets/{utils-D3U3rlqr.js → utils-DB9LsAzA.js} +1 -1
  172. package/build/assets/{vendor~browser-Cvg7jXCP.js → vendor~browser-5Njjw1Cl.js} +1 -1
  173. package/build/assets/{vendor~browser-tab-Di0kh5Zw.js → vendor~browser-tab-Xn94EtUo.js} +1 -1
  174. package/build/assets/{vendor~entry.client-9tWQ_Ugw.js → vendor~entry.client-DDuH4--y.js} +1 -1
  175. package/build/assets/{vendor~entry.client-D6BtSOtn.js → vendor~entry.client-DQGrNspu.js} +1 -1
  176. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dhilvkxv-BIieS_OD.js +2 -0
  177. package/build/assets/vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~iztiaf2o-rqo6vRFz.js +185 -0
  178. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~jewnulst-C57fm7C-.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ljq5gbus-B2XD4bi2.js} +1 -1
  179. package/build/assets/{vendor~files-tab-B02k3xvx.js → vendor~files-tab-CiC3-lGh.js} +1 -1
  180. package/build/assets/{vendor~home~conversation-panel~conversation-CYMF9JYB.js → vendor~index-home~home~conversation-panel~conversation-ChmYLVgz.js} +1 -1
  181. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-9UMeuPV6.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B8XNNEQZ.js} +1 -1
  182. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-d8aETPbZ.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CnJRLn5s.js} +1 -1
  183. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DWteoTyr.js +2 -0
  184. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D7EUmQ8Y.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dc5nKwLO.js} +1 -1
  185. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BD0GaRGa.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-tlf6AnDe.js} +1 -1
  186. package/build/assets/{vendor~launch-CHXA9cJu.js → vendor~launch-28DK5Rsl.js} +1 -1
  187. package/build/assets/{vendor~root-layout~conversation-panel~conversation-CpiTHpHl.js → vendor~root-layout~conversation-panel~conversation-8tIsyrPj.js} +1 -1
  188. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-_TdZcNMD.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-lYNG9C1U.js} +1 -1
  189. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-Cn2jwSga.js → vendor~root-layout~index-home~home~conversation-panel~conversation-Zhq6xAxR.js} +1 -1
  190. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CZM4hwUT.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation~aler~gfu7qdtz-B81b08bw.js} +1 -1
  191. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~enjl5xdm-B0EIDu4M.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~ftka0fnu-jpyLM1Nn.js} +2 -2
  192. package/build/assets/{verification-settings-D576N2xa.js → verification-settings-HRgEM-_3.js} +1 -1
  193. package/build/assets/x-ZE4iplpF.js +1 -0
  194. package/build/index.html +4 -4
  195. package/build/locales/ar/openhands.json +9 -0
  196. package/build/locales/ca/openhands.json +9 -0
  197. package/build/locales/de/openhands.json +9 -0
  198. package/build/locales/en/openhands.json +9 -0
  199. package/build/locales/es/openhands.json +9 -0
  200. package/build/locales/fr/openhands.json +9 -0
  201. package/build/locales/it/openhands.json +9 -0
  202. package/build/locales/ja/openhands.json +9 -0
  203. package/build/locales/ko-KR/openhands.json +9 -0
  204. package/build/locales/no/openhands.json +9 -0
  205. package/build/locales/pt/openhands.json +9 -0
  206. package/build/locales/tr/openhands.json +9 -0
  207. package/build/locales/uk/openhands.json +9 -0
  208. package/build/locales/zh-CN/openhands.json +9 -0
  209. package/build/locales/zh-TW/openhands.json +9 -0
  210. package/config/defaults.json +5 -4
  211. package/dist/api/agent-server-adapter.cjs +3 -3
  212. package/dist/api/agent-server-adapter.cjs.map +1 -1
  213. package/dist/api/agent-server-adapter.d.ts +9 -1
  214. package/dist/api/agent-server-adapter.js +105 -102
  215. package/dist/api/agent-server-adapter.js.map +1 -1
  216. package/dist/api/agent-server-config.cjs +1 -1
  217. package/dist/api/agent-server-config.cjs.map +1 -1
  218. package/dist/api/agent-server-config.d.ts +3 -0
  219. package/dist/api/agent-server-config.js +45 -23
  220. package/dist/api/agent-server-config.js.map +1 -1
  221. package/dist/api/backend-registry/auth.cjs +1 -1
  222. package/dist/api/backend-registry/auth.cjs.map +1 -1
  223. package/dist/api/backend-registry/auth.js +1 -1
  224. package/dist/api/backend-registry/auth.js.map +1 -1
  225. package/dist/api/backend-registry/default-backend.cjs +1 -1
  226. package/dist/api/backend-registry/default-backend.cjs.map +1 -1
  227. package/dist/api/backend-registry/default-backend.d.ts +3 -0
  228. package/dist/api/backend-registry/default-backend.js +22 -10
  229. package/dist/api/backend-registry/default-backend.js.map +1 -1
  230. package/dist/api/backend-registry/storage.cjs +1 -1
  231. package/dist/api/backend-registry/storage.cjs.map +1 -1
  232. package/dist/api/backend-registry/storage.js +38 -33
  233. package/dist/api/backend-registry/storage.js.map +1 -1
  234. package/dist/api/backend-registry/types.d.ts +2 -0
  235. package/dist/api/canvas-ui-client-tool.cjs +38 -0
  236. package/dist/api/canvas-ui-client-tool.cjs.map +1 -0
  237. package/dist/api/canvas-ui-client-tool.d.ts +14 -0
  238. package/dist/api/canvas-ui-client-tool.js +47 -0
  239. package/dist/api/canvas-ui-client-tool.js.map +1 -0
  240. package/dist/api/client-source.cjs +2 -0
  241. package/dist/api/client-source.cjs.map +1 -0
  242. package/dist/api/client-source.d.ts +9 -0
  243. package/dist/api/client-source.js +10 -0
  244. package/dist/api/client-source.js.map +1 -0
  245. package/dist/api/cloud/client.cjs +2 -0
  246. package/dist/api/cloud/client.cjs.map +1 -0
  247. package/dist/api/cloud/client.d.ts +4 -0
  248. package/dist/api/cloud/client.js +38 -0
  249. package/dist/api/cloud/client.js.map +1 -0
  250. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  251. package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
  252. package/dist/api/cloud/conversation-service.api.js +33 -31
  253. package/dist/api/cloud/conversation-service.api.js.map +1 -1
  254. package/dist/api/cloud/organization-service.api.cjs +1 -1
  255. package/dist/api/cloud/organization-service.api.cjs.map +1 -1
  256. package/dist/api/cloud/organization-service.api.js +11 -10
  257. package/dist/api/cloud/organization-service.api.js.map +1 -1
  258. package/dist/api/cloud/proxy.cjs +1 -1
  259. package/dist/api/cloud/proxy.cjs.map +1 -1
  260. package/dist/api/cloud/proxy.d.ts +2 -40
  261. package/dist/api/cloud/proxy.js +14 -39
  262. package/dist/api/cloud/proxy.js.map +1 -1
  263. package/dist/api/conversation-metadata-store.cjs +1 -1
  264. package/dist/api/conversation-metadata-store.cjs.map +1 -1
  265. package/dist/api/conversation-metadata-store.d.ts +2 -9
  266. package/dist/api/conversation-metadata-store.js +13 -9
  267. package/dist/api/conversation-metadata-store.js.map +1 -1
  268. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  269. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  270. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +2 -1
  271. package/dist/api/conversation-service/agent-server-conversation-service.api.js +98 -88
  272. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  273. package/dist/api/conversation-service/agent-server-conversation-service.types.d.ts +5 -0
  274. package/dist/api/device-flow-client.cjs +1 -1
  275. package/dist/api/device-flow-client.cjs.map +1 -1
  276. package/dist/api/device-flow-client.d.ts +4 -58
  277. package/dist/api/device-flow-client.js +8 -98
  278. package/dist/api/device-flow-client.js.map +1 -1
  279. package/dist/api/git-service/agent-server-git-service.api.cjs +1 -1
  280. package/dist/api/git-service/agent-server-git-service.api.cjs.map +1 -1
  281. package/dist/api/git-service/agent-server-git-service.api.d.ts +10 -2
  282. package/dist/api/git-service/agent-server-git-service.api.js +107 -40
  283. package/dist/api/git-service/agent-server-git-service.api.js.map +1 -1
  284. package/dist/api/open-hands.types.d.ts +11 -0
  285. package/dist/api/secrets-service.cjs +1 -1
  286. package/dist/api/secrets-service.cjs.map +1 -1
  287. package/dist/api/secrets-service.js +15 -14
  288. package/dist/api/secrets-service.js.map +1 -1
  289. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
  290. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
  291. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +73 -64
  292. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
  293. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.cjs +2 -2
  294. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.cjs.map +1 -1
  295. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.js +2 -0
  296. package/dist/components/conversation-events/chat/event-content-helpers/get-observation-content.js.map +1 -1
  297. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  298. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  299. package/dist/components/features/backends/backend-form-modal.d.ts +4 -2
  300. package/dist/components/features/backends/backend-form-modal.js +202 -188
  301. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  302. package/dist/components/features/backends/backend-row.cjs +1 -1
  303. package/dist/components/features/backends/backend-row.cjs.map +1 -1
  304. package/dist/components/features/backends/backend-row.js +2 -1
  305. package/dist/components/features/backends/backend-row.js.map +1 -1
  306. package/dist/components/features/backends/backend-selector.cjs +1 -1
  307. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  308. package/dist/components/features/backends/backend-selector.js +51 -51
  309. package/dist/components/features/backends/backend-selector.js.map +1 -1
  310. package/dist/components/features/backends/device-flow-auth.cjs +1 -1
  311. package/dist/components/features/backends/device-flow-auth.cjs.map +1 -1
  312. package/dist/components/features/backends/device-flow-auth.d.ts +4 -1
  313. package/dist/components/features/backends/device-flow-auth.js +38 -38
  314. package/dist/components/features/backends/device-flow-auth.js.map +1 -1
  315. package/dist/components/features/chat/chat-interface.cjs +2 -2
  316. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  317. package/dist/components/features/chat/chat-interface.js +173 -165
  318. package/dist/components/features/chat/chat-interface.js.map +1 -1
  319. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  320. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  321. package/dist/components/features/chat/components/chat-input-actions.d.ts +2 -1
  322. package/dist/components/features/chat/components/chat-input-actions.js +44 -43
  323. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  324. package/dist/components/features/chat/components/chat-input-container.cjs +1 -1
  325. package/dist/components/features/chat/components/chat-input-container.cjs.map +1 -1
  326. package/dist/components/features/chat/components/chat-input-container.d.ts +2 -1
  327. package/dist/components/features/chat/components/chat-input-container.js +20 -19
  328. package/dist/components/features/chat/components/chat-input-container.js.map +1 -1
  329. package/dist/components/features/chat/components/chat-input-profile-picker.cjs +1 -1
  330. package/dist/components/features/chat/components/chat-input-profile-picker.cjs.map +1 -1
  331. package/dist/components/features/chat/components/chat-input-profile-picker.js +44 -32
  332. package/dist/components/features/chat/components/chat-input-profile-picker.js.map +1 -1
  333. package/dist/components/features/chat/components/resolve-picker-kind.cjs +1 -1
  334. package/dist/components/features/chat/components/resolve-picker-kind.cjs.map +1 -1
  335. package/dist/components/features/chat/components/resolve-picker-kind.d.ts +10 -22
  336. package/dist/components/features/chat/components/resolve-picker-kind.js +6 -3
  337. package/dist/components/features/chat/components/resolve-picker-kind.js.map +1 -1
  338. package/dist/components/features/chat/custom-chat-input.cjs +1 -1
  339. package/dist/components/features/chat/custom-chat-input.cjs.map +1 -1
  340. package/dist/components/features/chat/custom-chat-input.d.ts +2 -1
  341. package/dist/components/features/chat/custom-chat-input.js +52 -51
  342. package/dist/components/features/chat/custom-chat-input.js.map +1 -1
  343. package/dist/components/features/chat/interactive-chat-box.cjs +1 -1
  344. package/dist/components/features/chat/interactive-chat-box.cjs.map +1 -1
  345. package/dist/components/features/chat/interactive-chat-box.d.ts +2 -1
  346. package/dist/components/features/chat/interactive-chat-box.js +9 -8
  347. package/dist/components/features/chat/interactive-chat-box.js.map +1 -1
  348. package/dist/components/features/diff-viewer/commit-list.cjs +2 -0
  349. package/dist/components/features/diff-viewer/commit-list.cjs.map +1 -0
  350. package/dist/components/features/diff-viewer/commit-list.d.ts +13 -0
  351. package/dist/components/features/diff-viewer/commit-list.js +27 -0
  352. package/dist/components/features/diff-viewer/commit-list.js.map +1 -0
  353. package/dist/components/features/diff-viewer/commit-row.cjs +2 -0
  354. package/dist/components/features/diff-viewer/commit-row.cjs.map +1 -0
  355. package/dist/components/features/diff-viewer/commit-row.d.ts +14 -0
  356. package/dist/components/features/diff-viewer/commit-row.js +59 -0
  357. package/dist/components/features/diff-viewer/commit-row.js.map +1 -0
  358. package/dist/components/features/diff-viewer/file-diff-viewer.cjs +1 -1
  359. package/dist/components/features/diff-viewer/file-diff-viewer.cjs.map +1 -1
  360. package/dist/components/features/diff-viewer/file-diff-viewer.d.ts +7 -1
  361. package/dist/components/features/diff-viewer/file-diff-viewer.js +40 -39
  362. package/dist/components/features/diff-viewer/file-diff-viewer.js.map +1 -1
  363. package/dist/components/features/plugins/plugin-detail-modal.d.ts +2 -1
  364. package/dist/components/shared/modals/settings/settings-modal.cjs +1 -1
  365. package/dist/components/shared/modals/settings/settings-modal.cjs.map +1 -1
  366. package/dist/components/shared/modals/settings/settings-modal.js +23 -22
  367. package/dist/components/shared/modals/settings/settings-modal.js.map +1 -1
  368. package/dist/config/defaults.cjs +1 -1
  369. package/dist/config/defaults.cjs.map +1 -1
  370. package/dist/config/defaults.js +5 -4
  371. package/dist/config/defaults.js.map +1 -1
  372. package/dist/constants/acp-providers.cjs.map +1 -1
  373. package/dist/constants/acp-providers.d.ts +1 -1
  374. package/dist/constants/acp-providers.js.map +1 -1
  375. package/dist/constants/canvas-ui.cjs +2 -0
  376. package/dist/constants/canvas-ui.cjs.map +1 -0
  377. package/dist/constants/canvas-ui.d.ts +8 -0
  378. package/dist/constants/canvas-ui.js +6 -0
  379. package/dist/constants/canvas-ui.js.map +1 -0
  380. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  381. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  382. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  383. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  384. package/dist/hooks/mutation/use-create-conversation.d.ts +2 -1
  385. package/dist/hooks/mutation/use-create-conversation.js +52 -52
  386. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  387. package/dist/hooks/query/query-keys.cjs +1 -1
  388. package/dist/hooks/query/query-keys.cjs.map +1 -1
  389. package/dist/hooks/query/query-keys.d.ts +2 -0
  390. package/dist/hooks/query/query-keys.js +4 -1
  391. package/dist/hooks/query/query-keys.js.map +1 -1
  392. package/dist/hooks/query/use-backends-health.cjs +1 -1
  393. package/dist/hooks/query/use-backends-health.cjs.map +1 -1
  394. package/dist/hooks/query/use-backends-health.js +50 -48
  395. package/dist/hooks/query/use-backends-health.js.map +1 -1
  396. package/dist/hooks/query/use-cloud-organizations.cjs +1 -1
  397. package/dist/hooks/query/use-cloud-organizations.cjs.map +1 -1
  398. package/dist/hooks/query/use-cloud-organizations.d.ts +1 -0
  399. package/dist/hooks/query/use-cloud-organizations.js +5 -2
  400. package/dist/hooks/query/use-cloud-organizations.js.map +1 -1
  401. package/dist/hooks/query/use-commit-changes.cjs +2 -0
  402. package/dist/hooks/query/use-commit-changes.cjs.map +1 -0
  403. package/dist/hooks/query/use-commit-changes.d.ts +5 -0
  404. package/dist/hooks/query/use-commit-changes.js +34 -0
  405. package/dist/hooks/query/use-commit-changes.js.map +1 -0
  406. package/dist/hooks/query/use-task-polling.cjs +1 -1
  407. package/dist/hooks/query/use-task-polling.cjs.map +1 -1
  408. package/dist/hooks/query/use-task-polling.d.ts +3 -2
  409. package/dist/hooks/query/use-task-polling.js +53 -40
  410. package/dist/hooks/query/use-task-polling.js.map +1 -1
  411. package/dist/hooks/query/use-unified-git-commits.cjs +2 -0
  412. package/dist/hooks/query/use-unified-git-commits.cjs.map +1 -0
  413. package/dist/hooks/query/use-unified-git-commits.d.ts +10 -0
  414. package/dist/hooks/query/use-unified-git-commits.js +41 -0
  415. package/dist/hooks/query/use-unified-git-commits.js.map +1 -0
  416. package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
  417. package/dist/hooks/query/use-unified-git-diff.cjs.map +1 -1
  418. package/dist/hooks/query/use-unified-git-diff.d.ts +5 -0
  419. package/dist/hooks/query/use-unified-git-diff.js +11 -4
  420. package/dist/hooks/query/use-unified-git-diff.js.map +1 -1
  421. package/dist/hooks/use-acp-model-context.cjs +1 -1
  422. package/dist/hooks/use-acp-model-context.cjs.map +1 -1
  423. package/dist/hooks/use-acp-model-context.d.ts +4 -1
  424. package/dist/hooks/use-acp-model-context.js +9 -8
  425. package/dist/hooks/use-acp-model-context.js.map +1 -1
  426. package/dist/hooks/use-auto-refresh-files-on-edit.cjs +1 -1
  427. package/dist/hooks/use-auto-refresh-files-on-edit.cjs.map +1 -1
  428. package/dist/hooks/use-auto-refresh-files-on-edit.js +1 -1
  429. package/dist/hooks/use-auto-refresh-files-on-edit.js.map +1 -1
  430. package/dist/hooks/use-chat-input-model-state.cjs.map +1 -1
  431. package/dist/hooks/use-chat-input-model-state.d.ts +2 -1
  432. package/dist/hooks/use-chat-input-model-state.js.map +1 -1
  433. package/dist/hooks/use-chat-input-profile-state.cjs +1 -1
  434. package/dist/hooks/use-chat-input-profile-state.cjs.map +1 -1
  435. package/dist/hooks/use-chat-input-profile-state.d.ts +1 -10
  436. package/dist/hooks/use-chat-input-profile-state.js +42 -15
  437. package/dist/hooks/use-chat-input-profile-state.js.map +1 -1
  438. package/dist/hooks/use-device-flow.cjs +1 -1
  439. package/dist/hooks/use-device-flow.cjs.map +1 -1
  440. package/dist/hooks/use-device-flow.d.ts +4 -1
  441. package/dist/hooks/use-device-flow.js +42 -40
  442. package/dist/hooks/use-device-flow.js.map +1 -1
  443. package/dist/hooks/use-tracking.cjs.map +1 -1
  444. package/dist/hooks/use-tracking.d.ts +2 -1
  445. package/dist/hooks/use-tracking.js.map +1 -1
  446. package/dist/i18n/declaration.cjs +1 -1
  447. package/dist/i18n/declaration.cjs.map +1 -1
  448. package/dist/i18n/declaration.d.ts +9 -0
  449. package/dist/i18n/declaration.js +1 -1
  450. package/dist/i18n/declaration.js.map +1 -1
  451. package/dist/i18n/index.cjs +1 -1
  452. package/dist/i18n/index.cjs.map +1 -1
  453. package/dist/i18n/index.js +19 -18
  454. package/dist/i18n/index.js.map +1 -1
  455. package/dist/i18n/translation.cjs +2 -2
  456. package/dist/i18n/translation.cjs.map +1 -1
  457. package/dist/i18n/translation.js +153 -0
  458. package/dist/i18n/translation.js.map +1 -1
  459. package/dist/index.cjs +1 -1
  460. package/dist/index.js +32 -32
  461. package/dist/lib/index.cjs +1 -1
  462. package/dist/lib/index.js +1 -1
  463. package/dist/locales/ar/openhands.json +9 -0
  464. package/dist/locales/ca/openhands.json +9 -0
  465. package/dist/locales/de/openhands.json +9 -0
  466. package/dist/locales/en/openhands.json +9 -0
  467. package/dist/locales/es/openhands.json +9 -0
  468. package/dist/locales/fr/openhands.json +9 -0
  469. package/dist/locales/it/openhands.json +9 -0
  470. package/dist/locales/ja/openhands.json +9 -0
  471. package/dist/locales/ko-KR/openhands.json +9 -0
  472. package/dist/locales/no/openhands.json +9 -0
  473. package/dist/locales/pt/openhands.json +9 -0
  474. package/dist/locales/tr/openhands.json +9 -0
  475. package/dist/locales/uk/openhands.json +9 -0
  476. package/dist/locales/zh-CN/openhands.json +9 -0
  477. package/dist/locales/zh-TW/openhands.json +9 -0
  478. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.cjs +2 -0
  479. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.cjs.map +1 -0
  480. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.js +351 -0
  481. package/dist/node_modules/@openhands/typescript-client/dist/client/cloud-client.js.map +1 -0
  482. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.cjs +2 -0
  483. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.cjs.map +1 -0
  484. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.js +106 -0
  485. package/dist/node_modules/@openhands/typescript-client/dist/client/device-flow-client.js.map +1 -0
  486. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs +2 -0
  487. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs.map +1 -0
  488. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js +69 -0
  489. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js.map +1 -0
  490. package/dist/node_modules/@openhands/typescript-client/dist/clients.cjs +1 -1
  491. package/dist/node_modules/@openhands/typescript-client/dist/clients.js +3 -0
  492. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  493. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  494. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +18 -2
  495. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  496. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs.map +1 -1
  497. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js.map +1 -1
  498. package/dist/node_modules/axios/index.cjs +1 -1
  499. package/dist/node_modules/axios/index.js +1 -1
  500. package/dist/package.cjs +1 -1
  501. package/dist/package.cjs.map +1 -1
  502. package/dist/package.js +2 -2
  503. package/dist/package.js.map +1 -1
  504. package/dist/routes/app-settings.cjs +1 -1
  505. package/dist/routes/app-settings.cjs.map +1 -1
  506. package/dist/routes/app-settings.js +67 -66
  507. package/dist/routes/app-settings.js.map +1 -1
  508. package/dist/routes/commits-tab.cjs +2 -0
  509. package/dist/routes/commits-tab.cjs.map +1 -0
  510. package/dist/routes/commits-tab.d.ts +8 -0
  511. package/dist/routes/commits-tab.js +45 -0
  512. package/dist/routes/commits-tab.js.map +1 -0
  513. package/dist/routes/files-tab.cjs +1 -1
  514. package/dist/routes/files-tab.cjs.map +1 -1
  515. package/dist/routes/files-tab.js +148 -127
  516. package/dist/routes/files-tab.js.map +1 -1
  517. package/dist/services/cloud-funnel-analytics.cjs +2 -0
  518. package/dist/services/cloud-funnel-analytics.cjs.map +1 -0
  519. package/dist/services/cloud-funnel-analytics.d.ts +12 -0
  520. package/dist/services/cloud-funnel-analytics.js +53 -0
  521. package/dist/services/cloud-funnel-analytics.js.map +1 -0
  522. package/dist/services/telemetry.cjs +1 -1
  523. package/dist/services/telemetry.cjs.map +1 -1
  524. package/dist/services/telemetry.js +49 -47
  525. package/dist/services/telemetry.js.map +1 -1
  526. package/dist/types/agent-server/core/base/action.d.ts +5 -4
  527. package/dist/types/agent-server/core/base/base.d.ts +3 -2
  528. package/dist/types/agent-server/core/base/observation.d.ts +1 -1
  529. package/dist/types/agent-server/type-guards.cjs +1 -1
  530. package/dist/types/agent-server/type-guards.cjs.map +1 -1
  531. package/dist/types/agent-server/type-guards.d.ts +3 -5
  532. package/dist/types/agent-server/type-guards.js +2 -1
  533. package/dist/types/agent-server/type-guards.js.map +1 -1
  534. package/dist/types/automation.d.ts +5 -0
  535. package/dist/utils/api-error-message.d.ts +11 -0
  536. package/dist/utils/automation-timeout.d.ts +19 -0
  537. package/dist/utils/base-path.cjs +2 -0
  538. package/dist/utils/base-path.cjs.map +1 -0
  539. package/dist/utils/base-path.d.ts +2 -0
  540. package/dist/utils/base-path.js +23 -0
  541. package/dist/utils/base-path.js.map +1 -0
  542. package/package.json +2 -2
  543. package/scripts/check-sdk-version-sync.mjs +6 -6
  544. package/scripts/dev-safe.mjs +7 -8
  545. package/scripts/dev-static.mjs +3 -0
  546. package/scripts/dev-with-automation.mjs +3 -0
  547. package/scripts/docker-build.mjs +4 -0
  548. package/scripts/static-server.mjs +93 -7
  549. package/tools/canvas_ui_tool.py +14 -8
  550. package/build/assets/active-backend-context-hxq31W25.js +0 -1
  551. package/build/assets/agent-server-client-options-DVLRd9sw.js +0 -1
  552. package/build/assets/agent-server-conversation-service.api-CuSdr4Qb.js +0 -5
  553. package/build/assets/agent-settings-D_yOy8XB.js +0 -1
  554. package/build/assets/api-key-entry-screen-Dl0qVYi4.js +0 -1
  555. package/build/assets/app-settings-yVizGXox.js +0 -1
  556. package/build/assets/automation-detail-dvJfLs5u.js +0 -2
  557. package/build/assets/automation-export-BPcgLqPD.js +0 -2
  558. package/build/assets/automations-list-CUXogTe0.js +0 -1
  559. package/build/assets/backend-form-modal-CQJGmItq.js +0 -1
  560. package/build/assets/browser-store-BI6uklCG.js +0 -1
  561. package/build/assets/check-Bvi71siz.js +0 -1
  562. package/build/assets/condenser-settings-Ca8fYZ_Z.js +0 -1
  563. package/build/assets/conversation-9NBSRoR9.js +0 -1
  564. package/build/assets/conversation-BXg6Oti2.js +0 -89
  565. package/build/assets/conversation-panel-CZri9Rup.js +0 -1
  566. package/build/assets/conversation-service.api-DPBQvTKD.js +0 -1
  567. package/build/assets/conversation-state-store-B2Bp32XI.js +0 -1
  568. package/build/assets/declaration-gq8V5eTU.js +0 -1
  569. package/build/assets/files-tab-UVoray88.js +0 -1
  570. package/build/assets/git-provider-icon-o6hdnJUB.js +0 -1
  571. package/build/assets/home-D1IHvGTN.js +0 -1
  572. package/build/assets/index-redirect-CUUJiNki.js +0 -1
  573. package/build/assets/install-server-modal-BbuQaA2w.js +0 -1
  574. package/build/assets/lesson-plan-jC0HBoe6.js +0 -4
  575. package/build/assets/llm-not-configured-banner-DoARbcdL.js +0 -27
  576. package/build/assets/llm-settings-4JMLV8mq.js +0 -1
  577. package/build/assets/llm-settings-D91gMw3U.js +0 -1
  578. package/build/assets/llm-subscription-service-CKyaV24y.js +0 -1
  579. package/build/assets/loader-circle-BEykUKYG.js +0 -1
  580. package/build/assets/manage-backends-modal-DkWAMSle.js +0 -1
  581. package/build/assets/manifest-aa5c29f6.js +0 -1
  582. package/build/assets/model-selector-BfqpMkSJ.js +0 -1
  583. package/build/assets/mouse-pointer-click-PdEQPL6v.js +0 -1
  584. package/build/assets/onboarding-Bu-HN1ze.js +0 -1
  585. package/build/assets/onboarding-modal-CZgjs5Bx.js +0 -1
  586. package/build/assets/option-service.api-BrZ5q87N.js +0 -1
  587. package/build/assets/organization-service.api-Bk4pCGU1.js +0 -1
  588. package/build/assets/path-utils-CSmuZoix.js +0 -1
  589. package/build/assets/plugins-management-service-BRBLxvUl.js +0 -1
  590. package/build/assets/profiles-service.api-BMXQ9pZC.js +0 -1
  591. package/build/assets/providers-BpO967xS.js +0 -1
  592. package/build/assets/proxy-BgJLtxu7.js +0 -1
  593. package/build/assets/root-BM5WgQ4_.js +0 -2
  594. package/build/assets/root-V8fkplMy.css +0 -1
  595. package/build/assets/root-layout-BdoyoobY.js +0 -2
  596. package/build/assets/schema-field-BSHrUMme.js +0 -1
  597. package/build/assets/sdk-section-page-t82gsOqa.js +0 -1
  598. package/build/assets/search-4DHskuhE.js +0 -1
  599. package/build/assets/secrets-service-B2GqPV0N.js +0 -1
  600. package/build/assets/settings-DmPLkgq0.js +0 -1
  601. package/build/assets/settings-modal-CwrZKrTq.js +0 -1
  602. package/build/assets/settings-service.api-Ce-ub11I.js +0 -1
  603. package/build/assets/shared-conversation-BDREQPnE.js +0 -1
  604. package/build/assets/skills-plugins-2Y44YHbO.js +0 -1
  605. package/build/assets/use-acp-credential-form-MzZM6D7D.js +0 -1
  606. package/build/assets/use-agent-profiles-DwZxnwO5.js +0 -1
  607. package/build/assets/use-backends-health-CRHzuvxO.js +0 -1
  608. package/build/assets/use-cloud-current-user-id-utFreJ0i.js +0 -1
  609. package/build/assets/use-config-mJrtZHP1.js +0 -1
  610. package/build/assets/use-create-conversation-DdqsP78F.js +0 -1
  611. package/build/assets/use-is-authed-B6M0mcGL.js +0 -1
  612. package/build/assets/use-llm-profiles-RC-PkpBl.js +0 -1
  613. package/build/assets/use-plugins-marketplace-FHlbeQ4c.js +0 -1
  614. package/build/assets/use-runtime-is-ready-RKj2WmEh.js +0 -1
  615. package/build/assets/use-save-agent-profile-tvDP15mh.js +0 -1
  616. package/build/assets/use-settings-CH5LzXD8.js +0 -1
  617. package/build/assets/use-settings-nav-items-Dwktv8wo.js +0 -16
  618. package/build/assets/use-skills-CSzSL6HG.js +0 -1
  619. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~lf310vj7-CSVPGId3.js +0 -10
  620. package/build/assets/vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~nfpll1wp-BCpa95De.js +0 -177
  621. package/build/assets/vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CqXiHwjU.js +0 -2
  622. package/build/assets/x-Cb0WAtak.js +0 -1
  623. /package/build/assets/{__vite-browser-external-4xJsU2-u.js → __vite-browser-external-DU-Cb_sT.js} +0 -0
  624. /package/build/assets/{automation-BU8zu-FP.js → automation-BVEmGghX.js} +0 -0
  625. /package/build/assets/{checkmark-h7udgXMM.js → checkmark-DhcMGwtO.js} +0 -0
  626. /package/build/assets/{close-p3Op5lxI.js → close-DO5Rq_CS.js} +0 -0
  627. /package/build/assets/{code-tag-DYlAhEQY.js → code-tag-BvjtzUsr.js} +0 -0
  628. /package/build/assets/{color-themes-DPBzNtVg.js → color-themes-bVMmJJG6.js} +0 -0
  629. /package/build/assets/{common-Dh7OqABE.js → common-CxbUL3nR.js} +0 -0
  630. /package/build/assets/{conversation-local-storage-F6zVMM--.js → conversation-local-storage-B31voBg7.js} +0 -0
  631. /package/build/assets/{copy-BLmGeJcd.js → copy-DLUw2us-.js} +0 -0
  632. /package/build/assets/{createLucideIcon-CVAsW2Yo.js → createLucideIcon-D4Tak8A1.js} +0 -0
  633. /package/build/assets/{dist-CodhvuAP.js → dist-Dj01FXbZ.js} +0 -0
  634. /package/build/assets/{environment-switch-store-DkjlKWzN.js → environment-switch-store-D9hB9PvY.js} +0 -0
  635. /package/build/assets/{extension-module-card-classes-9V3J9J_b.js → extension-module-card-classes-BlO4w702.js} +0 -0
  636. /package/build/assets/{folder-mMWFvaw0.js → folder-ynxP2EIL.js} +0 -0
  637. /package/build/assets/{get-language-from-path-CPNnuVhI.js → get-language-from-path-rWEQrOGU.js} +0 -0
  638. /package/build/assets/{git-status-mapper-BI8FyUVp.js → git-status-mapper-C3rfzUex.js} +0 -0
  639. /package/build/assets/{handle-capture-consent-CPMXU29K.js → handle-capture-consent-DDnXMC9Y.js} +0 -0
  640. /package/build/assets/{iconBase-DaTRRUjy.js → iconBase-CuvxAQZ5.js} +0 -0
  641. /package/build/assets/{link-external-DfKQroKX.js → link-external-BVDiJfZt.js} +0 -0
  642. /package/build/assets/{llm-subscription-DtsX5Lhg.js → llm-subscription-BkdHwtQe.js} +0 -0
  643. /package/build/assets/{map-provider-BM2SJarp.js → map-provider-XSFHmdDs.js} +0 -0
  644. /package/build/assets/{middleware-DYHYNAnP.js → middleware-CWYDMVuG.js} +0 -0
  645. /package/build/assets/{modal-backdrop-zVwfOJZr.js → modal-backdrop-Dsmzgr5s.js} +0 -0
  646. /package/build/assets/{navigation-context-DPs2G0X8.js → navigation-context-SjzfpygH.js} +0 -0
  647. /package/build/assets/{objectWithoutPropertiesLoose-BgYIAs2y.js → objectWithoutPropertiesLoose-CQwg6tC0.js} +0 -0
  648. /package/build/assets/{openhands-logo-bcNw5bkY.js → openhands-logo-C-gDp1wr.js} +0 -0
  649. /package/build/assets/{react-DPDgL6du.js → react-ao3tWj6K.js} +0 -0
  650. /package/build/assets/{search-BsjCEFNf.js → search-pb4UKTfK.js} +0 -0
  651. /package/build/assets/{settings-mQUckT0g.js → settings-DksoDv5z.js} +0 -0
  652. /package/build/assets/{settings-gear-MXCyZW0x.js → settings-gear-D_HVvE5L.js} +0 -0
  653. /package/build/assets/{settings-like-page-layout-classes-DENKlZpD.js → settings-like-page-layout-classes-DLrsPCaI.js} +0 -0
  654. /package/build/assets/{settings-section-header-context-COMOIaxV.js → settings-section-header-context-C2MUIG88.js} +0 -0
  655. /package/build/assets/{skills-9cDZONKh.js → skills-CH4xgIh-.js} +0 -0
  656. /package/build/assets/{switch-skeleton-DqQwalyu.js → switch-skeleton-BpSOvZ-n.js} +0 -0
  657. /package/build/assets/{terminal-BuLtIrb9.js → terminal-Dokfkxn0.js} +0 -0
  658. /package/build/assets/{u-check-circle-BqoIxNz_.js → u-check-circle-DPWVO-9N.js} +0 -0
  659. /package/build/assets/{u-check-circle-half-LiwZkuFe.js → u-check-circle-half-BGNAAjOW.js} +0 -0
  660. /package/build/assets/{u-edit-DJ90xc8Z.js → u-edit-BIskOoEG.js} +0 -0
  661. /package/build/assets/{use-breakpoint-B-O0lQ84.js → use-breakpoint-OjLm94cF.js} +0 -0
  662. /package/build/assets/{use-click-outside-element-Bf4Omzot.js → use-click-outside-element-Jpt2WWd7.js} +0 -0
  663. /package/build/assets/{use-onboarding-completion-C_Swglzp.js → use-onboarding-completion-DkRpDTsD.js} +0 -0
  664. /package/build/assets/{v4-DbCkTSoU.js → v4-DPgam_jW.js} +0 -0
  665. /package/build/assets/{vendor~browser-Dqd33JyP.js → vendor~browser-BvEKaLBz.js} +0 -0
  666. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-DHDkknt5.js → vendor~conversation-panel~conversation~alert-banner-CznX01nZ.js} +0 -0
  667. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~epa72zsm-B4rXs7Zd.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~e0djk9ee-Cw6LUe8p.js} +0 -0
  668. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~ivvrgjsa-BlIZPCXX.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g9hift2g-DmmpG9SI.js} +0 -0
  669. /package/build/assets/{vendor~entry.client~root~root-layout~index-redirect~home~conversation-panel~conversation~la~byh8a1g4-XNPdpuO8.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~jkpzbynd-CkpBIhVe.js} +0 -0
  670. /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DTzw2x50.js → vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BuL5kjfS.js} +0 -0
  671. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~skills-settings~automations-~ki5qnp0d-rV6d1Iv_.js → vendor~root-layout~index-home~home~conversation-panel~conversation~launch~skills-settings~a~j2jlu22h-DUYl8gbZ.js} +0 -0
  672. /package/build/assets/{vendor~root-layout~home~mcp~automations-list~onboarding-modal-CXyYYvYh.js → vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-pIgQDxTc.js} +0 -0
  673. /package/build/assets/{vendor~terminal-aeP3PnKf.js → vendor~terminal-jAaNQWS9.js} +0 -0
  674. /package/build/assets/{x-mark-Batp6gXa.js → x-mark-C5przfXl.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"conversation-websocket-context.js","names":[],"sources":["../../src/contexts/conversation-websocket-context.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n useCallback,\n useMemo,\n useRef,\n} from \"react\";\nimport { ConversationClient } from \"@openhands/typescript-client/clients\";\n\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePostHog } from \"posthog-js/react\";\nimport { useWebSocket, WebSocketHookOptions } from \"#/hooks/use-websocket\";\nimport { SERVER_CONNECTION_ERROR_MESSAGE } from \"#/constants/server-connection-error\";\nimport { useEventStore } from \"#/stores/use-event-store\";\nimport { useErrorMessageStore } from \"#/stores/error-message-store\";\nimport { useOptimisticUserMessageStore } from \"#/stores/optimistic-user-message-store\";\nimport { useConversationStateStore } from \"#/stores/conversation-state-store\";\nimport { useCommandStore } from \"#/stores/command-store\";\nimport { useBrowserStore } from \"#/stores/browser-store\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport {\n isAgentServerEvent,\n isAgentErrorEvent,\n isUserMessageEvent,\n isActionEvent,\n isConversationStateUpdateEvent,\n isFullStateConversationStateUpdateEvent,\n isAgentStatusConversationStateUpdateEvent,\n isStatsConversationStateUpdateEvent,\n isGoalConversationStateUpdateEvent,\n isExecuteBashActionEvent,\n isExecuteBashObservationEvent,\n isDisplayableErrorEvent,\n isPlanningFileEditorObservationEvent,\n isBrowserObservationEvent,\n isBrowserNavigateActionEvent,\n isSwitchLLMObservationEvent,\n isCanvasUIActionEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { handleCanvasUIAction } from \"#/services/canvas-ui\";\nimport { ConversationStateUpdateEventStats } from \"#/types/agent-server/core/events/conversation-state-event\";\nimport type {\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"#/types/agent-server/core/events/conversation-state-event\";\nimport { handleActionEventCacheInvalidation } from \"#/utils/cache-utils\";\nimport { buildWebSocketUrl } from \"#/utils/websocket-url\";\nimport type {\n AppConversation,\n SendMessageRequest,\n} from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport EventService from \"#/api/event-service/event-service.api\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { trackError } from \"#/utils/error-handler\";\nimport { useReadConversationFile } from \"#/hooks/mutation/use-read-conversation-file\";\nimport useMetricsStore from \"#/stores/metrics-store\";\nimport { useConversationHistory } from \"#/hooks/query/use-conversation-history\";\nimport { setConversationState } from \"#/utils/conversation-local-storage\";\nimport {\n recordModelSwitchMessage,\n seedModelSwitchesFromHistory,\n} from \"#/hooks/chat/record-model-switch-message\";\nimport {\n invalidateConversationQueries,\n updateConversationLlmModelInCache,\n} from \"#/hooks/mutation/conversation-mutation-utils\";\nimport {\n getStoredConversationMetadata,\n setStoredConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\n\nexport type WebSocketConnectionState =\n | \"CONNECTING\"\n | \"OPEN\"\n | \"CLOSED\"\n | \"CLOSING\";\n\ninterface SendMessageResult {\n queued: boolean; // true if message was queued for later delivery, false if sent immediately\n}\n\ninterface ConversationWebSocketContextType {\n connectionState: WebSocketConnectionState;\n sendMessage: (message: SendMessageRequest) => Promise<SendMessageResult>;\n isLoadingHistory: boolean;\n reconnect: () => void;\n}\n\nconst ConversationWebSocketContext = createContext<\n ConversationWebSocketContextType | undefined\n>(undefined);\n\n/**\n * Extract the text body of an echoed user `MessageEvent` for matching against\n * the optimistic pending-message queue. The server wraps the original\n * `args.content` string in one or more `TextContent` entries (alongside any\n * `ImageContent` entries for inline images), so concatenating the `text`\n * fields gives us back the exact prompt we sent.\n */\nfunction extractMessageEventText(\n event: import(\"#/types/agent-server/core/events/message-event\").MessageEvent,\n): string {\n return event.llm_message.content\n .filter(\n (part): part is { type: \"text\"; text: string } => part.type === \"text\",\n )\n .map((part) => part.text)\n .join(\"\");\n}\n\nexport function ConversationWebSocketProvider({\n children,\n conversationId,\n conversationUrl,\n sessionApiKey,\n subConversations,\n subConversationIds,\n}: {\n children: React.ReactNode;\n conversationId?: string;\n conversationUrl?: string | null;\n sessionApiKey?: string | null;\n subConversations?: AppConversation[];\n subConversationIds?: string[];\n}) {\n // Separate connection state tracking for each WebSocket\n const [mainConnectionState, setMainConnectionState] =\n useState<WebSocketConnectionState>(\"CONNECTING\");\n const [planningConnectionState, setPlanningConnectionState] =\n useState<WebSocketConnectionState>(\"CONNECTING\");\n\n // Track if we've ever successfully connected for each connection\n // Don't show errors until after first successful connection\n const hasConnectedRefMain = React.useRef(false);\n const hasConnectedRefPlanning = React.useRef(false);\n\n const posthog = usePostHog();\n const queryClient = useQueryClient();\n const addEvent = useEventStore((state) => state.addEvent);\n const addEvents = useEventStore((state) => state.addEvents);\n const clearEventsForConversation = useEventStore(\n (state) => state.clearEventsForConversation,\n );\n const { setErrorMessage, removeErrorMessage, clearConnectionError } =\n useErrorMessageStore();\n const consumeMatchingPendingMessage = useOptimisticUserMessageStore(\n (state) => state.consumeMatchingPendingMessage,\n );\n const { setExecutionStatus } = useConversationStateStore();\n const { appendInput, appendOutput } = useCommandStore();\n const resetBrowserStore = useBrowserStore((state) => state.reset);\n\n // History loading state.\n // - Main conversation history is now loaded via REST (`useConversationHistory`),\n // so its loading state mirrors the REST query state (see below).\n // - Planning sub-conversation history still streams over the WebSocket using\n // `resend_mode='all'`, so we keep the count-based detection for it.\n const [isLoadingHistoryPlanning, setIsLoadingHistoryPlanning] =\n useState(true);\n const [expectedEventCountPlanning, setExpectedEventCountPlanning] = useState<\n number | null\n >(null);\n\n const { setPlanContent } = useConversationStore();\n\n // Hook for reading conversation file\n const { mutate: readConversationFile } = useReadConversationFile();\n\n // Track planning-agent received events (still WS-driven).\n const receivedEventCountRefPlanning = useRef(0);\n\n // Track the latest PlanningFileEditorObservation for Plan.md during history replay\n const latestPlanningFileEventRef = useRef<{\n path: string;\n conversationId: string;\n } | null>(null);\n\n const isPlanFilePath = (path: string | null): boolean =>\n path?.toUpperCase().endsWith(\"PLAN.MD\") ?? false;\n\n const handleNonErrorEvent = useCallback(() => {\n // A normal event means connectivity recovered: clear a transient connection\n // error, but keep sticky conversation errors (e.g. a wrong API key).\n clearConnectionError();\n }, [clearConnectionError]);\n\n // Helper function to update metrics from stats event\n const updateMetricsFromStats = useCallback(\n (event: ConversationStateUpdateEventStats) => {\n if (event.value.usage_to_metrics?.agent) {\n const agentMetrics = event.value.usage_to_metrics.agent;\n const metrics = {\n cost: agentMetrics.accumulated_cost,\n max_budget_per_task: agentMetrics.max_budget_per_task ?? null,\n usage: agentMetrics.accumulated_token_usage\n ? {\n prompt_tokens:\n agentMetrics.accumulated_token_usage.prompt_tokens,\n completion_tokens:\n agentMetrics.accumulated_token_usage.completion_tokens,\n cache_read_tokens:\n agentMetrics.accumulated_token_usage.cache_read_tokens,\n cache_write_tokens:\n agentMetrics.accumulated_token_usage.cache_write_tokens,\n context_window:\n agentMetrics.accumulated_token_usage.context_window,\n per_turn_token:\n agentMetrics.accumulated_token_usage.per_turn_token,\n }\n : null,\n };\n useMetricsStore.getState().setMetrics(metrics);\n }\n },\n [],\n );\n\n // Initial REST history load: fetch the most recent events and seed the\n // store. Older events are paginated in via `useLoadOlderEvents` when the\n // user scrolls to the top of the chat. The WebSocket connection waits for\n // this query so it can subscribe with `resend_mode='since'` and avoid\n // re-streaming everything REST already returned.\n const {\n data: preloadedHistory,\n isPending: isPreloadingHistory,\n isFetching: isFetchingHistory,\n isError: isPreloadHistoryError,\n } = useConversationHistory(conversationId);\n\n // Skeleton only on the genuine first load (no cached data yet). On return the\n // cached page is present, so `isPending` is false and we render the\n // last-known discussion immediately while the tail refetch runs in the\n // background — see the socket gate below, which waits on `isFetching`.\n const isLoadingHistoryMain = !!conversationId && isPreloadingHistory;\n\n // Clear the (global, not conversation-scoped) event store when the active\n // conversation changes, BEFORE the preloaded-history effect below re-seeds\n // it. This MUST live here rather than in the route component: a parent's\n // passive effect runs *after* this child's layout effects, so clearing from\n // the route would wipe the freshly seeded history. On a conversation switch\n // the history page is already cached, so `preloadedHistory` is available\n // synchronously — without ordering the clear first, the user's already-echoed\n // message gets seeded then immediately wiped, leaving only the `since`\n // WebSocket resend (the agent's reply). Re-entering the same conversation is\n // a no-op, so the store survives navigating away to Settings and back.\n useLayoutEffect(() => {\n const nextId = conversationId ?? null;\n if (useEventStore.getState().loadedConversationId === nextId) {\n return;\n }\n // Single atomic action: clears the previous conversation's events and\n // records the new loaded id in one `set`, so no subscriber can observe a\n // half-applied state (events gone but the old id still reported).\n clearEventsForConversation(nextId);\n resetBrowserStore();\n }, [conversationId, clearEventsForConversation, resetBrowserStore]);\n\n useLayoutEffect(() => {\n if (!preloadedHistory || preloadedHistory.events.length === 0) {\n return;\n }\n addEvents(preloadedHistory.events);\n\n // The first user message of a cloud start-task conversation is persisted\n // server-side and reaches us via this REST preload, not over the WebSocket\n // (which subscribes with resend_mode='since' after the latest preloaded\n // timestamp). Consume any matching optimistic \"Sending…\" bubble here too —\n // mirroring the WS handler — so it doesn't linger as a duplicate of the echo.\n if (conversationId) {\n // Rebuild inline \"Switched to\" messages from the REST-preloaded history.\n // The live store writers (WS handler / user action) never see preloaded\n // events, so without this past model switches wouldn't render on reload.\n // Read the post-`addEvents` `uiEvents` (actions replaced by observations,\n // Think/Finish observations dropped) — not the raw history — so anchors\n // match the ids the renderer actually mounts.\n seedModelSwitchesFromHistory(\n conversationId,\n useEventStore.getState().uiEvents,\n );\n\n for (const event of preloadedHistory.events) {\n if (isUserMessageEvent(event)) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n }\n }\n }\n }, [\n preloadedHistory,\n addEvents,\n conversationId,\n consumeMatchingPendingMessage,\n ]);\n\n /**\n * Timestamp of the latest event we already have from REST. Used as\n * `after_timestamp` when opening the WebSocket so the server only resends\n * events strictly after this point. `null` until the REST query settles\n * (we hold the WS connection open until then to avoid an `all` resend).\n */\n const initialAfterTimestamp = useMemo<string | null>(() => {\n // Wait for the history query to settle — including the refetch fired when\n // returning to a conversation — so we anchor `since` to the freshest event\n // we have rather than a stale cached tail.\n if (isFetchingHistory) return null;\n const events = preloadedHistory?.events ?? [];\n const latest = events[events.length - 1];\n if (!latest || !(\"timestamp\" in latest) || !latest.timestamp) return null;\n return latest.timestamp;\n }, [preloadedHistory, isFetchingHistory]);\n\n // Build WebSocket URL from props.\n //\n // We deliberately wait for the history fetch to settle before opening the\n // socket so the WS subscription can use `resend_mode='since'` with a\n // meaningful `after_timestamp`. This gate is on `isFetching`, not just the\n // first-load `isPending`, so the refetch fired when returning to a\n // conversation also completes first: the socket bakes `after_timestamp` into\n // its URL at connect time and will NOT re-subscribe when the value changes\n // later (see use-websocket — options live in a ref, reconnect keys on the URL\n // only). Connecting mid-fetch would therefore pin `since` to the stale cached\n // tail and replay the entire backlog over the socket. Without any gate the WS\n // would instead fall back to `resend_mode='all'`. If the REST query errored we\n // fall through and connect with `resend_mode='all'` so the user still sees\n // live events.\n const wsUrl = useMemo(() => {\n if (!conversationId || !conversationUrl) {\n return null;\n }\n if (isFetchingHistory && !isPreloadHistoryError) {\n return null;\n }\n return buildWebSocketUrl(conversationId, conversationUrl);\n }, [\n conversationId,\n conversationUrl,\n isFetchingHistory,\n isPreloadHistoryError,\n ]);\n\n const planningAgentWsUrl = useMemo(() => {\n if (!subConversations?.length) {\n return null;\n }\n\n // Currently, there is only one sub-conversation and it uses the planning agent.\n const planningAgentConversation = subConversations[0];\n\n if (\n !planningAgentConversation?.id ||\n !planningAgentConversation.conversation_url\n ) {\n return null;\n }\n\n return buildWebSocketUrl(\n planningAgentConversation.id,\n planningAgentConversation.conversation_url,\n );\n }, [subConversations]);\n\n // Merged connection state - reflects combined status of both connections\n const connectionState = useMemo<WebSocketConnectionState>(() => {\n // If planning agent connection doesn't exist, use main connection state\n if (!planningAgentWsUrl) {\n return mainConnectionState;\n }\n\n // If either is connecting, merged state is connecting\n if (\n mainConnectionState === \"CONNECTING\" ||\n planningConnectionState === \"CONNECTING\"\n ) {\n return \"CONNECTING\";\n }\n\n // If both are open, merged state is open\n if (mainConnectionState === \"OPEN\" && planningConnectionState === \"OPEN\") {\n return \"OPEN\";\n }\n\n // If both are closed, merged state is closed\n if (\n mainConnectionState === \"CLOSED\" &&\n planningConnectionState === \"CLOSED\"\n ) {\n return \"CLOSED\";\n }\n\n // If either is closing, merged state is closing\n if (\n mainConnectionState === \"CLOSING\" ||\n planningConnectionState === \"CLOSING\"\n ) {\n return \"CLOSING\";\n }\n\n // Default to closed if states don't match expected patterns\n return \"CLOSED\";\n }, [mainConnectionState, planningConnectionState, planningAgentWsUrl]);\n\n useEffect(() => {\n if (\n expectedEventCountPlanning !== null &&\n receivedEventCountRefPlanning.current >= expectedEventCountPlanning &&\n isLoadingHistoryPlanning\n ) {\n setIsLoadingHistoryPlanning(false);\n }\n }, [\n expectedEventCountPlanning,\n isLoadingHistoryPlanning,\n receivedEventCountRefPlanning,\n ]);\n\n // Call API once after history loading completes if we tracked any PlanningFileEditorObservation events\n useEffect(() => {\n if (!isLoadingHistoryPlanning && latestPlanningFileEventRef.current) {\n const { path, conversationId: currentPlanningConversationId } =\n latestPlanningFileEventRef.current;\n\n readConversationFile(\n {\n conversationId: currentPlanningConversationId,\n filePath: path,\n },\n {\n onSuccess: (fileContent) => {\n setPlanContent(fileContent);\n },\n onError: (error) => {\n console.warn(\"Failed to read conversation file:\", error);\n },\n },\n );\n\n // Clear the ref after calling the API\n latestPlanningFileEventRef.current = null;\n }\n }, [isLoadingHistoryPlanning, readConversationFile, setPlanContent]);\n\n useEffect(() => {\n hasConnectedRefMain.current = false;\n setIsLoadingHistoryPlanning(!!subConversationIds?.length);\n setExpectedEventCountPlanning(null);\n receivedEventCountRefPlanning.current = 0;\n // Reset the tracked event ref when sub-conversations change\n latestPlanningFileEventRef.current = null;\n }, [subConversationIds]);\n\n // Reset hasConnected flags when the conversation changes.\n useEffect(() => {\n hasConnectedRefMain.current = false;\n hasConnectedRefPlanning.current = false;\n // Reset the tracked event ref when conversation changes\n latestPlanningFileEventRef.current = null;\n }, [conversationId]);\n\n // Merged loading history state - true if either connection is still loading\n const isLoadingHistory = useMemo(\n () => isLoadingHistoryMain || isLoadingHistoryPlanning,\n [isLoadingHistoryMain, isLoadingHistoryPlanning],\n );\n\n // Separate message handlers for each connection\n const handleMainMessage = useCallback(\n (messageEvent: MessageEvent) => {\n try {\n const event = JSON.parse(messageEvent.data);\n\n // History loading for the main conversation is REST-driven now;\n // every WS message is a new event we add to the store.\n\n // Use type guard to validate v1 event structure\n if (isAgentServerEvent(event)) {\n const isDuplicateEvent = useEventStore\n .getState()\n .eventIds.has(event.id);\n const switchLLMObservation =\n !isDuplicateEvent && isSwitchLLMObservationEvent(event)\n ? event\n : null;\n addEvent(event);\n\n // Handle displayable error events - show error banner\n // AgentErrorEvent errors are displayed inline in the chat, not as banners\n if (isDisplayableErrorEvent(event)) {\n const errorEvent = event as\n | ConversationErrorEvent\n | ServerErrorEvent;\n trackError({\n message: errorEvent.detail,\n source: \"conversation\",\n metadata: {\n eventId: errorEvent.id,\n errorCode: errorEvent.code,\n },\n posthog,\n });\n setErrorMessage(errorEvent.detail, \"conversation\", errorEvent.code);\n } else {\n handleNonErrorEvent();\n }\n\n // LLM errors render inline in the chat (see ErrorEventMessage); track\n // them for analytics but keep them out of the banner above the chat box.\n if (isAgentErrorEvent(event)) {\n trackError({\n message: event.error,\n source: \"agent\",\n metadata: {\n eventId: event.id,\n toolName: event.tool_name,\n toolCallId: event.tool_call_id,\n },\n posthog,\n });\n }\n\n // Clear optimistic user message when a user message is confirmed.\n // We match by the echoed text content (with FIFO fallback inside the\n // store), so an echo for \"second\" pops \"second\" — not whichever\n // pending entry happens to be oldest — protecting against any\n // out-of-order delivery between conversations or sub-agents.\n if (isUserMessageEvent(event)) {\n if (conversationId) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n // Clear draft from localStorage - message was successfully delivered\n setConversationState(conversationId, { draftMessage: null });\n }\n }\n\n // Handle cache invalidation for ActionEvent\n if (isActionEvent(event)) {\n const currentConversationId =\n conversationId || \"test-conversation-id\"; // TODO: Get from context\n handleActionEventCacheInvalidation(\n event,\n currentConversationId,\n queryClient,\n );\n }\n\n // Handle conversation state updates\n // TODO: Tests\n if (isConversationStateUpdateEvent(event)) {\n if (isFullStateConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value.execution_status);\n }\n if (isAgentStatusConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value);\n }\n if (isStatsConversationStateUpdateEvent(event)) {\n updateMetricsFromStats(event);\n }\n // Mirror goal status into the store. Intentionally duplicated across\n // the main and planning WebSocket handlers (like the execution_status\n // and stats branches above), not a merge artifact.\n if (isGoalConversationStateUpdateEvent(event) && conversationId) {\n useGoalStore.getState().setStatus(conversationId, event.value);\n }\n }\n\n // Handle ExecuteBashAction events - add command as input to terminal\n if (isExecuteBashActionEvent(event)) {\n appendInput(event.action.command);\n }\n\n // Handle ExecuteBashObservation events - add output to terminal\n if (isExecuteBashObservationEvent(event)) {\n // Extract text content from the observation content array\n const textContent = event.observation.content\n .filter((c) => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\\n\");\n appendOutput(textContent);\n }\n\n // Handle BrowserObservation events - update browser store with screenshot\n if (isBrowserObservationEvent(event)) {\n const { screenshot_data: screenshotData } = event.observation;\n if (screenshotData) {\n const screenshotSrc = screenshotData.startsWith(\"data:\")\n ? screenshotData\n : `data:image/png;base64,${screenshotData}`;\n useBrowserStore.getState().setScreenshotSrc(screenshotSrc);\n }\n }\n\n // Handle BrowserNavigateAction events - update browser store with URL\n if (isBrowserNavigateActionEvent(event)) {\n useBrowserStore.getState().setUrl(event.action.url);\n }\n\n if (\n conversationId &&\n switchLLMObservation &&\n !switchLLMObservation.observation.is_error\n ) {\n recordModelSwitchMessage(\n conversationId,\n switchLLMObservation.observation.profile_name,\n );\n\n // Mirror the user-driven `/model` path: persist the profile so the\n // chat-header switcher shows the right name after a reload, even\n // when several profiles share a model (#1082).\n const prevMetadata = getStoredConversationMetadata(conversationId);\n setStoredConversationMetadata(conversationId, {\n selected_repository: prevMetadata?.selected_repository ?? null,\n selected_branch: prevMetadata?.selected_branch ?? null,\n git_provider: prevMetadata?.git_provider ?? null,\n selected_workspace: prevMetadata?.selected_workspace ?? null,\n active_profile: switchLLMObservation.observation.profile_name,\n // Full-object replace: carry the plugins snapshot forward so the\n // in-conversation plugins view survives a profile switch.\n plugins: prevMetadata?.plugins ?? null,\n });\n\n if (switchLLMObservation.observation.active_model) {\n updateConversationLlmModelInCache(\n queryClient,\n conversationId,\n switchLLMObservation.observation.active_model,\n );\n }\n\n invalidateConversationQueries(queryClient, conversationId);\n }\n\n // Handle canvas_ui custom-tool ActionEvents - drive the frontend\n // (navigate to a file, switch tabs, show a preview). The tool\n // executes server-side as a no-op; the actual UI change happens\n // here on the client.\n if (isCanvasUIActionEvent(event)) {\n handleCanvasUIAction(event.action, conversationId ?? null);\n }\n }\n } catch (error) {\n console.warn(\"Failed to parse WebSocket message as JSON:\", error);\n }\n },\n [\n addEvent,\n setErrorMessage,\n consumeMatchingPendingMessage,\n queryClient,\n conversationId,\n setExecutionStatus,\n appendInput,\n appendOutput,\n updateMetricsFromStats,\n handleNonErrorEvent,\n posthog,\n ],\n );\n\n const handlePlanningMessage = useCallback(\n (messageEvent: MessageEvent) => {\n try {\n const event = JSON.parse(messageEvent.data);\n\n // Track received events for history loading (count ALL events from WebSocket)\n // Always count when loading, even if we don't have the expected count yet\n if (isLoadingHistoryPlanning) {\n receivedEventCountRefPlanning.current += 1;\n\n if (\n expectedEventCountPlanning !== null &&\n receivedEventCountRefPlanning.current >= expectedEventCountPlanning\n ) {\n setIsLoadingHistoryPlanning(false);\n }\n }\n\n // Use type guard to validate v1 event structure\n if (isAgentServerEvent(event)) {\n // Mark this event as coming from the planning agent\n const eventWithPlanningFlag = {\n ...event,\n isFromPlanningAgent: true,\n };\n addEvent(eventWithPlanningFlag);\n\n // Handle displayable error events - show error banner\n // AgentErrorEvent errors are displayed inline in the chat, not as banners\n if (isDisplayableErrorEvent(event)) {\n const errorEvent = event as\n | ConversationErrorEvent\n | ServerErrorEvent;\n trackError({\n message: errorEvent.detail,\n source: \"planning_conversation\",\n metadata: {\n eventId: errorEvent.id,\n errorCode: errorEvent.code,\n },\n posthog,\n });\n setErrorMessage(errorEvent.detail, \"conversation\", errorEvent.code);\n } else {\n handleNonErrorEvent();\n }\n\n // LLM errors render inline in the chat (see ErrorEventMessage); track\n // them for analytics but keep them out of the banner above the chat box.\n if (isAgentErrorEvent(event)) {\n trackError({\n message: event.error,\n source: \"planning_agent\",\n metadata: {\n eventId: event.id,\n toolName: event.tool_name,\n toolCallId: event.tool_call_id,\n },\n posthog,\n });\n }\n\n // Clear optimistic user message when a user message is confirmed.\n // Always scope to the main `conversationId` (where the user types)\n // and match on the echoed content so the planning sub-agent's own\n // events can never consume a main-conversation pending entry.\n if (isUserMessageEvent(event)) {\n if (conversationId) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n setConversationState(conversationId, { draftMessage: null });\n }\n }\n\n // Handle cache invalidation for ActionEvent\n if (isActionEvent(event)) {\n const planningAgentConversation = subConversations?.[0];\n const currentConversationId =\n planningAgentConversation?.id || \"test-conversation-id\"; // TODO: Get from context\n handleActionEventCacheInvalidation(\n event,\n currentConversationId,\n queryClient,\n );\n }\n\n // Handle conversation state updates\n // TODO: Tests\n if (isConversationStateUpdateEvent(event)) {\n if (isFullStateConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value.execution_status);\n }\n if (isAgentStatusConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value);\n }\n if (isStatsConversationStateUpdateEvent(event)) {\n updateMetricsFromStats(event);\n }\n // Mirror goal status into the store. Intentionally duplicated across\n // the main and planning WebSocket handlers (like the execution_status\n // and stats branches above), not a merge artifact.\n if (isGoalConversationStateUpdateEvent(event) && conversationId) {\n useGoalStore.getState().setStatus(conversationId, event.value);\n }\n }\n\n // Handle ExecuteBashAction events - add command as input to terminal\n if (isExecuteBashActionEvent(event)) {\n appendInput(event.action.command);\n }\n\n // Handle ExecuteBashObservation events - add output to terminal\n if (isExecuteBashObservationEvent(event)) {\n // Extract text content from the observation content array\n const textContent = event.observation.content\n .filter((c) => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\\n\");\n appendOutput(textContent);\n }\n\n // Handle PlanningFileEditorObservation - only update plan for Plan.md\n if (isPlanningFileEditorObservationEvent(event)) {\n const { path } = event.observation;\n if (isPlanFilePath(path)) {\n const planningAgentConversation = subConversations?.[0];\n const planningConversationId = planningAgentConversation?.id;\n\n if (planningConversationId && path) {\n if (isLoadingHistoryPlanning) {\n latestPlanningFileEventRef.current = {\n path,\n conversationId: planningConversationId,\n };\n } else {\n readConversationFile(\n {\n conversationId: planningConversationId,\n filePath: path,\n },\n {\n onSuccess: (fileContent) => {\n setPlanContent(fileContent);\n },\n onError: (error) => {\n console.warn(\n \"Failed to read conversation file:\",\n error,\n );\n },\n },\n );\n }\n }\n }\n }\n }\n } catch (error) {\n console.warn(\"Failed to parse WebSocket message as JSON:\", error);\n }\n },\n [\n addEvent,\n isLoadingHistoryPlanning,\n expectedEventCountPlanning,\n setErrorMessage,\n consumeMatchingPendingMessage,\n queryClient,\n subConversations,\n conversationId,\n setExecutionStatus,\n appendInput,\n appendOutput,\n readConversationFile,\n setPlanContent,\n updateMetricsFromStats,\n handleNonErrorEvent,\n posthog,\n ],\n );\n\n // Separate WebSocket options for main connection\n const mainWebsocketOptions: WebSocketHookOptions = useMemo(() => {\n // History was already loaded over REST (`useConversationHistory`).\n // Subscribe with `resend_mode='since'` so the server only resends events\n // strictly after the latest one we already have. If REST returned no\n // events at all (brand-new conversation), fall back to `'all'` so any\n // events that may have been written between the REST call and the WS\n // handshake still show up. Dedup in the event store handles overlap.\n const queryParams: Record<string, string | boolean> = initialAfterTimestamp\n ? { resend_mode: \"since\", after_timestamp: initialAfterTimestamp }\n : { resend_mode: \"all\" };\n\n // Add session_api_key if available\n if (sessionApiKey) {\n queryParams.session_api_key = sessionApiKey;\n }\n\n return {\n queryParams,\n reconnect: { enabled: true },\n onOpen: () => {\n setMainConnectionState(\"OPEN\");\n hasConnectedRefMain.current = true; // Mark that we've successfully connected\n clearConnectionError(); // Clear a previous connection error; keep sticky conversation errors\n },\n onClose: () => {\n setMainConnectionState(\"CLOSED\");\n },\n onError: () => {\n setMainConnectionState(\"CLOSED\");\n // Only show error message if we've previously connected successfully\n if (hasConnectedRefMain.current) {\n setErrorMessage(SERVER_CONNECTION_ERROR_MESSAGE, \"connection\");\n }\n },\n onMessage: handleMainMessage,\n };\n }, [\n handleMainMessage,\n setErrorMessage,\n clearConnectionError,\n sessionApiKey,\n initialAfterTimestamp,\n ]);\n\n // Separate WebSocket options for planning agent connection\n const planningWebsocketOptions: WebSocketHookOptions = useMemo(() => {\n const queryParams: Record<string, string | boolean> = {\n resend_all: true,\n };\n\n // Add session_api_key if available\n if (sessionApiKey) {\n queryParams.session_api_key = sessionApiKey;\n }\n\n const planningAgentConversation = subConversations?.[0];\n\n return {\n queryParams,\n reconnect: { enabled: true },\n onOpen: async () => {\n setPlanningConnectionState(\"OPEN\");\n hasConnectedRefPlanning.current = true; // Mark that we've successfully connected\n clearConnectionError(); // Clear a previous connection error; keep sticky conversation errors\n\n // Fetch expected event count for history loading detection\n if (\n planningAgentConversation?.id &&\n planningAgentConversation.conversation_url\n ) {\n try {\n const count = await EventService.getEventCount(\n planningAgentConversation.id,\n planningAgentConversation.conversation_url,\n planningAgentConversation.session_api_key,\n );\n setExpectedEventCountPlanning(count);\n\n // If no events expected, mark as loaded immediately\n if (count === 0) {\n setIsLoadingHistoryPlanning(false);\n }\n } catch (error) {\n // Fall back to marking as loaded to avoid infinite loading state\n setIsLoadingHistoryPlanning(false);\n }\n }\n },\n onClose: () => {\n setPlanningConnectionState(\"CLOSED\");\n },\n onError: () => {\n setPlanningConnectionState(\"CLOSED\");\n // Only show error message if we've previously connected successfully\n if (hasConnectedRefPlanning.current) {\n setErrorMessage(SERVER_CONNECTION_ERROR_MESSAGE, \"connection\");\n }\n },\n onMessage: handlePlanningMessage,\n };\n }, [\n handlePlanningMessage,\n setErrorMessage,\n clearConnectionError,\n sessionApiKey,\n subConversations,\n ]);\n\n // Only attempt WebSocket connection when we have a valid URL\n // This prevents connection attempts during task polling phase\n const websocketUrl = wsUrl;\n const { socket: mainSocket, reconnect: reconnectMain } = useWebSocket(\n websocketUrl || \"\",\n mainWebsocketOptions,\n );\n\n const { socket: planningAgentSocket, reconnect: reconnectPlanning } =\n useWebSocket(planningAgentWsUrl || \"\", planningWebsocketOptions);\n\n const reconnect = useCallback(() => {\n removeErrorMessage();\n const currentMode = useConversationStore.getState().conversationMode;\n if (currentMode === \"plan\" && planningAgentWsUrl) {\n reconnectPlanning();\n return;\n }\n reconnectMain();\n }, [\n planningAgentWsUrl,\n reconnectMain,\n reconnectPlanning,\n removeErrorMessage,\n ]);\n\n // V1 send message function via WebSocket\n // Falls back to REST API queue when WebSocket is not connected\n const sendMessage = useCallback(\n async (message: SendMessageRequest): Promise<SendMessageResult> => {\n const currentMode = useConversationStore.getState().conversationMode;\n const currentSocket =\n currentMode === \"plan\" ? planningAgentSocket : mainSocket;\n\n if (currentSocket?.readyState !== WebSocket.OPEN) {\n // WebSocket not connected - queue message via REST API\n // Message will be delivered automatically when conversation becomes ready\n if (!conversationId) {\n const error = new Error(\"No conversation ID available\");\n setErrorMessage(error.message);\n throw error;\n }\n\n try {\n await new ConversationClient(getAgentServerClientOptions()).sendEvent(\n conversationId,\n {\n role: \"user\",\n content: message.content,\n },\n { run: true },\n );\n // Message queued successfully - it will be delivered when ready\n // Return queued: true so caller knows not to show optimistic UI\n return { queued: true };\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : \"Failed to queue message for delivery\";\n setErrorMessage(errorMessage);\n throw error;\n }\n }\n\n try {\n // Send message through WebSocket as JSON with run: true so the\n // agent loop starts automatically in async mode.\n currentSocket.send(JSON.stringify({ ...message, run: true }));\n return { queued: false };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to send message\";\n setErrorMessage(errorMessage);\n throw error;\n }\n },\n [mainSocket, planningAgentSocket, setErrorMessage, conversationId],\n );\n\n // Track main socket state changes\n useEffect(() => {\n // Only process socket updates if we have a valid URL and socket\n if (mainSocket && wsUrl) {\n // Update state based on socket readyState\n const updateState = () => {\n switch (mainSocket.readyState) {\n case WebSocket.CONNECTING:\n setMainConnectionState(\"CONNECTING\");\n break;\n case WebSocket.OPEN:\n setMainConnectionState(\"OPEN\");\n break;\n case WebSocket.CLOSING:\n setMainConnectionState(\"CLOSING\");\n break;\n case WebSocket.CLOSED:\n setMainConnectionState(\"CLOSED\");\n break;\n default:\n setMainConnectionState(\"CLOSED\");\n break;\n }\n };\n\n updateState();\n }\n }, [mainSocket, wsUrl]);\n\n // Track planning agent socket state changes\n useEffect(() => {\n // Only process socket updates if we have a valid URL and socket\n if (planningAgentSocket && planningAgentWsUrl) {\n // Update state based on socket readyState\n const updateState = () => {\n switch (planningAgentSocket.readyState) {\n case WebSocket.CONNECTING:\n setPlanningConnectionState(\"CONNECTING\");\n break;\n case WebSocket.OPEN:\n setPlanningConnectionState(\"OPEN\");\n break;\n case WebSocket.CLOSING:\n setPlanningConnectionState(\"CLOSING\");\n break;\n case WebSocket.CLOSED:\n setPlanningConnectionState(\"CLOSED\");\n break;\n default:\n setPlanningConnectionState(\"CLOSED\");\n break;\n }\n };\n\n updateState();\n }\n }, [planningAgentSocket, planningAgentWsUrl]);\n\n const contextValue = useMemo(\n () => ({ connectionState, sendMessage, isLoadingHistory, reconnect }),\n [connectionState, sendMessage, isLoadingHistory, reconnect],\n );\n\n return (\n <ConversationWebSocketContext.Provider value={contextValue}>\n {children}\n </ConversationWebSocketContext.Provider>\n );\n}\n\nexport const useConversationWebSocket =\n (): ConversationWebSocketContextType | null => {\n const context = useContext(ConversationWebSocketContext);\n // Return null instead of throwing when not in provider\n // This allows the hook to be called conditionally based on conversation version\n return context || null;\n };\n"],"mappings":"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"}
1
+ {"version":3,"file":"conversation-websocket-context.js","names":[],"sources":["../../src/contexts/conversation-websocket-context.tsx"],"sourcesContent":["import React, {\n createContext,\n useContext,\n useEffect,\n useLayoutEffect,\n useState,\n useCallback,\n useMemo,\n useRef,\n} from \"react\";\nimport { ConversationClient } from \"@openhands/typescript-client/clients\";\n\nimport { useQueryClient } from \"@tanstack/react-query\";\nimport { usePostHog } from \"posthog-js/react\";\nimport { useWebSocket, WebSocketHookOptions } from \"#/hooks/use-websocket\";\nimport { SERVER_CONNECTION_ERROR_MESSAGE } from \"#/constants/server-connection-error\";\nimport { useEventStore } from \"#/stores/use-event-store\";\nimport { useErrorMessageStore } from \"#/stores/error-message-store\";\nimport { useOptimisticUserMessageStore } from \"#/stores/optimistic-user-message-store\";\nimport { useConversationStateStore } from \"#/stores/conversation-state-store\";\nimport { useCommandStore } from \"#/stores/command-store\";\nimport { useBrowserStore } from \"#/stores/browser-store\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport {\n isAgentServerEvent,\n isAgentErrorEvent,\n isUserMessageEvent,\n isActionEvent,\n isConversationStateUpdateEvent,\n isFullStateConversationStateUpdateEvent,\n isAgentStatusConversationStateUpdateEvent,\n isStatsConversationStateUpdateEvent,\n isGoalConversationStateUpdateEvent,\n isExecuteBashActionEvent,\n isExecuteBashObservationEvent,\n isDisplayableErrorEvent,\n isPlanningFileEditorObservationEvent,\n isBrowserObservationEvent,\n isBrowserNavigateActionEvent,\n isSwitchLLMObservationEvent,\n isCanvasUIActionEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { handleCanvasUIAction } from \"#/services/canvas-ui\";\nimport { ConversationStateUpdateEventStats } from \"#/types/agent-server/core/events/conversation-state-event\";\nimport type {\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"#/types/agent-server/core/events/conversation-state-event\";\nimport { handleActionEventCacheInvalidation } from \"#/utils/cache-utils\";\nimport { buildWebSocketUrl } from \"#/utils/websocket-url\";\nimport type {\n AppConversation,\n SendMessageRequest,\n} from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport EventService from \"#/api/event-service/event-service.api\";\nimport { getAgentServerClientOptions } from \"#/api/agent-server-client-options\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { trackError } from \"#/utils/error-handler\";\nimport { useReadConversationFile } from \"#/hooks/mutation/use-read-conversation-file\";\nimport useMetricsStore from \"#/stores/metrics-store\";\nimport { useConversationHistory } from \"#/hooks/query/use-conversation-history\";\nimport { setConversationState } from \"#/utils/conversation-local-storage\";\nimport {\n recordModelSwitchMessage,\n seedModelSwitchesFromHistory,\n} from \"#/hooks/chat/record-model-switch-message\";\nimport {\n invalidateConversationQueries,\n updateConversationLlmModelInCache,\n} from \"#/hooks/mutation/conversation-mutation-utils\";\nimport {\n getStoredConversationMetadata,\n setStoredConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\n\nexport type WebSocketConnectionState =\n | \"CONNECTING\"\n | \"OPEN\"\n | \"CLOSED\"\n | \"CLOSING\";\n\ninterface SendMessageResult {\n queued: boolean; // true if message was queued for later delivery, false if sent immediately\n}\n\ninterface ConversationWebSocketContextType {\n connectionState: WebSocketConnectionState;\n sendMessage: (message: SendMessageRequest) => Promise<SendMessageResult>;\n isLoadingHistory: boolean;\n reconnect: () => void;\n}\n\nconst ConversationWebSocketContext = createContext<\n ConversationWebSocketContextType | undefined\n>(undefined);\n\n/**\n * Extract the text body of an echoed user `MessageEvent` for matching against\n * the optimistic pending-message queue. The server wraps the original\n * `args.content` string in one or more `TextContent` entries (alongside any\n * `ImageContent` entries for inline images), so concatenating the `text`\n * fields gives us back the exact prompt we sent.\n */\nfunction extractMessageEventText(\n event: import(\"#/types/agent-server/core/events/message-event\").MessageEvent,\n): string {\n return event.llm_message.content\n .filter(\n (part): part is { type: \"text\"; text: string } => part.type === \"text\",\n )\n .map((part) => part.text)\n .join(\"\");\n}\n\nexport function ConversationWebSocketProvider({\n children,\n conversationId,\n conversationUrl,\n sessionApiKey,\n subConversations,\n subConversationIds,\n}: {\n children: React.ReactNode;\n conversationId?: string;\n conversationUrl?: string | null;\n sessionApiKey?: string | null;\n subConversations?: AppConversation[];\n subConversationIds?: string[];\n}) {\n // Separate connection state tracking for each WebSocket\n const [mainConnectionState, setMainConnectionState] =\n useState<WebSocketConnectionState>(\"CONNECTING\");\n const [planningConnectionState, setPlanningConnectionState] =\n useState<WebSocketConnectionState>(\"CONNECTING\");\n\n // Track if we've ever successfully connected for each connection\n // Don't show errors until after first successful connection\n const hasConnectedRefMain = React.useRef(false);\n const hasConnectedRefPlanning = React.useRef(false);\n\n const posthog = usePostHog();\n const queryClient = useQueryClient();\n const addEvent = useEventStore((state) => state.addEvent);\n const addEvents = useEventStore((state) => state.addEvents);\n const clearEventsForConversation = useEventStore(\n (state) => state.clearEventsForConversation,\n );\n const { setErrorMessage, removeErrorMessage, clearConnectionError } =\n useErrorMessageStore();\n const consumeMatchingPendingMessage = useOptimisticUserMessageStore(\n (state) => state.consumeMatchingPendingMessage,\n );\n const { setExecutionStatus } = useConversationStateStore();\n const { appendInput, appendOutput } = useCommandStore();\n const resetBrowserStore = useBrowserStore((state) => state.reset);\n\n // History loading state.\n // - Main conversation history is now loaded via REST (`useConversationHistory`),\n // so its loading state mirrors the REST query state (see below).\n // - Planning sub-conversation history still streams over the WebSocket using\n // `resend_mode='all'`, so we keep the count-based detection for it.\n const [isLoadingHistoryPlanning, setIsLoadingHistoryPlanning] =\n useState(true);\n const [expectedEventCountPlanning, setExpectedEventCountPlanning] = useState<\n number | null\n >(null);\n\n const { setPlanContent } = useConversationStore();\n\n // Hook for reading conversation file\n const { mutate: readConversationFile } = useReadConversationFile();\n\n // Track planning-agent received events (still WS-driven).\n const receivedEventCountRefPlanning = useRef(0);\n\n // Track the latest PlanningFileEditorObservation for Plan.md during history replay\n const latestPlanningFileEventRef = useRef<{\n path: string;\n conversationId: string;\n } | null>(null);\n\n const isPlanFilePath = (path: string | null): boolean =>\n path?.toUpperCase().endsWith(\"PLAN.MD\") ?? false;\n\n const handleNonErrorEvent = useCallback(() => {\n // A normal event means connectivity recovered: clear a transient connection\n // error, but keep sticky conversation errors (e.g. a wrong API key).\n clearConnectionError();\n }, [clearConnectionError]);\n\n // Helper function to update metrics from stats event\n const updateMetricsFromStats = useCallback(\n (event: ConversationStateUpdateEventStats) => {\n if (event.value.usage_to_metrics?.agent) {\n const agentMetrics = event.value.usage_to_metrics.agent;\n const metrics = {\n cost: agentMetrics.accumulated_cost,\n max_budget_per_task: agentMetrics.max_budget_per_task ?? null,\n usage: agentMetrics.accumulated_token_usage\n ? {\n prompt_tokens:\n agentMetrics.accumulated_token_usage.prompt_tokens,\n completion_tokens:\n agentMetrics.accumulated_token_usage.completion_tokens,\n cache_read_tokens:\n agentMetrics.accumulated_token_usage.cache_read_tokens,\n cache_write_tokens:\n agentMetrics.accumulated_token_usage.cache_write_tokens,\n context_window:\n agentMetrics.accumulated_token_usage.context_window,\n per_turn_token:\n agentMetrics.accumulated_token_usage.per_turn_token,\n }\n : null,\n };\n useMetricsStore.getState().setMetrics(metrics);\n }\n },\n [],\n );\n\n // Initial REST history load: fetch the most recent events and seed the\n // store. Older events are paginated in via `useLoadOlderEvents` when the\n // user scrolls to the top of the chat. The WebSocket connection waits for\n // this query so it can subscribe with `resend_mode='since'` and avoid\n // re-streaming everything REST already returned.\n const {\n data: preloadedHistory,\n isPending: isPreloadingHistory,\n isFetching: isFetchingHistory,\n isError: isPreloadHistoryError,\n } = useConversationHistory(conversationId);\n\n // Skeleton only on the genuine first load (no cached data yet). On return the\n // cached page is present, so `isPending` is false and we render the\n // last-known discussion immediately while the tail refetch runs in the\n // background — see the socket gate below, which waits on `isFetching`.\n const isLoadingHistoryMain = !!conversationId && isPreloadingHistory;\n\n // Clear the (global, not conversation-scoped) event store when the active\n // conversation changes, BEFORE the preloaded-history effect below re-seeds\n // it. This MUST live here rather than in the route component: a parent's\n // passive effect runs *after* this child's layout effects, so clearing from\n // the route would wipe the freshly seeded history. On a conversation switch\n // the history page is already cached, so `preloadedHistory` is available\n // synchronously — without ordering the clear first, the user's already-echoed\n // message gets seeded then immediately wiped, leaving only the `since`\n // WebSocket resend (the agent's reply). Re-entering the same conversation is\n // a no-op, so the store survives navigating away to Settings and back.\n useLayoutEffect(() => {\n const nextId = conversationId ?? null;\n if (useEventStore.getState().loadedConversationId === nextId) {\n return;\n }\n // Single atomic action: clears the previous conversation's events and\n // records the new loaded id in one `set`, so no subscriber can observe a\n // half-applied state (events gone but the old id still reported).\n clearEventsForConversation(nextId);\n resetBrowserStore();\n }, [conversationId, clearEventsForConversation, resetBrowserStore]);\n\n useLayoutEffect(() => {\n if (!preloadedHistory || preloadedHistory.events.length === 0) {\n return;\n }\n addEvents(preloadedHistory.events);\n\n // The first user message of a cloud start-task conversation is persisted\n // server-side and reaches us via this REST preload, not over the WebSocket\n // (which subscribes with resend_mode='since' after the latest preloaded\n // timestamp). Consume any matching optimistic \"Sending…\" bubble here too —\n // mirroring the WS handler — so it doesn't linger as a duplicate of the echo.\n if (conversationId) {\n // Rebuild inline \"Switched to\" messages from the REST-preloaded history.\n // The live store writers (WS handler / user action) never see preloaded\n // events, so without this past model switches wouldn't render on reload.\n // Read the post-`addEvents` `uiEvents` (actions replaced by observations,\n // Think/Finish observations dropped) — not the raw history — so anchors\n // match the ids the renderer actually mounts.\n seedModelSwitchesFromHistory(\n conversationId,\n useEventStore.getState().uiEvents,\n );\n\n for (const event of preloadedHistory.events) {\n if (isUserMessageEvent(event)) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n }\n }\n }\n }, [\n preloadedHistory,\n addEvents,\n conversationId,\n consumeMatchingPendingMessage,\n ]);\n\n /**\n * Timestamp of the latest event we already have from REST. Used as\n * `after_timestamp` when opening the WebSocket so the server only resends\n * events strictly after this point. `null` until the REST query settles\n * (we hold the WS connection open until then to avoid an `all` resend).\n */\n const initialAfterTimestamp = useMemo<string | null>(() => {\n // Wait for the history query to settle — including the refetch fired when\n // returning to a conversation — so we anchor `since` to the freshest event\n // we have rather than a stale cached tail.\n if (isFetchingHistory) return null;\n const events = preloadedHistory?.events ?? [];\n const latest = events[events.length - 1];\n if (!latest || !(\"timestamp\" in latest) || !latest.timestamp) return null;\n return latest.timestamp;\n }, [preloadedHistory, isFetchingHistory]);\n\n // Build WebSocket URL from props.\n //\n // We deliberately wait for the history fetch to settle before opening the\n // socket so the WS subscription can use `resend_mode='since'` with a\n // meaningful `after_timestamp`. This gate is on `isFetching`, not just the\n // first-load `isPending`, so the refetch fired when returning to a\n // conversation also completes first: the socket bakes `after_timestamp` into\n // its URL at connect time and will NOT re-subscribe when the value changes\n // later (see use-websocket — options live in a ref, reconnect keys on the URL\n // only). Connecting mid-fetch would therefore pin `since` to the stale cached\n // tail and replay the entire backlog over the socket. Without any gate the WS\n // would instead fall back to `resend_mode='all'`. If the REST query errored we\n // fall through and connect with `resend_mode='all'` so the user still sees\n // live events.\n const wsUrl = useMemo(() => {\n if (!conversationId || !conversationUrl) {\n return null;\n }\n if (isFetchingHistory && !isPreloadHistoryError) {\n return null;\n }\n return buildWebSocketUrl(conversationId, conversationUrl);\n }, [\n conversationId,\n conversationUrl,\n isFetchingHistory,\n isPreloadHistoryError,\n ]);\n\n const planningAgentWsUrl = useMemo(() => {\n if (!subConversations?.length) {\n return null;\n }\n\n // Currently, there is only one sub-conversation and it uses the planning agent.\n const planningAgentConversation = subConversations[0];\n\n if (\n !planningAgentConversation?.id ||\n !planningAgentConversation.conversation_url\n ) {\n return null;\n }\n\n return buildWebSocketUrl(\n planningAgentConversation.id,\n planningAgentConversation.conversation_url,\n );\n }, [subConversations]);\n\n // Merged connection state - reflects combined status of both connections\n const connectionState = useMemo<WebSocketConnectionState>(() => {\n // If planning agent connection doesn't exist, use main connection state\n if (!planningAgentWsUrl) {\n return mainConnectionState;\n }\n\n // If either is connecting, merged state is connecting\n if (\n mainConnectionState === \"CONNECTING\" ||\n planningConnectionState === \"CONNECTING\"\n ) {\n return \"CONNECTING\";\n }\n\n // If both are open, merged state is open\n if (mainConnectionState === \"OPEN\" && planningConnectionState === \"OPEN\") {\n return \"OPEN\";\n }\n\n // If both are closed, merged state is closed\n if (\n mainConnectionState === \"CLOSED\" &&\n planningConnectionState === \"CLOSED\"\n ) {\n return \"CLOSED\";\n }\n\n // If either is closing, merged state is closing\n if (\n mainConnectionState === \"CLOSING\" ||\n planningConnectionState === \"CLOSING\"\n ) {\n return \"CLOSING\";\n }\n\n // Default to closed if states don't match expected patterns\n return \"CLOSED\";\n }, [mainConnectionState, planningConnectionState, planningAgentWsUrl]);\n\n useEffect(() => {\n if (\n expectedEventCountPlanning !== null &&\n receivedEventCountRefPlanning.current >= expectedEventCountPlanning &&\n isLoadingHistoryPlanning\n ) {\n setIsLoadingHistoryPlanning(false);\n }\n }, [\n expectedEventCountPlanning,\n isLoadingHistoryPlanning,\n receivedEventCountRefPlanning,\n ]);\n\n // Call API once after history loading completes if we tracked any PlanningFileEditorObservation events\n useEffect(() => {\n if (!isLoadingHistoryPlanning && latestPlanningFileEventRef.current) {\n const { path, conversationId: currentPlanningConversationId } =\n latestPlanningFileEventRef.current;\n\n readConversationFile(\n {\n conversationId: currentPlanningConversationId,\n filePath: path,\n },\n {\n onSuccess: (fileContent) => {\n setPlanContent(fileContent);\n },\n onError: (error) => {\n console.warn(\"Failed to read conversation file:\", error);\n },\n },\n );\n\n // Clear the ref after calling the API\n latestPlanningFileEventRef.current = null;\n }\n }, [isLoadingHistoryPlanning, readConversationFile, setPlanContent]);\n\n useEffect(() => {\n hasConnectedRefMain.current = false;\n setIsLoadingHistoryPlanning(!!subConversationIds?.length);\n setExpectedEventCountPlanning(null);\n receivedEventCountRefPlanning.current = 0;\n // Reset the tracked event ref when sub-conversations change\n latestPlanningFileEventRef.current = null;\n }, [subConversationIds]);\n\n // Reset hasConnected flags when the conversation changes.\n useEffect(() => {\n hasConnectedRefMain.current = false;\n hasConnectedRefPlanning.current = false;\n // Reset the tracked event ref when conversation changes\n latestPlanningFileEventRef.current = null;\n }, [conversationId]);\n\n // Merged loading history state - true if either connection is still loading\n const isLoadingHistory = useMemo(\n () => isLoadingHistoryMain || isLoadingHistoryPlanning,\n [isLoadingHistoryMain, isLoadingHistoryPlanning],\n );\n\n // Separate message handlers for each connection\n const handleMainMessage = useCallback(\n (messageEvent: MessageEvent) => {\n try {\n const event = JSON.parse(messageEvent.data);\n\n // History loading for the main conversation is REST-driven now;\n // every WS message is a new event we add to the store.\n\n // Use type guard to validate v1 event structure\n if (isAgentServerEvent(event)) {\n const isDuplicateEvent = useEventStore\n .getState()\n .eventIds.has(event.id);\n const switchLLMObservation =\n !isDuplicateEvent && isSwitchLLMObservationEvent(event)\n ? event\n : null;\n addEvent(event);\n\n // Handle displayable error events - show error banner\n // AgentErrorEvent errors are displayed inline in the chat, not as banners\n if (isDisplayableErrorEvent(event)) {\n const errorEvent = event as\n | ConversationErrorEvent\n | ServerErrorEvent;\n trackError({\n message: errorEvent.detail,\n source: \"conversation\",\n metadata: {\n eventId: errorEvent.id,\n errorCode: errorEvent.code,\n },\n posthog,\n });\n setErrorMessage(errorEvent.detail, \"conversation\", errorEvent.code);\n } else {\n handleNonErrorEvent();\n }\n\n // LLM errors render inline in the chat (see ErrorEventMessage); track\n // them for analytics but keep them out of the banner above the chat box.\n if (isAgentErrorEvent(event)) {\n trackError({\n message: event.error,\n source: \"agent\",\n metadata: {\n eventId: event.id,\n toolName: event.tool_name,\n toolCallId: event.tool_call_id,\n },\n posthog,\n });\n }\n\n // Clear optimistic user message when a user message is confirmed.\n // We match by the echoed text content (with FIFO fallback inside the\n // store), so an echo for \"second\" pops \"second\" — not whichever\n // pending entry happens to be oldest — protecting against any\n // out-of-order delivery between conversations or sub-agents.\n if (isUserMessageEvent(event)) {\n if (conversationId) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n // Clear draft from localStorage - message was successfully delivered\n setConversationState(conversationId, { draftMessage: null });\n }\n }\n\n // Handle cache invalidation for ActionEvent\n if (isActionEvent(event)) {\n const currentConversationId =\n conversationId || \"test-conversation-id\"; // TODO: Get from context\n handleActionEventCacheInvalidation(\n event,\n currentConversationId,\n queryClient,\n );\n }\n\n // Handle conversation state updates\n // TODO: Tests\n if (isConversationStateUpdateEvent(event)) {\n if (isFullStateConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value.execution_status);\n }\n if (isAgentStatusConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value);\n }\n if (isStatsConversationStateUpdateEvent(event)) {\n updateMetricsFromStats(event);\n }\n // Mirror goal status into the store. Intentionally duplicated across\n // the main and planning WebSocket handlers (like the execution_status\n // and stats branches above), not a merge artifact.\n if (isGoalConversationStateUpdateEvent(event) && conversationId) {\n useGoalStore.getState().setStatus(conversationId, event.value);\n }\n }\n\n // Handle ExecuteBashAction events - add command as input to terminal\n if (isExecuteBashActionEvent(event)) {\n appendInput(event.action.command);\n }\n\n // Handle ExecuteBashObservation events - add output to terminal\n if (isExecuteBashObservationEvent(event)) {\n // Extract text content from the observation content array\n const textContent = event.observation.content\n .filter((c) => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\\n\");\n appendOutput(textContent);\n }\n\n // Handle BrowserObservation events - update browser store with screenshot\n if (isBrowserObservationEvent(event)) {\n const { screenshot_data: screenshotData } = event.observation;\n if (screenshotData) {\n const screenshotSrc = screenshotData.startsWith(\"data:\")\n ? screenshotData\n : `data:image/png;base64,${screenshotData}`;\n useBrowserStore.getState().setScreenshotSrc(screenshotSrc);\n }\n }\n\n // Handle BrowserNavigateAction events - update browser store with URL\n if (isBrowserNavigateActionEvent(event)) {\n useBrowserStore.getState().setUrl(event.action.url);\n }\n\n if (\n conversationId &&\n switchLLMObservation &&\n !switchLLMObservation.observation.is_error\n ) {\n recordModelSwitchMessage(\n conversationId,\n switchLLMObservation.observation.profile_name,\n );\n\n // Mirror the user-driven `/model` path: persist the profile so the\n // chat-header switcher shows the right name after a reload, even\n // when several profiles share a model (#1082).\n const prevMetadata = getStoredConversationMetadata(conversationId);\n setStoredConversationMetadata(conversationId, {\n selected_repository: prevMetadata?.selected_repository ?? null,\n selected_branch: prevMetadata?.selected_branch ?? null,\n git_provider: prevMetadata?.git_provider ?? null,\n selected_workspace: prevMetadata?.selected_workspace ?? null,\n active_profile: switchLLMObservation.observation.profile_name,\n // Full-object replace: carry the plugins snapshot forward so the\n // in-conversation plugins view survives a profile switch.\n plugins: prevMetadata?.plugins ?? null,\n });\n\n if (switchLLMObservation.observation.active_model) {\n updateConversationLlmModelInCache(\n queryClient,\n conversationId,\n switchLLMObservation.observation.active_model,\n );\n }\n\n invalidateConversationQueries(queryClient, conversationId);\n }\n\n // Handle canvas_ui ActionEvents from both the legacy Python tool and\n // the client-defined JSON tool. The server acknowledges immediately;\n // the actual UI change happens here on the client.\n if (isCanvasUIActionEvent(event)) {\n handleCanvasUIAction(event.action, conversationId ?? null);\n }\n }\n } catch (error) {\n console.warn(\"Failed to parse WebSocket message as JSON:\", error);\n }\n },\n [\n addEvent,\n setErrorMessage,\n consumeMatchingPendingMessage,\n queryClient,\n conversationId,\n setExecutionStatus,\n appendInput,\n appendOutput,\n updateMetricsFromStats,\n handleNonErrorEvent,\n posthog,\n ],\n );\n\n const handlePlanningMessage = useCallback(\n (messageEvent: MessageEvent) => {\n try {\n const event = JSON.parse(messageEvent.data);\n\n // Track received events for history loading (count ALL events from WebSocket)\n // Always count when loading, even if we don't have the expected count yet\n if (isLoadingHistoryPlanning) {\n receivedEventCountRefPlanning.current += 1;\n\n if (\n expectedEventCountPlanning !== null &&\n receivedEventCountRefPlanning.current >= expectedEventCountPlanning\n ) {\n setIsLoadingHistoryPlanning(false);\n }\n }\n\n // Use type guard to validate v1 event structure\n if (isAgentServerEvent(event)) {\n // Mark this event as coming from the planning agent\n const eventWithPlanningFlag = {\n ...event,\n isFromPlanningAgent: true,\n };\n addEvent(eventWithPlanningFlag);\n\n // Handle displayable error events - show error banner\n // AgentErrorEvent errors are displayed inline in the chat, not as banners\n if (isDisplayableErrorEvent(event)) {\n const errorEvent = event as\n | ConversationErrorEvent\n | ServerErrorEvent;\n trackError({\n message: errorEvent.detail,\n source: \"planning_conversation\",\n metadata: {\n eventId: errorEvent.id,\n errorCode: errorEvent.code,\n },\n posthog,\n });\n setErrorMessage(errorEvent.detail, \"conversation\", errorEvent.code);\n } else {\n handleNonErrorEvent();\n }\n\n // LLM errors render inline in the chat (see ErrorEventMessage); track\n // them for analytics but keep them out of the banner above the chat box.\n if (isAgentErrorEvent(event)) {\n trackError({\n message: event.error,\n source: \"planning_agent\",\n metadata: {\n eventId: event.id,\n toolName: event.tool_name,\n toolCallId: event.tool_call_id,\n },\n posthog,\n });\n }\n\n // Clear optimistic user message when a user message is confirmed.\n // Always scope to the main `conversationId` (where the user types)\n // and match on the echoed content so the planning sub-agent's own\n // events can never consume a main-conversation pending entry.\n if (isUserMessageEvent(event)) {\n if (conversationId) {\n consumeMatchingPendingMessage(\n conversationId,\n extractMessageEventText(event),\n );\n setConversationState(conversationId, { draftMessage: null });\n }\n }\n\n // Handle cache invalidation for ActionEvent\n if (isActionEvent(event)) {\n const planningAgentConversation = subConversations?.[0];\n const currentConversationId =\n planningAgentConversation?.id || \"test-conversation-id\"; // TODO: Get from context\n handleActionEventCacheInvalidation(\n event,\n currentConversationId,\n queryClient,\n );\n }\n\n // Handle conversation state updates\n // TODO: Tests\n if (isConversationStateUpdateEvent(event)) {\n if (isFullStateConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value.execution_status);\n }\n if (isAgentStatusConversationStateUpdateEvent(event)) {\n setExecutionStatus(event.value);\n }\n if (isStatsConversationStateUpdateEvent(event)) {\n updateMetricsFromStats(event);\n }\n // Mirror goal status into the store. Intentionally duplicated across\n // the main and planning WebSocket handlers (like the execution_status\n // and stats branches above), not a merge artifact.\n if (isGoalConversationStateUpdateEvent(event) && conversationId) {\n useGoalStore.getState().setStatus(conversationId, event.value);\n }\n }\n\n // Handle ExecuteBashAction events - add command as input to terminal\n if (isExecuteBashActionEvent(event)) {\n appendInput(event.action.command);\n }\n\n // Handle ExecuteBashObservation events - add output to terminal\n if (isExecuteBashObservationEvent(event)) {\n // Extract text content from the observation content array\n const textContent = event.observation.content\n .filter((c) => c.type === \"text\")\n .map((c) => c.text)\n .join(\"\\n\");\n appendOutput(textContent);\n }\n\n // Handle PlanningFileEditorObservation - only update plan for Plan.md\n if (isPlanningFileEditorObservationEvent(event)) {\n const { path } = event.observation;\n if (isPlanFilePath(path)) {\n const planningAgentConversation = subConversations?.[0];\n const planningConversationId = planningAgentConversation?.id;\n\n if (planningConversationId && path) {\n if (isLoadingHistoryPlanning) {\n latestPlanningFileEventRef.current = {\n path,\n conversationId: planningConversationId,\n };\n } else {\n readConversationFile(\n {\n conversationId: planningConversationId,\n filePath: path,\n },\n {\n onSuccess: (fileContent) => {\n setPlanContent(fileContent);\n },\n onError: (error) => {\n console.warn(\n \"Failed to read conversation file:\",\n error,\n );\n },\n },\n );\n }\n }\n }\n }\n }\n } catch (error) {\n console.warn(\"Failed to parse WebSocket message as JSON:\", error);\n }\n },\n [\n addEvent,\n isLoadingHistoryPlanning,\n expectedEventCountPlanning,\n setErrorMessage,\n consumeMatchingPendingMessage,\n queryClient,\n subConversations,\n conversationId,\n setExecutionStatus,\n appendInput,\n appendOutput,\n readConversationFile,\n setPlanContent,\n updateMetricsFromStats,\n handleNonErrorEvent,\n posthog,\n ],\n );\n\n // Separate WebSocket options for main connection\n const mainWebsocketOptions: WebSocketHookOptions = useMemo(() => {\n // History was already loaded over REST (`useConversationHistory`).\n // Subscribe with `resend_mode='since'` so the server only resends events\n // strictly after the latest one we already have. If REST returned no\n // events at all (brand-new conversation), fall back to `'all'` so any\n // events that may have been written between the REST call and the WS\n // handshake still show up. Dedup in the event store handles overlap.\n const queryParams: Record<string, string | boolean> = initialAfterTimestamp\n ? { resend_mode: \"since\", after_timestamp: initialAfterTimestamp }\n : { resend_mode: \"all\" };\n\n // Add session_api_key if available\n if (sessionApiKey) {\n queryParams.session_api_key = sessionApiKey;\n }\n\n return {\n queryParams,\n reconnect: { enabled: true },\n onOpen: () => {\n setMainConnectionState(\"OPEN\");\n hasConnectedRefMain.current = true; // Mark that we've successfully connected\n clearConnectionError(); // Clear a previous connection error; keep sticky conversation errors\n },\n onClose: () => {\n setMainConnectionState(\"CLOSED\");\n },\n onError: () => {\n setMainConnectionState(\"CLOSED\");\n // Only show error message if we've previously connected successfully\n if (hasConnectedRefMain.current) {\n setErrorMessage(SERVER_CONNECTION_ERROR_MESSAGE, \"connection\");\n }\n },\n onMessage: handleMainMessage,\n };\n }, [\n handleMainMessage,\n setErrorMessage,\n clearConnectionError,\n sessionApiKey,\n initialAfterTimestamp,\n ]);\n\n // Separate WebSocket options for planning agent connection\n const planningWebsocketOptions: WebSocketHookOptions = useMemo(() => {\n const queryParams: Record<string, string | boolean> = {\n resend_all: true,\n };\n\n // Add session_api_key if available\n if (sessionApiKey) {\n queryParams.session_api_key = sessionApiKey;\n }\n\n const planningAgentConversation = subConversations?.[0];\n\n return {\n queryParams,\n reconnect: { enabled: true },\n onOpen: async () => {\n setPlanningConnectionState(\"OPEN\");\n hasConnectedRefPlanning.current = true; // Mark that we've successfully connected\n clearConnectionError(); // Clear a previous connection error; keep sticky conversation errors\n\n // Fetch expected event count for history loading detection\n if (\n planningAgentConversation?.id &&\n planningAgentConversation.conversation_url\n ) {\n try {\n const count = await EventService.getEventCount(\n planningAgentConversation.id,\n planningAgentConversation.conversation_url,\n planningAgentConversation.session_api_key,\n );\n setExpectedEventCountPlanning(count);\n\n // If no events expected, mark as loaded immediately\n if (count === 0) {\n setIsLoadingHistoryPlanning(false);\n }\n } catch (error) {\n // Fall back to marking as loaded to avoid infinite loading state\n setIsLoadingHistoryPlanning(false);\n }\n }\n },\n onClose: () => {\n setPlanningConnectionState(\"CLOSED\");\n },\n onError: () => {\n setPlanningConnectionState(\"CLOSED\");\n // Only show error message if we've previously connected successfully\n if (hasConnectedRefPlanning.current) {\n setErrorMessage(SERVER_CONNECTION_ERROR_MESSAGE, \"connection\");\n }\n },\n onMessage: handlePlanningMessage,\n };\n }, [\n handlePlanningMessage,\n setErrorMessage,\n clearConnectionError,\n sessionApiKey,\n subConversations,\n ]);\n\n // Only attempt WebSocket connection when we have a valid URL\n // This prevents connection attempts during task polling phase\n const websocketUrl = wsUrl;\n const { socket: mainSocket, reconnect: reconnectMain } = useWebSocket(\n websocketUrl || \"\",\n mainWebsocketOptions,\n );\n\n const { socket: planningAgentSocket, reconnect: reconnectPlanning } =\n useWebSocket(planningAgentWsUrl || \"\", planningWebsocketOptions);\n\n const reconnect = useCallback(() => {\n removeErrorMessage();\n const currentMode = useConversationStore.getState().conversationMode;\n if (currentMode === \"plan\" && planningAgentWsUrl) {\n reconnectPlanning();\n return;\n }\n reconnectMain();\n }, [\n planningAgentWsUrl,\n reconnectMain,\n reconnectPlanning,\n removeErrorMessage,\n ]);\n\n // V1 send message function via WebSocket\n // Falls back to REST API queue when WebSocket is not connected\n const sendMessage = useCallback(\n async (message: SendMessageRequest): Promise<SendMessageResult> => {\n const currentMode = useConversationStore.getState().conversationMode;\n const currentSocket =\n currentMode === \"plan\" ? planningAgentSocket : mainSocket;\n\n if (currentSocket?.readyState !== WebSocket.OPEN) {\n // WebSocket not connected - queue message via REST API\n // Message will be delivered automatically when conversation becomes ready\n if (!conversationId) {\n const error = new Error(\"No conversation ID available\");\n setErrorMessage(error.message);\n throw error;\n }\n\n try {\n await new ConversationClient(getAgentServerClientOptions()).sendEvent(\n conversationId,\n {\n role: \"user\",\n content: message.content,\n },\n { run: true },\n );\n // Message queued successfully - it will be delivered when ready\n // Return queued: true so caller knows not to show optimistic UI\n return { queued: true };\n } catch (error) {\n const errorMessage =\n error instanceof Error\n ? error.message\n : \"Failed to queue message for delivery\";\n setErrorMessage(errorMessage);\n throw error;\n }\n }\n\n try {\n // Send message through WebSocket as JSON with run: true so the\n // agent loop starts automatically in async mode.\n currentSocket.send(JSON.stringify({ ...message, run: true }));\n return { queued: false };\n } catch (error) {\n const errorMessage =\n error instanceof Error ? error.message : \"Failed to send message\";\n setErrorMessage(errorMessage);\n throw error;\n }\n },\n [mainSocket, planningAgentSocket, setErrorMessage, conversationId],\n );\n\n // Track main socket state changes\n useEffect(() => {\n // Only process socket updates if we have a valid URL and socket\n if (mainSocket && wsUrl) {\n // Update state based on socket readyState\n const updateState = () => {\n switch (mainSocket.readyState) {\n case WebSocket.CONNECTING:\n setMainConnectionState(\"CONNECTING\");\n break;\n case WebSocket.OPEN:\n setMainConnectionState(\"OPEN\");\n break;\n case WebSocket.CLOSING:\n setMainConnectionState(\"CLOSING\");\n break;\n case WebSocket.CLOSED:\n setMainConnectionState(\"CLOSED\");\n break;\n default:\n setMainConnectionState(\"CLOSED\");\n break;\n }\n };\n\n updateState();\n }\n }, [mainSocket, wsUrl]);\n\n // Track planning agent socket state changes\n useEffect(() => {\n // Only process socket updates if we have a valid URL and socket\n if (planningAgentSocket && planningAgentWsUrl) {\n // Update state based on socket readyState\n const updateState = () => {\n switch (planningAgentSocket.readyState) {\n case WebSocket.CONNECTING:\n setPlanningConnectionState(\"CONNECTING\");\n break;\n case WebSocket.OPEN:\n setPlanningConnectionState(\"OPEN\");\n break;\n case WebSocket.CLOSING:\n setPlanningConnectionState(\"CLOSING\");\n break;\n case WebSocket.CLOSED:\n setPlanningConnectionState(\"CLOSED\");\n break;\n default:\n setPlanningConnectionState(\"CLOSED\");\n break;\n }\n };\n\n updateState();\n }\n }, [planningAgentSocket, planningAgentWsUrl]);\n\n const contextValue = useMemo(\n () => ({ connectionState, sendMessage, isLoadingHistory, reconnect }),\n [connectionState, sendMessage, isLoadingHistory, reconnect],\n );\n\n return (\n <ConversationWebSocketContext.Provider value={contextValue}>\n {children}\n </ConversationWebSocketContext.Provider>\n );\n}\n\nexport const useConversationWebSocket =\n (): ConversationWebSocketContextType | null => {\n const context = useContext(ConversationWebSocketContext);\n // Return null instead of throwing when not in provider\n // This allows the hook to be called conditionally based on conversation version\n return context || null;\n };\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../query/query-keys.cjs`),t=require(`../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs`),n=require(`../../node_modules/@tanstack/react-query/build/modern/useMutation.cjs`),r=require(`../../contexts/active-backend-context.cjs`),i=require(`../../api/profiles-service/profiles-service.api.cjs`),a=require(`../../api/conversation-metadata-store.cjs`),o=require(`../../api/conversation-service/agent-server-conversation-service.api.cjs`),s=require(`../use-tracking.cjs`),c=require(`../query/use-llm-profiles.cjs`),l=require(`../../api/agent-profiles-service/agent-profiles-service.api.cjs`),u=require(`../query/use-agent-profiles.cjs`),d=require(`../../api/plugins-management-service.cjs`),f=require(`../../utils/plugin-display.cjs`);var p=()=>{let p=t.useQueryClient(),{trackConversationCreated:m}=s.useTracking(),{data:h}=c.useLlmProfiles(),{backend:g,orgId:_}=r.useActiveBackend();return u.useAgentProfiles(),n.useMutation({mutationKey:[`create-conversation`],mutationFn:async t=>{let{query:n,conversationInstructions:r,plugins:s,repository:c,workingDir:u,workspaceMode:m,parentConversationId:v,agentType:y,agentProfileId:b}=t,x;try{x=await p.ensureQueryData({queryKey:[...e.AGENT_PROFILES_QUERY_KEYS.all,g.id,_],queryFn:l.default.listProfiles,...e.AGENT_PROFILES_RETRY_OPTIONS})}catch{}let S=b??x?.active_agent_profile_id??void 0,C=S?x?.profiles?.find(e=>e.id===S):void 0,w=g.kind===`cloud`,T=S;if(!w&&C?.name===`default`&&C?.agent_kind===`openhands`)T=void 0;else if(C?.agent_kind===`openhands`&&C.llm_profile_ref){let t=!1;try{t=(await p.ensureQueryData({queryKey:[...e.LLM_PROFILES_QUERY_KEYS.all,g.id,_],queryFn:i.default.listProfiles,retry:!1})).profiles.some(e=>e.name===C.llm_profile_ref)}catch{}t||(console.warn(`Agent profile "${C.name}" references missing LLM profile "${C.llm_profile_ref}"; launching from agent_settings instead.`),T=void 0)}let E=T?[void 0,T]:[],D=await o.default.createConversation(n,r,s,c?{selected_repository:c.name,selected_branch:c.branch??null,git_provider:c.gitProvider}:null,u,m,v,y,...E),O=D.app_conversation_id,k=s?.map(e=>({source:e.source,ref:e.ref??null,repo_path:e.repo_path??null}))??[],A=k;if(O){let t=[];try{t=await p.ensureQueryData({queryKey:e.PLUGINS_QUERY_KEYS.installed,queryFn:()=>d.default.listInstalledPlugins()})}catch{}let n=new Set(k.map(f.pluginReferenceKey)),r=t.filter(e=>e.enabled).map(e=>({source:e.source,ref:e.resolved_ref??null,repo_path:e.repo_path??null,name:e.name})).filter(e=>!n.has(f.pluginReferenceKey(e)));A=[...k,...r]}let j=T&&C?.agent_kind===`openhands`?C.llm_profile_ref:h?.active_profile??null;if(O&&(j||A.length)){let e=a.getStoredConversationMetadata(O);a.setStoredConversationMetadata(O,{selected_repository:e?.selected_repository??null,selected_branch:e?.selected_branch??null,git_provider:e?.git_provider??null,selected_workspace:e?.selected_workspace??null,workspace_mode:e?.workspace_mode??null,active_profile:j??e?.active_profile??null,plugins:A.length?A:e?.plugins??null})}return{conversation_id:D.app_conversation_id?D.app_conversation_id:`task-${D.id}`,session_api_key:null,url:D.agent_server_url,task_id:D.id}},onSuccess:async(e,t)=>{m({conversationId:e.conversation_id,taskId:e.task_id,hasRepository:!!t.repository,gitProvider:t.repository?.gitProvider,hasWorkspace:!!t.workingDir,workspaceMode:t.workspaceMode,hasInitialQuery:!!t.query,agentType:t.agentType,hasParentConversation:!!t.parentConversationId,entryPoint:t.entryPoint}),p.invalidateQueries({queryKey:[`user`,`conversations`]}),p.invalidateQueries({queryKey:[`start-tasks`]})}})};exports.useCreateConversation=p;
1
+ require(`../../_virtual/_rolldown/runtime.cjs`);const e=require(`../query/query-keys.cjs`),t=require(`../../node_modules/@tanstack/react-query/build/modern/QueryClientProvider.cjs`),n=require(`../../node_modules/@tanstack/react-query/build/modern/useMutation.cjs`),r=require(`../../contexts/active-backend-context.cjs`),i=require(`../../api/profiles-service/profiles-service.api.cjs`),a=require(`../../api/conversation-metadata-store.cjs`),o=require(`../../api/conversation-service/agent-server-conversation-service.api.cjs`),s=require(`../use-tracking.cjs`),c=require(`../query/use-llm-profiles.cjs`),l=require(`../../api/agent-profiles-service/agent-profiles-service.api.cjs`),u=require(`../query/use-agent-profiles.cjs`),d=require(`../../api/plugins-management-service.cjs`),f=require(`../../utils/plugin-display.cjs`);var p=[`create-conversation`],m=()=>{let m=t.useQueryClient(),{trackConversationCreated:h}=s.useTracking(),{data:g}=c.useLlmProfiles(),{backend:_,orgId:v}=r.useActiveBackend();return u.useAgentProfiles(),n.useMutation({mutationKey:p,mutationFn:async t=>{let{query:n,conversationInstructions:r,plugins:s,repository:c,workingDir:u,workspaceMode:p,parentConversationId:h,agentType:y,agentProfileId:b}=t,x;try{x=await m.ensureQueryData({queryKey:[...e.AGENT_PROFILES_QUERY_KEYS.all,_.id,v],queryFn:l.default.listProfiles,...e.AGENT_PROFILES_RETRY_OPTIONS})}catch{}let S=b??x?.active_agent_profile_id??void 0,C=S?x?.profiles?.find(e=>e.id===S):void 0,w=_.kind===`cloud`,T=S;if(!w&&C?.name===`default`&&C?.agent_kind===`openhands`)T=void 0;else if(C?.agent_kind===`openhands`&&C.llm_profile_ref){let t=!1;try{t=(await m.ensureQueryData({queryKey:[...e.LLM_PROFILES_QUERY_KEYS.all,_.id,v],queryFn:i.default.listProfiles,retry:!1})).profiles.some(e=>e.name===C.llm_profile_ref)}catch{}t||(console.warn(`Agent profile "${C.name}" references missing LLM profile "${C.llm_profile_ref}"; launching from agent_settings instead.`),T=void 0)}let E=T?[void 0,T,C?.agent_kind]:[],D=await o.default.createConversation(n,r,s,c?{selected_repository:c.name,selected_branch:c.branch??null,git_provider:c.gitProvider}:null,u,p,h,y,...E),O=D.app_conversation_id,k=s?.map(a.toPluginCoordinates)??[],A=k;if(O){let t=[];try{t=await m.ensureQueryData({queryKey:e.PLUGINS_QUERY_KEYS.installed,queryFn:()=>d.default.listInstalledPlugins()})}catch{}let n=new Set(k.map(f.pluginReferenceKey)),r=t.filter(e=>e.enabled).map(e=>({source:e.source,ref:e.resolved_ref??null,repo_path:e.repo_path??null,name:e.name})).filter(e=>!n.has(f.pluginReferenceKey(e)));A=[...k,...r]}let j=T&&C?.agent_kind===`openhands`?C.llm_profile_ref:g?.active_profile??null;if(O&&(j||A.length)){let e=a.getStoredConversationMetadata(O);a.setStoredConversationMetadata(O,{selected_repository:e?.selected_repository??null,selected_branch:e?.selected_branch??null,git_provider:e?.git_provider??null,selected_workspace:e?.selected_workspace??null,workspace_mode:e?.workspace_mode??null,active_profile:j??e?.active_profile??null,plugins:A.length?A:e?.plugins??null})}return{conversation_id:D.app_conversation_id?D.app_conversation_id:`task-${D.id}`,session_api_key:null,url:D.agent_server_url,task_id:D.id}},onSuccess:async(e,t)=>{h({conversationId:e.conversation_id,taskId:e.task_id,hasRepository:!!t.repository,gitProvider:t.repository?.gitProvider,hasWorkspace:!!t.workingDir,workspaceMode:t.workspaceMode,hasInitialQuery:!!t.query,agentType:t.agentType,hasParentConversation:!!t.parentConversationId,entryPoint:t.entryPoint}),m.invalidateQueries({queryKey:[`user`,`conversations`]}),m.invalidateQueries({queryKey:[`start-tasks`]})}})};exports.CREATE_CONVERSATION_MUTATION_KEY=p,exports.useCreateConversation=m;
2
2
  //# sourceMappingURL=use-create-conversation.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-create-conversation.cjs","names":[],"sources":["../../../src/hooks/mutation/use-create-conversation.ts"],"sourcesContent":["import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport { PluginSpec } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport { SuggestedTask } from \"#/utils/types\";\nimport { Provider } from \"#/types/settings\";\nimport { useTracking } from \"#/hooks/use-tracking\";\nimport { useLlmProfiles } from \"#/hooks/query/use-llm-profiles\";\nimport { useAgentProfiles } from \"#/hooks/query/use-agent-profiles\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport ProfilesService from \"#/api/profiles-service/profiles-service.api\";\nimport AgentProfilesService, {\n WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME,\n type AgentProfileListResponse,\n} from \"#/api/agent-profiles-service/agent-profiles-service.api\";\nimport PluginsManagementService, {\n type InstalledPluginInfo,\n} from \"#/api/plugins-management-service\";\nimport {\n PLUGINS_QUERY_KEYS,\n LLM_PROFILES_QUERY_KEYS,\n AGENT_PROFILES_QUERY_KEYS,\n AGENT_PROFILES_RETRY_OPTIONS,\n} from \"#/hooks/query/query-keys\";\nimport { pluginReferenceKey } from \"#/utils/plugin-display\";\nimport {\n getStoredConversationMetadata,\n setStoredConversationMetadata,\n type WorkspaceMode,\n} from \"#/api/conversation-metadata-store\";\n\ninterface CreateConversationVariables {\n query?: string;\n repository?: {\n name: string;\n gitProvider: Provider;\n branch?: string;\n };\n suggestedTask?: SuggestedTask;\n conversationInstructions?: string;\n parentConversationId?: string;\n agentType?: \"default\" | \"plan\";\n plugins?: PluginSpec[];\n workingDir?: string;\n workspaceMode?: WorkspaceMode;\n // Launch from a specific AgentProfile (local backend). When omitted, the\n // active AgentProfile (if any) is used so home-composed conversations\n // launch from the user's selected profile (#3727).\n agentProfileId?: string;\n entryPoint?: string; // analytics only; not forwarded to the service\n}\n\ninterface CreateConversationResponse {\n conversation_id: string;\n session_api_key: string | null;\n url: string | null;\n task_id?: string;\n}\n\nexport const useCreateConversation = () => {\n const queryClient = useQueryClient();\n const { trackConversationCreated } = useTracking();\n // Cache-warm on the home page (the profile picker reads the same query).\n // Stamped onto the conversation at creation so the switcher can show the\n // exact profile even when several profiles share a model (#1082).\n const { data: llmProfiles } = useLlmProfiles();\n // Warm the agent-profiles cache too — the launch path below awaits the same\n // query via ensureQueryData, so a warm cache makes home-launch instant. The\n // hook's maybe-unresolved data is deliberately NOT read at launch time:\n // activation is pointer-only (it never writes agent_settings), so racing a\n // cold cache into the agent_settings fallback would silently launch the\n // wrong agent.\n const { backend, orgId } = useActiveBackend();\n useAgentProfiles();\n\n return useMutation({\n mutationKey: [\"create-conversation\"],\n mutationFn: async (\n variables: CreateConversationVariables,\n ): Promise<CreateConversationResponse> => {\n const {\n query,\n conversationInstructions,\n plugins,\n repository,\n workingDir,\n workspaceMode,\n parentConversationId,\n agentType,\n agentProfileId,\n } = variables;\n\n // The active AgentProfile is the default launch profile for new\n // conversations (#3727), on both local and cloud (cloud gained\n // /api/agent-profiles in OpenHands #15060, #3730). Await the list from\n // the shared query cache: a send fired before the home query resolves\n // must still launch from the active profile, not fall through to the\n // agent_settings path. Degrades safely: if the fetch errors (older\n // backend without the surface), this stays undefined and creation falls\n // back to the encrypted agent_settings launch path.\n let agentProfiles: AgentProfileListResponse | undefined;\n try {\n agentProfiles = await queryClient.ensureQueryData({\n queryKey: [...AGENT_PROFILES_QUERY_KEYS.all, backend.id, orgId],\n queryFn: AgentProfilesService.listProfiles,\n // Shared with useAgentProfiles and redirectIfAcpActive so the retry\n // policy for this surface can't drift between call sites (#1571\n // review): a backend without it fails every one of these on every\n // call, so degrade to the fallback immediately rather than sitting\n // through the default exponential-backoff retries each time.\n ...AGENT_PROFILES_RETRY_OPTIONS,\n });\n } catch {\n // Profiles unavailable → legacy agent_settings launch.\n }\n\n const requestedAgentProfileId =\n agentProfileId ?? agentProfiles?.active_agent_profile_id ?? undefined;\n\n // Fall back to the legacy agent_settings launch when the resolved agent\n // profile can't resolve its LLM. The agent-server seeds a `default`\n // openhands profile whose `llm_profile_ref` can point at an LLM profile\n // that doesn't exist (fresh store, or one configured with named profiles\n // only); launching from it 404s (\"LLM profile '<ref>' not found\") and\n // would brick home-launch. agent_settings reflects the active LLM, so the\n // fallback degrades cleanly until the seed mirrors it (SDK #3933).\n // ACP profiles carry no llm_profile_ref, so they're never gated here.\n const resolvedAgentProfile = requestedAgentProfileId\n ? agentProfiles?.profiles?.find(\n (profile) => profile.id === requestedAgentProfileId,\n )\n : undefined;\n // Cloud has no `agent_settings` payload to fall back to — the downgrade\n // below only makes sense on local, where it exists and carries the\n // canvas-only enrichments. Gating it here keeps cloud always launching\n // from the resolved profile id, so the conversation gets\n // `launched_agent_profile` stamped and the profile's config applied\n // (#1571 review).\n const isCloud = backend.kind === \"cloud\";\n let effectiveAgentProfileId = requestedAgentProfileId;\n if (\n !isCloud &&\n resolvedAgentProfile?.name === WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME &&\n resolvedAgentProfile?.agent_kind === \"openhands\"\n ) {\n // The seeded OpenHands `default` profile is the enriched baseline, not a\n // deliberate profile pick — it mirrors global agent_settings. Launch it\n // via agent_settings so the canvas-only enrichments the profile-resolution\n // path drops survive for the common home-launch: the <RUNTIME_SERVICES>\n // system-message suffix, the canvas_ui tool, and project-skill loading\n // (buildAgentContext). Named profiles are deliberate custom configs and\n // still use the profile path (accepting that enrichment boundary).\n // Trade-off: per-profile fields set on `default` itself don't apply on\n // home-launch — custom per-profile config belongs in a named profile.\n //\n // Scoped to OpenHands: an ACP `default` must keep the profile path.\n // Activation is pointer-only, so global agent_settings is stale (often\n // still OpenHands) when an ACP profile is active — launching it via\n // agent_settings would start the wrong agent. ACP also carries no\n // <RUNTIME_SERVICES>/canvas_ui enrichment, so there's nothing to preserve.\n //\n // Scoped to local: cloud never writes agent_settings, so it always\n // resolves `default` server-side via agent_profile_id (validated below).\n effectiveAgentProfileId = undefined;\n } else if (\n resolvedAgentProfile?.agent_kind === \"openhands\" &&\n resolvedAgentProfile.llm_profile_ref\n ) {\n // Await the LLM-profile list rather than reading the maybe-unresolved\n // `useLlmProfiles()` result: a send fired before that query loads (or\n // after it errors) must still validate the ref, not launch blind.\n let llmProfileExists = false;\n try {\n const llm = await queryClient.ensureQueryData({\n queryKey: [...LLM_PROFILES_QUERY_KEYS.all, backend.id, orgId],\n queryFn: ProfilesService.listProfiles,\n // Match the agent-profiles fetch above: on a backend where this\n // errors, fall back to agent_settings immediately rather than\n // stalling the send through the default exponential backoff.\n retry: false,\n });\n llmProfileExists = llm.profiles.some(\n (profile) => profile.name === resolvedAgentProfile.llm_profile_ref,\n );\n } catch {\n // List unavailable → can't validate → fall back to agent_settings.\n }\n if (!llmProfileExists) {\n // Downgrade is silent in the UI; leave a diagnosable trace.\n console.warn(\n `Agent profile \"${resolvedAgentProfile.name}\" references missing ` +\n `LLM profile \"${resolvedAgentProfile.llm_profile_ref}\"; ` +\n \"launching from agent_settings instead.\",\n );\n effectiveAgentProfileId = undefined;\n }\n }\n\n // Only extend the call with the [sandboxId, agentProfileId] tail when\n // launching from a profile, so a plain create stays byte-identical to\n // the legacy agent_settings path (#3727). sandboxId is unused here.\n // TODO(#1587): createConversation has grown to 10 positional params;\n // refactor it to an options object so this position-skipping tail isn't\n // needed.\n const profileArgs: [undefined, string] | [] = effectiveAgentProfileId\n ? [undefined, effectiveAgentProfileId]\n : [];\n\n const conversation =\n await AgentServerConversationService.createConversation(\n query,\n conversationInstructions,\n plugins,\n repository\n ? {\n selected_repository: repository.name,\n selected_branch: repository.branch ?? null,\n git_provider: repository.gitProvider,\n }\n : null,\n workingDir,\n workspaceMode,\n parentConversationId,\n agentType,\n ...profileArgs,\n );\n\n // Stamp the active LLM profile onto the (local) conversation so the\n // chat switcher shows the exact profile even when several profiles\n // share a model (#1082). Cloud conversations don't use local profiles\n // (app_conversation_id stays null until the sandbox is READY). Merge so\n // the repo/workspace metadata the service just persisted is preserved.\n const localConversationId = conversation.app_conversation_id;\n // Snapshot the conversation's plugins into client-side metadata so the\n // in-conversation plugins view can show what's loaded (coordinates only\n // — strip parameters, which may carry secrets). The agent-server doesn't\n // return a live conversation's loaded plugins, so this snapshot is the\n // source for that view. Two sources, deduped by coordinates:\n // 1. plugins explicitly attached at creation (e.g. the /launch flow);\n // 2. enabled installed plugins, which the SDK auto-loads into every new\n // local conversation (see use-set-plugin-enabled).\n const explicitPlugins =\n plugins?.map((plugin) => ({\n source: plugin.source,\n ref: plugin.ref ?? null,\n repo_path: plugin.repo_path ?? null,\n })) ?? [];\n let attachedPlugins: PluginSpec[] = explicitPlugins;\n if (localConversationId) {\n let installed: InstalledPluginInfo[] = [];\n try {\n installed = await queryClient.ensureQueryData({\n queryKey: PLUGINS_QUERY_KEYS.installed,\n queryFn: () => PluginsManagementService.listInstalledPlugins(),\n });\n } catch {\n // Best-effort: never let plugin lookup block conversation creation.\n }\n const seen = new Set(explicitPlugins.map(pluginReferenceKey));\n const enabledInstalled = installed\n .filter((plugin) => plugin.enabled)\n .map((plugin) => ({\n source: plugin.source,\n ref: plugin.resolved_ref ?? null,\n repo_path: plugin.repo_path ?? null,\n // Keep the human-friendly name so the plugins view shows e.g.\n // \"city-weather\" rather than deriving \"local\" from the source.\n name: plugin.name,\n }))\n .filter((plugin) => !seen.has(pluginReferenceKey(plugin)));\n attachedPlugins = [...explicitPlugins, ...enabledInstalled];\n }\n // A launch from a named OpenHands profile runs that profile's\n // `llm_profile_ref`, which can differ from the standalone active LLM\n // profile — stamp the ref so the switcher pill names the exact profile\n // the conversation runs (#1082). The agent_settings path (the `default`\n // baseline or a dangling ref, where effectiveAgentProfileId is cleared)\n // runs the active LLM, so it keeps `active_profile`. ACP profiles carry\n // no LLM profile, so they fall through to the active-profile stamp\n // (unused by the ACP model chip).\n const activeProfile =\n effectiveAgentProfileId &&\n resolvedAgentProfile?.agent_kind === \"openhands\"\n ? resolvedAgentProfile.llm_profile_ref\n : (llmProfiles?.active_profile ?? null);\n if (localConversationId && (activeProfile || attachedPlugins.length)) {\n const prev = getStoredConversationMetadata(localConversationId);\n setStoredConversationMetadata(localConversationId, {\n selected_repository: prev?.selected_repository ?? null,\n selected_branch: prev?.selected_branch ?? null,\n git_provider: prev?.git_provider ?? null,\n selected_workspace: prev?.selected_workspace ?? null,\n workspace_mode: prev?.workspace_mode ?? null,\n active_profile: activeProfile ?? prev?.active_profile ?? null,\n plugins: attachedPlugins.length\n ? attachedPlugins\n : (prev?.plugins ?? null),\n });\n }\n\n // OpenHands cloud pattern: when the start task isn't immediately\n // READY (cloud sandbox is still provisioning),\n // app_conversation_id is null. We return a `task-{id}` URL so the\n // conversation route's useTaskPolling can drive it to READY and\n // then redirect to the real `/conversations/{app_conversation_id}`.\n const conversationId = conversation.app_conversation_id\n ? conversation.app_conversation_id\n : `task-${conversation.id}`;\n\n return {\n conversation_id: conversationId,\n session_api_key: null,\n url: conversation.agent_server_url,\n task_id: conversation.id,\n };\n },\n onSuccess: async (data, variables) => {\n trackConversationCreated({\n conversationId: data.conversation_id,\n taskId: data.task_id,\n hasRepository: !!variables.repository,\n gitProvider: variables.repository?.gitProvider,\n hasWorkspace: !!variables.workingDir,\n workspaceMode: variables.workspaceMode,\n hasInitialQuery: !!variables.query,\n agentType: variables.agentType,\n hasParentConversation: !!variables.parentConversationId,\n entryPoint: variables.entryPoint,\n });\n\n // Invalidate (rather than remove) so the existing paginated list stays\n // rendered while a background refetch picks up the new conversation.\n // `removeQueries` would wipe the cache and force the panel back to its\n // initial loading state, dropping loaded pages and scroll position.\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversations\"],\n });\n // The cloud path returns a start task (no app_conversation_id\n // yet); the sidebar surfaces those via `useStartTasks` which doesn't\n // poll, so invalidate it explicitly so the in-flight task shows up\n // in the conversation list immediately.\n queryClient.invalidateQueries({\n queryKey: [\"start-tasks\"],\n });\n },\n });\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-create-conversation.cjs","names":[],"sources":["../../../src/hooks/mutation/use-create-conversation.ts"],"sourcesContent":["import { useMutation, useQueryClient } from \"@tanstack/react-query\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport { PluginSpec } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport { SuggestedTask } from \"#/utils/types\";\nimport { AgentKind, Provider } from \"#/types/settings\";\nimport { useTracking } from \"#/hooks/use-tracking\";\nimport { useLlmProfiles } from \"#/hooks/query/use-llm-profiles\";\nimport { useAgentProfiles } from \"#/hooks/query/use-agent-profiles\";\nimport { useActiveBackend } from \"#/contexts/active-backend-context\";\nimport ProfilesService from \"#/api/profiles-service/profiles-service.api\";\nimport AgentProfilesService, {\n WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME,\n type AgentProfileListResponse,\n} from \"#/api/agent-profiles-service/agent-profiles-service.api\";\nimport PluginsManagementService, {\n type InstalledPluginInfo,\n} from \"#/api/plugins-management-service\";\nimport {\n PLUGINS_QUERY_KEYS,\n LLM_PROFILES_QUERY_KEYS,\n AGENT_PROFILES_QUERY_KEYS,\n AGENT_PROFILES_RETRY_OPTIONS,\n} from \"#/hooks/query/query-keys\";\nimport { pluginReferenceKey } from \"#/utils/plugin-display\";\nimport {\n getStoredConversationMetadata,\n setStoredConversationMetadata,\n toPluginCoordinates,\n type WorkspaceMode,\n} from \"#/api/conversation-metadata-store\";\n\nexport interface CreateConversationVariables {\n query?: string;\n repository?: {\n name: string;\n gitProvider: Provider;\n branch?: string;\n };\n suggestedTask?: SuggestedTask;\n conversationInstructions?: string;\n parentConversationId?: string;\n agentType?: \"default\" | \"plan\";\n plugins?: PluginSpec[];\n workingDir?: string;\n workspaceMode?: WorkspaceMode;\n // Launch from a specific AgentProfile (local backend). When omitted, the\n // active AgentProfile (if any) is used so home-composed conversations\n // launch from the user's selected profile (#3727).\n agentProfileId?: string;\n entryPoint?: string; // analytics only; not forwarded to the service\n}\n\nexport const CREATE_CONVERSATION_MUTATION_KEY = [\"create-conversation\"];\n\ninterface CreateConversationResponse {\n conversation_id: string;\n session_api_key: string | null;\n url: string | null;\n task_id?: string;\n}\n\nexport const useCreateConversation = () => {\n const queryClient = useQueryClient();\n const { trackConversationCreated } = useTracking();\n // Cache-warm on the home page (the profile picker reads the same query).\n // Stamped onto the conversation at creation so the switcher can show the\n // exact profile even when several profiles share a model (#1082).\n const { data: llmProfiles } = useLlmProfiles();\n // Warm the agent-profiles cache too — the launch path below awaits the same\n // query via ensureQueryData, so a warm cache makes home-launch instant. The\n // hook's maybe-unresolved data is deliberately NOT read at launch time:\n // activation is pointer-only (it never writes agent_settings), so racing a\n // cold cache into the agent_settings fallback would silently launch the\n // wrong agent.\n const { backend, orgId } = useActiveBackend();\n useAgentProfiles();\n\n return useMutation({\n mutationKey: CREATE_CONVERSATION_MUTATION_KEY,\n mutationFn: async (\n variables: CreateConversationVariables,\n ): Promise<CreateConversationResponse> => {\n const {\n query,\n conversationInstructions,\n plugins,\n repository,\n workingDir,\n workspaceMode,\n parentConversationId,\n agentType,\n agentProfileId,\n } = variables;\n\n // The active AgentProfile is the default launch profile for new\n // conversations (#3727), on both local and cloud (cloud gained\n // /api/agent-profiles in OpenHands #15060, #3730). Await the list from\n // the shared query cache: a send fired before the home query resolves\n // must still launch from the active profile, not fall through to the\n // agent_settings path. Degrades safely: if the fetch errors (older\n // backend without the surface), this stays undefined and creation falls\n // back to the encrypted agent_settings launch path.\n let agentProfiles: AgentProfileListResponse | undefined;\n try {\n agentProfiles = await queryClient.ensureQueryData({\n queryKey: [...AGENT_PROFILES_QUERY_KEYS.all, backend.id, orgId],\n queryFn: AgentProfilesService.listProfiles,\n // Shared with useAgentProfiles and redirectIfAcpActive so the retry\n // policy for this surface can't drift between call sites (#1571\n // review): a backend without it fails every one of these on every\n // call, so degrade to the fallback immediately rather than sitting\n // through the default exponential-backoff retries each time.\n ...AGENT_PROFILES_RETRY_OPTIONS,\n });\n } catch {\n // Profiles unavailable → legacy agent_settings launch.\n }\n\n const requestedAgentProfileId =\n agentProfileId ?? agentProfiles?.active_agent_profile_id ?? undefined;\n\n // Fall back to the legacy agent_settings launch when the resolved agent\n // profile can't resolve its LLM. The agent-server seeds a `default`\n // openhands profile whose `llm_profile_ref` can point at an LLM profile\n // that doesn't exist (fresh store, or one configured with named profiles\n // only); launching from it 404s (\"LLM profile '<ref>' not found\") and\n // would brick home-launch. agent_settings reflects the active LLM, so the\n // fallback degrades cleanly until the seed mirrors it (SDK #3933).\n // ACP profiles carry no llm_profile_ref, so they're never gated here.\n const resolvedAgentProfile = requestedAgentProfileId\n ? agentProfiles?.profiles?.find(\n (profile) => profile.id === requestedAgentProfileId,\n )\n : undefined;\n // Cloud has no `agent_settings` payload to fall back to — the downgrade\n // below only makes sense on local, where it exists and carries the\n // canvas-only enrichments. Gating it here keeps cloud always launching\n // from the resolved profile id, so the conversation gets\n // `launched_agent_profile` stamped and the profile's config applied\n // (#1571 review).\n const isCloud = backend.kind === \"cloud\";\n let effectiveAgentProfileId = requestedAgentProfileId;\n if (\n !isCloud &&\n resolvedAgentProfile?.name === WELL_KNOWN_DEFAULT_AGENT_PROFILE_NAME &&\n resolvedAgentProfile?.agent_kind === \"openhands\"\n ) {\n // The seeded OpenHands `default` profile is the enriched baseline, not a\n // deliberate profile pick — it mirrors global agent_settings. Launch it\n // via agent_settings so the canvas-only enrichments the profile-resolution\n // path drops survive for the common home-launch: the <RUNTIME_SERVICES>\n // system-message suffix and project-skill loading (buildAgentContext).\n // Named profiles are deliberate custom configs and still use the profile\n // path (accepting that enrichment boundary).\n // Trade-off: per-profile fields set on `default` itself don't apply on\n // home-launch — custom per-profile config belongs in a named profile.\n //\n // Scoped to OpenHands: an ACP `default` must keep the profile path.\n // Activation is pointer-only, so global agent_settings is stale (often\n // still OpenHands) when an ACP profile is active — launching it via\n // agent_settings would start the wrong agent. ACP carries no\n // <RUNTIME_SERVICES> enrichment, so there's nothing to preserve.\n //\n // Scoped to local: cloud never writes agent_settings, so it always\n // resolves `default` server-side via agent_profile_id (validated below).\n effectiveAgentProfileId = undefined;\n } else if (\n resolvedAgentProfile?.agent_kind === \"openhands\" &&\n resolvedAgentProfile.llm_profile_ref\n ) {\n // Await the LLM-profile list rather than reading the maybe-unresolved\n // `useLlmProfiles()` result: a send fired before that query loads (or\n // after it errors) must still validate the ref, not launch blind.\n let llmProfileExists = false;\n try {\n const llm = await queryClient.ensureQueryData({\n queryKey: [...LLM_PROFILES_QUERY_KEYS.all, backend.id, orgId],\n queryFn: ProfilesService.listProfiles,\n // Match the agent-profiles fetch above: on a backend where this\n // errors, fall back to agent_settings immediately rather than\n // stalling the send through the default exponential backoff.\n retry: false,\n });\n llmProfileExists = llm.profiles.some(\n (profile) => profile.name === resolvedAgentProfile.llm_profile_ref,\n );\n } catch {\n // List unavailable → can't validate → fall back to agent_settings.\n }\n if (!llmProfileExists) {\n // Downgrade is silent in the UI; leave a diagnosable trace.\n console.warn(\n `Agent profile \"${resolvedAgentProfile.name}\" references missing ` +\n `LLM profile \"${resolvedAgentProfile.llm_profile_ref}\"; ` +\n \"launching from agent_settings instead.\",\n );\n effectiveAgentProfileId = undefined;\n }\n }\n\n // Only extend the call with the profile tail when launching from a\n // profile, so a plain create stays byte-identical to the legacy\n // agent_settings path (#3727). sandboxId is unused here.\n // TODO(#1587): createConversation has grown to 11 positional params;\n // refactor it to an options object so this position-skipping tail isn't\n // needed.\n const profileArgs: [undefined, string, AgentKind | undefined] | [] =\n effectiveAgentProfileId\n ? [\n undefined,\n effectiveAgentProfileId,\n resolvedAgentProfile?.agent_kind,\n ]\n : [];\n\n const conversation =\n await AgentServerConversationService.createConversation(\n query,\n conversationInstructions,\n plugins,\n repository\n ? {\n selected_repository: repository.name,\n selected_branch: repository.branch ?? null,\n git_provider: repository.gitProvider,\n }\n : null,\n workingDir,\n workspaceMode,\n parentConversationId,\n agentType,\n ...profileArgs,\n );\n\n // Stamp the active LLM profile onto the (local) conversation so the\n // chat switcher shows the exact profile even when several profiles\n // share a model (#1082). Cloud conversations don't use local profiles\n // (app_conversation_id stays null until the sandbox is READY). Merge so\n // the repo/workspace metadata the service just persisted is preserved.\n const localConversationId = conversation.app_conversation_id;\n // Snapshot the conversation's plugins into client-side metadata so the\n // in-conversation plugins view can show what's loaded (coordinates only\n // — strip parameters, which may carry secrets). The agent-server doesn't\n // return a live conversation's loaded plugins, so this snapshot is the\n // source for that view. Two sources, deduped by coordinates:\n // 1. plugins explicitly attached at creation (e.g. the /launch flow);\n // 2. enabled installed plugins, which the SDK auto-loads into every new\n // local conversation (see use-set-plugin-enabled).\n const explicitPlugins = plugins?.map(toPluginCoordinates) ?? [];\n let attachedPlugins: PluginSpec[] = explicitPlugins;\n if (localConversationId) {\n let installed: InstalledPluginInfo[] = [];\n try {\n installed = await queryClient.ensureQueryData({\n queryKey: PLUGINS_QUERY_KEYS.installed,\n queryFn: () => PluginsManagementService.listInstalledPlugins(),\n });\n } catch {\n // Best-effort: never let plugin lookup block conversation creation.\n }\n const seen = new Set(explicitPlugins.map(pluginReferenceKey));\n const enabledInstalled = installed\n .filter((plugin) => plugin.enabled)\n .map((plugin) => ({\n source: plugin.source,\n ref: plugin.resolved_ref ?? null,\n repo_path: plugin.repo_path ?? null,\n // Keep the human-friendly name so the plugins view shows e.g.\n // \"city-weather\" rather than deriving \"local\" from the source.\n name: plugin.name,\n }))\n .filter((plugin) => !seen.has(pluginReferenceKey(plugin)));\n attachedPlugins = [...explicitPlugins, ...enabledInstalled];\n }\n // A launch from a named OpenHands profile runs that profile's\n // `llm_profile_ref`, which can differ from the standalone active LLM\n // profile — stamp the ref so the switcher pill names the exact profile\n // the conversation runs (#1082). The agent_settings path (the `default`\n // baseline or a dangling ref, where effectiveAgentProfileId is cleared)\n // runs the active LLM, so it keeps `active_profile`. ACP profiles carry\n // no LLM profile, so they fall through to the active-profile stamp\n // (unused by the ACP model chip).\n const activeProfile =\n effectiveAgentProfileId &&\n resolvedAgentProfile?.agent_kind === \"openhands\"\n ? resolvedAgentProfile.llm_profile_ref\n : (llmProfiles?.active_profile ?? null);\n if (localConversationId && (activeProfile || attachedPlugins.length)) {\n const prev = getStoredConversationMetadata(localConversationId);\n setStoredConversationMetadata(localConversationId, {\n selected_repository: prev?.selected_repository ?? null,\n selected_branch: prev?.selected_branch ?? null,\n git_provider: prev?.git_provider ?? null,\n selected_workspace: prev?.selected_workspace ?? null,\n workspace_mode: prev?.workspace_mode ?? null,\n active_profile: activeProfile ?? prev?.active_profile ?? null,\n plugins: attachedPlugins.length\n ? attachedPlugins\n : (prev?.plugins ?? null),\n });\n }\n\n // OpenHands cloud pattern: when the start task isn't immediately\n // READY (cloud sandbox is still provisioning),\n // app_conversation_id is null. We return a `task-{id}` URL so the\n // conversation route's useTaskPolling can drive it to READY and\n // then redirect to the real `/conversations/{app_conversation_id}`.\n const conversationId = conversation.app_conversation_id\n ? conversation.app_conversation_id\n : `task-${conversation.id}`;\n\n return {\n conversation_id: conversationId,\n session_api_key: null,\n url: conversation.agent_server_url,\n task_id: conversation.id,\n };\n },\n onSuccess: async (data, variables) => {\n trackConversationCreated({\n conversationId: data.conversation_id,\n taskId: data.task_id,\n hasRepository: !!variables.repository,\n gitProvider: variables.repository?.gitProvider,\n hasWorkspace: !!variables.workingDir,\n workspaceMode: variables.workspaceMode,\n hasInitialQuery: !!variables.query,\n agentType: variables.agentType,\n hasParentConversation: !!variables.parentConversationId,\n entryPoint: variables.entryPoint,\n });\n\n // Invalidate (rather than remove) so the existing paginated list stays\n // rendered while a background refetch picks up the new conversation.\n // `removeQueries` would wipe the cache and force the panel back to its\n // initial loading state, dropping loaded pages and scroll position.\n queryClient.invalidateQueries({\n queryKey: [\"user\", \"conversations\"],\n });\n // The cloud path returns a start task (no app_conversation_id\n // yet); the sidebar surfaces those via `useStartTasks` which doesn't\n // poll, so invalidate it explicitly so the in-flight task shows up\n // in the conversation list immediately.\n queryClient.invalidateQueries({\n queryKey: [\"start-tasks\"],\n });\n },\n });\n};\n"],"mappings":"szBAoDA,IAAa,EAAmC,CAAC,sBAAsB,CAS1D,MAA8B,CACzC,IAAM,EAAc,EAAA,gBAAgB,CAC9B,CAAE,4BAA6B,EAAA,aAAa,CAI5C,CAAE,KAAM,GAAgB,EAAA,gBAAgB,CAOxC,CAAE,UAAS,SAAU,EAAA,kBAAkB,CAG7C,OAFA,EAAA,kBAAkB,CAEX,EAAA,YAAY,CACjB,YAAa,EACb,WAAY,KACV,IACwC,CACxC,GAAM,CACJ,QACA,2BACA,UACA,aACA,aACA,gBACA,uBACA,YACA,kBACE,EAUA,EACJ,GAAI,CACF,EAAgB,MAAM,EAAY,gBAAgB,CAChD,SAAU,CAAC,GAAG,EAAA,0BAA0B,IAAK,EAAQ,GAAI,EAAM,CAC/D,QAAS,EAAA,QAAqB,aAM9B,GAAG,EAAA,6BACJ,CAAC,MACI,EAIR,IAAM,EACJ,GAAkB,GAAe,yBAA2B,IAAA,GAUxD,EAAuB,EACzB,GAAe,UAAU,KACtB,GAAY,EAAQ,KAAO,EAC7B,CACD,IAAA,GAOE,EAAU,EAAQ,OAAS,QAC7B,EAA0B,EAC9B,GACE,CAAC,GACD,GAAsB,OAAA,WACtB,GAAsB,aAAe,YAoBrC,EAA0B,IAAA,WAE1B,GAAsB,aAAe,aACrC,EAAqB,gBACrB,CAIA,IAAI,EAAmB,GACvB,GAAI,CASF,GAAmB,MARD,EAAY,gBAAgB,CAC5C,SAAU,CAAC,GAAG,EAAA,wBAAwB,IAAK,EAAQ,GAAI,EAAM,CAC7D,QAAS,EAAA,QAAgB,aAIzB,MAAO,GACR,CAAC,EACqB,SAAS,KAC7B,GAAY,EAAQ,OAAS,EAAqB,gBACpD,MACK,EAGH,IAEH,QAAQ,KACN,kBAAkB,EAAqB,KAAK,oCAC1B,EAAqB,gBAAgB,2CAExD,CACD,EAA0B,IAAA,IAU9B,IAAM,EACJ,EACI,CACE,IAAA,GACA,EACA,GAAsB,WACvB,CACD,EAAE,CAEF,EACJ,MAAM,EAAA,QAA+B,mBACnC,EACA,EACA,EACA,EACI,CACE,oBAAqB,EAAW,KAChC,gBAAiB,EAAW,QAAU,KACtC,aAAc,EAAW,YAC1B,CACD,KACJ,EACA,EACA,EACA,EACA,GAAG,EACJ,CAOG,EAAsB,EAAa,oBASnC,EAAkB,GAAS,IAAI,EAAA,oBAAoB,EAAI,EAAE,CAC3D,EAAgC,EACpC,GAAI,EAAqB,CACvB,IAAI,EAAmC,EAAE,CACzC,GAAI,CACF,EAAY,MAAM,EAAY,gBAAgB,CAC5C,SAAU,EAAA,mBAAmB,UAC7B,YAAe,EAAA,QAAyB,sBAAsB,CAC/D,CAAC,MACI,EAGR,IAAM,EAAO,IAAI,IAAI,EAAgB,IAAI,EAAA,mBAAmB,CAAC,CACvD,EAAmB,EACtB,OAAQ,GAAW,EAAO,QAAQ,CAClC,IAAK,IAAY,CAChB,OAAQ,EAAO,OACf,IAAK,EAAO,cAAgB,KAC5B,UAAW,EAAO,WAAa,KAG/B,KAAM,EAAO,KACd,EAAE,CACF,OAAQ,GAAW,CAAC,EAAK,IAAI,EAAA,mBAAmB,EAAO,CAAC,CAAC,CAC5D,EAAkB,CAAC,GAAG,EAAiB,GAAG,EAAiB,CAU7D,IAAM,EACJ,GACA,GAAsB,aAAe,YACjC,EAAqB,gBACpB,GAAa,gBAAkB,KACtC,GAAI,IAAwB,GAAiB,EAAgB,QAAS,CACpE,IAAM,EAAO,EAAA,8BAA8B,EAAoB,CAC/D,EAAA,8BAA8B,EAAqB,CACjD,oBAAqB,GAAM,qBAAuB,KAClD,gBAAiB,GAAM,iBAAmB,KAC1C,aAAc,GAAM,cAAgB,KACpC,mBAAoB,GAAM,oBAAsB,KAChD,eAAgB,GAAM,gBAAkB,KACxC,eAAgB,GAAiB,GAAM,gBAAkB,KACzD,QAAS,EAAgB,OACrB,EACC,GAAM,SAAW,KACvB,CAAC,CAYJ,MAAO,CACL,gBALqB,EAAa,oBAChC,EAAa,oBACb,QAAQ,EAAa,KAIvB,gBAAiB,KACjB,IAAK,EAAa,iBAClB,QAAS,EAAa,GACvB,EAEH,UAAW,MAAO,EAAM,IAAc,CACpC,EAAyB,CACvB,eAAgB,EAAK,gBACrB,OAAQ,EAAK,QACb,cAAe,CAAC,CAAC,EAAU,WAC3B,YAAa,EAAU,YAAY,YACnC,aAAc,CAAC,CAAC,EAAU,WAC1B,cAAe,EAAU,cACzB,gBAAiB,CAAC,CAAC,EAAU,MAC7B,UAAW,EAAU,UACrB,sBAAuB,CAAC,CAAC,EAAU,qBACnC,WAAY,EAAU,WACvB,CAAC,CAMF,EAAY,kBAAkB,CAC5B,SAAU,CAAC,OAAQ,gBAAgB,CACpC,CAAC,CAKF,EAAY,kBAAkB,CAC5B,SAAU,CAAC,cAAc,CAC1B,CAAC,EAEL,CAAC"}
@@ -2,7 +2,7 @@ import { PluginSpec } from "#/api/conversation-service/agent-server-conversation
2
2
  import { SuggestedTask } from "#/utils/types";
3
3
  import { Provider } from "#/types/settings";
4
4
  import { type WorkspaceMode } from "#/api/conversation-metadata-store";
5
- interface CreateConversationVariables {
5
+ export interface CreateConversationVariables {
6
6
  query?: string;
7
7
  repository?: {
8
8
  name: string;
@@ -19,6 +19,7 @@ interface CreateConversationVariables {
19
19
  agentProfileId?: string;
20
20
  entryPoint?: string;
21
21
  }
22
+ export declare const CREATE_CONVERSATION_MUTATION_KEY: string[];
22
23
  interface CreateConversationResponse {
23
24
  conversation_id: string;
24
25
  session_api_key: string | null;
@@ -3,96 +3,96 @@ import { useQueryClient as i } from "../../node_modules/@tanstack/react-query/bu
3
3
  import { useMutation as a } from "../../node_modules/@tanstack/react-query/build/modern/useMutation.js";
4
4
  import { useActiveBackend as o } from "../../contexts/active-backend-context.js";
5
5
  import s from "../../api/profiles-service/profiles-service.api.js";
6
- import { getStoredConversationMetadata as c, setStoredConversationMetadata as l } from "../../api/conversation-metadata-store.js";
7
- import u from "../../api/conversation-service/agent-server-conversation-service.api.js";
8
- import { useTracking as d } from "../use-tracking.js";
9
- import { useLlmProfiles as f } from "../query/use-llm-profiles.js";
10
- import p from "../../api/agent-profiles-service/agent-profiles-service.api.js";
11
- import { useAgentProfiles as m } from "../query/use-agent-profiles.js";
12
- import h from "../../api/plugins-management-service.js";
13
- import { pluginReferenceKey as g } from "../../utils/plugin-display.js";
6
+ import { getStoredConversationMetadata as c, setStoredConversationMetadata as l, toPluginCoordinates as u } from "../../api/conversation-metadata-store.js";
7
+ import d from "../../api/conversation-service/agent-server-conversation-service.api.js";
8
+ import { useTracking as f } from "../use-tracking.js";
9
+ import { useLlmProfiles as p } from "../query/use-llm-profiles.js";
10
+ import m from "../../api/agent-profiles-service/agent-profiles-service.api.js";
11
+ import { useAgentProfiles as h } from "../query/use-agent-profiles.js";
12
+ import g from "../../api/plugins-management-service.js";
13
+ import { pluginReferenceKey as _ } from "../../utils/plugin-display.js";
14
14
  //#region src/hooks/mutation/use-create-conversation.ts
15
- var _ = () => {
16
- let _ = i(), { trackConversationCreated: v } = d(), { data: y } = f(), { backend: b, orgId: x } = o();
17
- return m(), a({
18
- mutationKey: ["create-conversation"],
15
+ var v = ["create-conversation"], y = () => {
16
+ let y = i(), { trackConversationCreated: b } = f(), { data: x } = p(), { backend: S, orgId: C } = o();
17
+ return h(), a({
18
+ mutationKey: v,
19
19
  mutationFn: async (i) => {
20
- let { query: a, conversationInstructions: o, plugins: d, repository: f, workingDir: m, workspaceMode: v, parentConversationId: S, agentType: C, agentProfileId: w } = i, T;
20
+ let { query: a, conversationInstructions: o, plugins: f, repository: p, workingDir: h, workspaceMode: v, parentConversationId: b, agentType: w, agentProfileId: T } = i, E;
21
21
  try {
22
- T = await _.ensureQueryData({
22
+ E = await y.ensureQueryData({
23
23
  queryKey: [
24
24
  ...e.all,
25
- b.id,
26
- x
25
+ S.id,
26
+ C
27
27
  ],
28
- queryFn: p.listProfiles,
28
+ queryFn: m.listProfiles,
29
29
  ...t
30
30
  });
31
31
  } catch {}
32
- let E = w ?? T?.active_agent_profile_id ?? void 0, D = E ? T?.profiles?.find((e) => e.id === E) : void 0, O = b.kind === "cloud", k = E;
33
- if (!O && D?.name === "default" && D?.agent_kind === "openhands") k = void 0;
34
- else if (D?.agent_kind === "openhands" && D.llm_profile_ref) {
32
+ let D = T ?? E?.active_agent_profile_id ?? void 0, O = D ? E?.profiles?.find((e) => e.id === D) : void 0, k = S.kind === "cloud", A = D;
33
+ if (!k && O?.name === "default" && O?.agent_kind === "openhands") A = void 0;
34
+ else if (O?.agent_kind === "openhands" && O.llm_profile_ref) {
35
35
  let e = !1;
36
36
  try {
37
- e = (await _.ensureQueryData({
37
+ e = (await y.ensureQueryData({
38
38
  queryKey: [
39
39
  ...n.all,
40
- b.id,
41
- x
40
+ S.id,
41
+ C
42
42
  ],
43
43
  queryFn: s.listProfiles,
44
44
  retry: !1
45
- })).profiles.some((e) => e.name === D.llm_profile_ref);
45
+ })).profiles.some((e) => e.name === O.llm_profile_ref);
46
46
  } catch {}
47
- e || (console.warn(`Agent profile "${D.name}" references missing LLM profile "${D.llm_profile_ref}"; launching from agent_settings instead.`), k = void 0);
47
+ e || (console.warn(`Agent profile "${O.name}" references missing LLM profile "${O.llm_profile_ref}"; launching from agent_settings instead.`), A = void 0);
48
48
  }
49
- let A = k ? [void 0, k] : [], j = await u.createConversation(a, o, d, f ? {
50
- selected_repository: f.name,
51
- selected_branch: f.branch ?? null,
52
- git_provider: f.gitProvider
53
- } : null, m, v, S, C, ...A), M = j.app_conversation_id, N = d?.map((e) => ({
54
- source: e.source,
55
- ref: e.ref ?? null,
56
- repo_path: e.repo_path ?? null
57
- })) ?? [], P = N;
58
- if (M) {
49
+ let j = A ? [
50
+ void 0,
51
+ A,
52
+ O?.agent_kind
53
+ ] : [], M = await d.createConversation(a, o, f, p ? {
54
+ selected_repository: p.name,
55
+ selected_branch: p.branch ?? null,
56
+ git_provider: p.gitProvider
57
+ } : null, h, v, b, w, ...j), N = M.app_conversation_id, P = f?.map(u) ?? [], F = P;
58
+ if (N) {
59
59
  let e = [];
60
60
  try {
61
- e = await _.ensureQueryData({
61
+ e = await y.ensureQueryData({
62
62
  queryKey: r.installed,
63
- queryFn: () => h.listInstalledPlugins()
63
+ queryFn: () => g.listInstalledPlugins()
64
64
  });
65
65
  } catch {}
66
- let t = new Set(N.map(g)), n = e.filter((e) => e.enabled).map((e) => ({
66
+ let t = new Set(P.map(_)), n = e.filter((e) => e.enabled).map((e) => ({
67
67
  source: e.source,
68
68
  ref: e.resolved_ref ?? null,
69
69
  repo_path: e.repo_path ?? null,
70
70
  name: e.name
71
- })).filter((e) => !t.has(g(e)));
72
- P = [...N, ...n];
71
+ })).filter((e) => !t.has(_(e)));
72
+ F = [...P, ...n];
73
73
  }
74
- let F = k && D?.agent_kind === "openhands" ? D.llm_profile_ref : y?.active_profile ?? null;
75
- if (M && (F || P.length)) {
76
- let e = c(M);
77
- l(M, {
74
+ let I = A && O?.agent_kind === "openhands" ? O.llm_profile_ref : x?.active_profile ?? null;
75
+ if (N && (I || F.length)) {
76
+ let e = c(N);
77
+ l(N, {
78
78
  selected_repository: e?.selected_repository ?? null,
79
79
  selected_branch: e?.selected_branch ?? null,
80
80
  git_provider: e?.git_provider ?? null,
81
81
  selected_workspace: e?.selected_workspace ?? null,
82
82
  workspace_mode: e?.workspace_mode ?? null,
83
- active_profile: F ?? e?.active_profile ?? null,
84
- plugins: P.length ? P : e?.plugins ?? null
83
+ active_profile: I ?? e?.active_profile ?? null,
84
+ plugins: F.length ? F : e?.plugins ?? null
85
85
  });
86
86
  }
87
87
  return {
88
- conversation_id: j.app_conversation_id ? j.app_conversation_id : `task-${j.id}`,
88
+ conversation_id: M.app_conversation_id ? M.app_conversation_id : `task-${M.id}`,
89
89
  session_api_key: null,
90
- url: j.agent_server_url,
91
- task_id: j.id
90
+ url: M.agent_server_url,
91
+ task_id: M.id
92
92
  };
93
93
  },
94
94
  onSuccess: async (e, t) => {
95
- v({
95
+ b({
96
96
  conversationId: e.conversation_id,
97
97
  taskId: e.task_id,
98
98
  hasRepository: !!t.repository,
@@ -103,11 +103,11 @@ var _ = () => {
103
103
  agentType: t.agentType,
104
104
  hasParentConversation: !!t.parentConversationId,
105
105
  entryPoint: t.entryPoint
106
- }), _.invalidateQueries({ queryKey: ["user", "conversations"] }), _.invalidateQueries({ queryKey: ["start-tasks"] });
106
+ }), y.invalidateQueries({ queryKey: ["user", "conversations"] }), y.invalidateQueries({ queryKey: ["start-tasks"] });
107
107
  }
108
108
  });
109
109
  };
110
110
  //#endregion
111
- export { _ as useCreateConversation };
111
+ export { v as CREATE_CONVERSATION_MUTATION_KEY, y as useCreateConversation };
112
112
 
113
113
  //# sourceMappingURL=use-create-conversation.js.map