@openhands/agent-canvas 1.5.1 → 1.6.1

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 (720) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{Trans-DqF_OEZB.js → Trans-52HCczKK.js} +1 -1
  4. package/build/assets/{acp-providers-DerAmTyY.js → acp-providers-xACMK9ZP.js} +1 -1
  5. package/build/assets/{active-backend-context-B8rMv0X9.js → active-backend-context-Cd1qcZsb.js} +1 -1
  6. package/build/assets/{add-backend-modal-CxC7BNEc.js → add-backend-modal-5chsfln3.js} +1 -1
  7. package/build/assets/{agent-profiles-settings-BJb8YXR1.js → agent-profiles-settings-BWK4H3kR.js} +1 -1
  8. package/build/assets/agent-server-compatibility-DMY0CihN.js +1 -0
  9. package/build/assets/agent-server-conversation-service.api-CI5-xmIX.js +41 -0
  10. package/build/assets/agent-settings-CMd8-O4f.js +2 -0
  11. package/build/assets/agent-settings-CdrhQJXX.js +1 -0
  12. package/build/assets/{alert-banner-Dh51WURQ.js → alert-banner-CLfWuIAX.js} +1 -1
  13. package/build/assets/api-key-entry-screen-Cc3aC7DZ.js +1 -0
  14. package/build/assets/app-settings-B-DQcBoB.js +1 -0
  15. package/build/assets/{automation-detail-CZsu2CoM.js → automation-detail-BgxBIpyK.js} +2 -2
  16. package/build/assets/automation-export-Dbur8OHa.js +2 -0
  17. package/build/assets/automations-list-BWfjizlp.js +1 -0
  18. package/build/assets/{back-nav-button-DVknCcSP.js → back-nav-button-CaKvsD1E.js} +1 -1
  19. package/build/assets/backend-form-modal-YcfpuBZo.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-BSfN0AZA.js → backend-synced-settings-badge-BellMmgC.js} +1 -1
  21. package/build/assets/{base-modal-CIF_785P.js → base-modal-Cw7enAHz.js} +1 -1
  22. package/build/assets/{brand-button-B1izLAdR.js → brand-button-BulEFHr3.js} +1 -1
  23. package/build/assets/{browser-DKaeq_sT.js → browser-DfVgUREb.js} +2 -2
  24. package/build/assets/browser-store-CBqI3tNR.js +1 -0
  25. package/build/assets/{browser-tab-qb_ftCxW.js → browser-tab-CmIKQB7S.js} +1 -1
  26. package/build/assets/chat-send-button-BchSSwuD.js +1 -0
  27. package/build/assets/circle-check-BXbQ5nEH.js +1 -0
  28. package/build/assets/{circle-plus-check-toggle-DoFIjVE0.js → circle-plus-check-toggle-BhpPjkh9.js} +1 -1
  29. package/build/assets/client-source-DAUgyJsi.js +1 -0
  30. package/build/assets/cloud-funnel-analytics-B5OeWh4y.js +1 -0
  31. package/build/assets/{color-themes-CHizm1xF.js → color-themes-QKMSVfDU.js} +1 -1
  32. package/build/assets/{combobox-caret-CxZpqoSR.js → combobox-caret-kNZtp53R.js} +1 -1
  33. package/build/assets/{command-menu-oTvKDU3Y.js → command-menu-tUsn23zy.js} +1 -1
  34. package/build/assets/{command-store-D8J5LMW1.js → command-store-B0hrIWMN.js} +1 -1
  35. package/build/assets/{condenser-settings-BGlWn93P.js → condenser-settings-BBizzfIH.js} +1 -1
  36. package/build/assets/{confirmation-modal-DGBWM2Ls.js → confirmation-modal-108xng7X.js} +1 -1
  37. package/build/assets/{context-menu-list-item-6psNyl7X.js → context-menu-list-item-BbKfFuN1.js} +1 -1
  38. package/build/assets/conversation-CTzSrLMq.js +1 -0
  39. package/build/assets/conversation-CgcXNb-Z.js +90 -0
  40. package/build/assets/conversation-panel-bscjGQda.js +1 -0
  41. package/build/assets/{conversation-service.api-_f9GlIEi.js → conversation-service.api-DbBvscJA.js} +1 -1
  42. package/build/assets/conversation-state-store-C5GagZHT.js +1 -0
  43. package/build/assets/{conversation-store-LptbOvKa.js → conversation-store-CyVqoa0d.js} +1 -1
  44. package/build/assets/{conversation-tab-empty-state-CNUmIms2.js → conversation-tab-empty-state-2KYlk5Bh.js} +1 -1
  45. package/build/assets/{copy-to-clipboard-button-CFzH_NOX.js → copy-to-clipboard-button-C54YRPMg.js} +1 -1
  46. package/build/assets/declaration-_yumYLEx.js +1 -0
  47. package/build/assets/{device-verify-WBSjJBLH.js → device-verify-D6FW_KZq.js} +1 -1
  48. package/build/assets/{dropdown-classes-D3wMJlXT.js → dropdown-classes-9uONlOg1.js} +1 -1
  49. package/build/assets/{ellipsis-button-CbNnHlIq.js → ellipsis-button-CoaUrKmQ.js} +1 -1
  50. package/build/assets/{entry.client-DNlPGxsu.js → entry.client-BJSVzw7V.js} +2 -2
  51. package/build/assets/{enum-filter-dropdown-JLYv1t8L.js → enum-filter-dropdown-qQzyjk_q.js} +1 -1
  52. package/build/assets/{environment-switch-overlay-B1PFO8P8.js → environment-switch-overlay-XRWCClLu.js} +1 -1
  53. package/build/assets/extensions-hub-DUp9sWQY.js +1 -0
  54. package/build/assets/extensions-navigation-DmkftjAx.js +1 -0
  55. package/build/assets/file-tUAzhmJ3.js +1 -0
  56. package/build/assets/files-tab-GV5hwiIM.js +1 -0
  57. package/build/assets/files-tab-store-DehyITNC.js +1 -0
  58. package/build/assets/{form-control-classes-CbdN5aO3.js → form-control-classes-BPUnoYyg.js} +1 -1
  59. package/build/assets/git-provider-icon-DDkru3b_.js +1 -0
  60. package/build/assets/highlighted-source-view-CYkPZUxr.js +1 -0
  61. package/build/assets/home-C53WekZ2.js +1 -0
  62. package/build/assets/home-EAwq8ah2.js +1 -0
  63. package/build/assets/index-home-kBdZGKtu.js +1 -0
  64. package/build/assets/install-server-modal-DYk4VP3-.js +1 -0
  65. package/build/assets/{launch-CAGavYl-.js → launch-CIecOy_y.js} +1 -1
  66. package/build/assets/lesson-plan-BSEUwflx.js +4 -0
  67. package/build/assets/llm-not-configured-banner-C8uv1PHT.js +27 -0
  68. package/build/assets/llm-settings-0Bb97NCk.js +1 -0
  69. package/build/assets/llm-settings-D4toJ90k.js +1 -0
  70. package/build/assets/{llm-subscription-service-Casyzgn1.js → llm-subscription-service-BPRw3u3H.js} +1 -1
  71. package/build/assets/{loading-spinner-BcdnT0mN.js → loading-spinner-BCInDEXB.js} +1 -1
  72. package/build/assets/manage-backends-modal-Bon32JYr.js +1 -0
  73. package/build/assets/manifest-ec000ae3.js +1 -0
  74. package/build/assets/markdown-renderer-BlaJOAI2.js +2 -0
  75. package/build/assets/mcp-CFIpOkn2.js +9 -0
  76. package/build/assets/messages-eRPeR4pW.js +40 -0
  77. package/build/assets/{modal-body-Cc2zp0xb.js → modal-body-Bg9UCeoV.js} +1 -1
  78. package/build/assets/{modal-classes-3qT9Ap7Q.js → modal-classes-H1KbJkeC.js} +1 -1
  79. package/build/assets/{modal-close-button-BcIBUdSq.js → modal-close-button-BMLYvo0C.js} +1 -1
  80. package/build/assets/{model-selector-DJrMrFAr.js → model-selector-BqX2RvEr.js} +1 -1
  81. package/build/assets/{navigation-link-D8hfwvxI.js → navigation-link-DoJzTCEz.js} +1 -1
  82. package/build/assets/onboarding-3KO4qE6R.js +1 -0
  83. package/build/assets/onboarding-modal-D4vqkJeT.js +1 -0
  84. package/build/assets/{organization-service.api-vrRNwOma.js → organization-service.api-CvfQugOt.js} +1 -1
  85. package/build/assets/{path-utils-PNfCKQDV.js → path-utils-DRgexxnX.js} +1 -1
  86. package/build/assets/{plan-components-BdZXPyZU.js → plan-components-qkMiGs_-.js} +1 -1
  87. package/build/assets/{planner-tab-ByOw1he8.js → planner-tab-ZrjiirRr.js} +1 -1
  88. package/build/assets/{plugins-management-service-B75jyC56.js → plugins-management-service-9qT-r_37.js} +1 -1
  89. package/build/assets/plus-CzE1b-wl.js +1 -0
  90. package/build/assets/{profiles-service.api-BQTklD5D.js → profiles-service.api-CRVlrQpT.js} +1 -1
  91. package/build/assets/{providers-DfTB2gPd.js → providers-C2DFGBrf.js} +1 -1
  92. package/build/assets/{proxy-B3tCGf0X.js → proxy-DnnDqkwn.js} +1 -1
  93. package/build/assets/puzzle-IHQnD54C.js +1 -0
  94. package/build/assets/recommended-automations-launcher-B97vgHuN.js +58 -0
  95. package/build/assets/refresh-cw-DxbAs3SK.js +1 -0
  96. package/build/assets/root-BbONavTg.css +1 -0
  97. package/build/assets/root-D01rpqZU.js +2 -0
  98. package/build/assets/root-layout-BiHbCIkS.js +2 -0
  99. package/build/assets/{runtime-waiting-state-CnyVhX4T.js → runtime-waiting-state-CbUV9txX.js} +1 -1
  100. package/build/assets/{schema-field-CUwk4wrQ.js → schema-field-B9-9Ta9u.js} +1 -1
  101. package/build/assets/sdk-section-page-DG1s25Pr.js +1 -0
  102. package/build/assets/{sdk-settings-schema-CLR01YIK.js → sdk-settings-schema-B42JFr5Y.js} +1 -1
  103. package/build/assets/{secrets-service-CvyPWJhg.js → secrets-service-DvyS6-mV.js} +1 -1
  104. package/build/assets/secrets-settings-CeSq6cQw.js +1 -0
  105. package/build/assets/{segmented-toggle-kzwZkLaj.js → segmented-toggle-DRVJBNy0.js} +1 -1
  106. package/build/assets/settings-Dzo0dJKM.js +1 -0
  107. package/build/assets/{settings-mQUckT0g.js → settings-HcwfqMjJ.js} +1 -1
  108. package/build/assets/{settings-dropdown-input-Dhx9ErGd.js → settings-dropdown-input-B6RL_mt0.js} +1 -1
  109. package/build/assets/settings-index-uAKHyijw.js +1 -0
  110. package/build/assets/{settings-input-BsgQWS5R.js → settings-input-ZoaAPNQh.js} +1 -1
  111. package/build/assets/{settings-list-classes-DSJJX99U.js → settings-list-classes-Dr8LgITi.js} +1 -1
  112. package/build/assets/settings-modal-BVX2G_IT.js +1 -0
  113. package/build/assets/{settings-service.api-DXk5TdeJ.js → settings-service.api-BY7ncxIz.js} +1 -1
  114. package/build/assets/{settings-switch-Bes8dIlV.js → settings-switch-DUlGnP0B.js} +1 -1
  115. package/build/assets/{settings-utils-DzCVfiR3.js → settings-utils-kWEEvcdv.js} +1 -1
  116. package/build/assets/shared-conversation-CV4Rtdz2.js +1 -0
  117. package/build/assets/{sidebar-layout-Bm7pa3Vk.js → sidebar-layout-BhGooCga.js} +1 -1
  118. package/build/assets/sidebar-mobile-menu-toggle-BntOJphE.js +1 -0
  119. package/build/assets/{sidebar-nav-link-CHqyFXwV.js → sidebar-nav-link-DkA1XWV8.js} +1 -1
  120. package/build/assets/{sidebar-store-uiUXBU1N.js → sidebar-store-CF3b12-m.js} +1 -1
  121. package/build/assets/{skill-card-pill-row-BPYKQ3IN.js → skill-card-pill-row-_gWe1Tws.js} +1 -1
  122. package/build/assets/skill-icon-badge-C-zbaXbD.js +1 -0
  123. package/build/assets/skills-plugins-BwoXKtrR.js +1 -0
  124. package/build/assets/skills-settings-Crq-fZSt.js +2 -0
  125. package/build/assets/{styled-tooltip-B14i_Uac.js → styled-tooltip-CdnCJ67u.js} +1 -1
  126. package/build/assets/syntax-highlighter-CZK2ab-O.js +1 -0
  127. package/build/assets/{task-list-tab-DyfgG4MC.js → task-list-tab-CONOO9iI.js} +1 -1
  128. package/build/assets/telemetry-CC32dJYW.js +2 -0
  129. package/build/assets/{terminal-DRXt2Lr4.js → terminal-CTkDpIU-.js} +1 -1
  130. package/build/assets/{toggle-switch-DX_k418f.js → toggle-switch-yVIzmJLM.js} +1 -1
  131. package/build/assets/{typography-CzfoD9DY.js → typography-BBVEW0un.js} +1 -1
  132. package/build/assets/{use-acp-credential-form-Bm8qXdlc.js → use-acp-credential-form-CIsp8hJe.js} +1 -1
  133. package/build/assets/use-activate-agent-profile-BfcTTJJG.js +1 -0
  134. package/build/assets/{use-active-agent-profile-hI-x-UBa.js → use-active-agent-profile-CCeFtmk9.js} +1 -1
  135. package/build/assets/{use-active-conversation-2qeez40F.js → use-active-conversation-Badxj8w4.js} +1 -1
  136. package/build/assets/{use-agent-profiles-0pr5bLS7.js → use-agent-profiles-BSWVN6hH.js} +1 -1
  137. package/build/assets/{use-agent-state-C6peDXUm.js → use-agent-state-BzRrhoPd.js} +1 -1
  138. package/build/assets/use-backends-health-BsZikyFW.js +1 -0
  139. package/build/assets/{use-can-manage-org-profiles-Ck_bsNbH.js → use-can-manage-org-profiles-Do6mYf6P.js} +1 -1
  140. package/build/assets/{use-cloud-current-user-id-DUCSxNjY.js → use-cloud-current-user-id-Cx3B_0-w.js} +1 -1
  141. package/build/assets/use-config-D9zUVw5b.js +1 -0
  142. package/build/assets/{use-create-conversation-r1JHfKO5.js → use-create-conversation-vvl0kvu7.js} +1 -1
  143. package/build/assets/{use-create-secret-Cf2N11d2.js → use-create-secret-CB7Dvd4s.js} +1 -1
  144. package/build/assets/use-event-store-BlpSUF7N.js +4 -0
  145. package/build/assets/{use-handle-plan-click-CsAW3FlH.js → use-handle-plan-click-DDZkew9p.js} +1 -1
  146. package/build/assets/use-is-creating-conversation-fxSzN778.js +1 -0
  147. package/build/assets/{use-launch-skill-in-chat-DJWEfVc5.js → use-launch-skill-in-chat-D6_n1fs3.js} +1 -1
  148. package/build/assets/use-llm-profiles-CNQm7HYS.js +1 -0
  149. package/build/assets/use-plugins-marketplace-CloojomS.js +1 -0
  150. package/build/assets/use-runtime-is-ready-Bx_4cfar.js +1 -0
  151. package/build/assets/{use-save-agent-profile-RqUGqTv6.js → use-save-agent-profile-Bj7N-1jS.js} +1 -1
  152. package/build/assets/{use-save-settings-Chvzeu2s.js → use-save-settings-BMsb2wk_.js} +1 -1
  153. package/build/assets/use-settings-B9XEmh7i.js +1 -0
  154. package/build/assets/use-settings-nav-items-BGfBS0yL.js +16 -0
  155. package/build/assets/{use-skills-xrVoKYrF.js → use-skills-CSRbvPle.js} +1 -1
  156. package/build/assets/{use-task-list-CfisapBW.js → use-task-list-CTIVLrQA.js} +1 -1
  157. package/build/assets/use-tracking-DjdCssvp.js +1 -0
  158. package/build/assets/{use-user-conversation-CnE4zhOA.js → use-user-conversation-BCpFn-Ei.js} +1 -1
  159. package/build/assets/{useTranslation-Cbf5AGg_.js → useTranslation-CHm_6BRr.js} +1 -1
  160. package/build/assets/{utils-DB9LsAzA.js → utils-CrRpzbM2.js} +1 -1
  161. package/build/assets/vendor-markdown-DzO4uO_0.js +37 -0
  162. package/build/assets/{vendor~browser-wXQ-WZEC.js → vendor~browser-Byfuq5J-.js} +1 -1
  163. package/build/assets/{vendor~browser-tab-CsMb-pta.js → vendor~browser-tab-BLk3qxhw.js} +1 -1
  164. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dhilvkxv-eGLBu60s.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dhilvkxv-DTJ_cjR5.js} +2 -2
  165. package/build/assets/{vendor~files-tab-B-5fBohe.js → vendor~files-tab-XmnauIZI.js} +1 -1
  166. package/build/assets/{vendor~launch-_Km3J77E.js → vendor~launch-DeC8QhXQ.js} +1 -1
  167. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-BrguKXCn.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DDmLzCzm.js} +1 -1
  168. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation-YrfJYTQw.js +1 -0
  169. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~launch~skills-settings~a~j2jlu22h-mC0v44WR.js → vendor~root-layout~index-home~home~conversation-panel~conversation~launch~skills-settings~a~j2jlu22h-DoaS51iW.js} +123 -3
  170. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation~aler~gfu7qdtz-BOKQkbc6.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation~aler~gfu7qdtz-DEYwoy8u.js} +1 -1
  171. package/build/assets/vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-DZHLw5Sr.js +1 -0
  172. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~ftka0fnu-cFNmfAE6.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~ftka0fnu-UJ4BNDJP.js} +1 -1
  173. package/build/assets/{verification-settings-CfkrXoSa.js → verification-settings-uwOfc16F.js} +1 -1
  174. package/build/index.html +4 -4
  175. package/build/locales/ar/openhands.json +38 -4
  176. package/build/locales/ca/openhands.json +38 -4
  177. package/build/locales/de/openhands.json +38 -4
  178. package/build/locales/en/openhands.json +38 -4
  179. package/build/locales/es/openhands.json +38 -4
  180. package/build/locales/fr/openhands.json +38 -4
  181. package/build/locales/it/openhands.json +38 -4
  182. package/build/locales/ja/openhands.json +38 -4
  183. package/build/locales/ko-KR/openhands.json +38 -4
  184. package/build/locales/no/openhands.json +38 -4
  185. package/build/locales/pt/openhands.json +38 -4
  186. package/build/locales/tr/openhands.json +38 -4
  187. package/build/locales/uk/openhands.json +38 -4
  188. package/build/locales/zh-CN/openhands.json +38 -4
  189. package/build/locales/zh-TW/openhands.json +38 -4
  190. package/config/defaults.json +7 -3
  191. package/dist/api/agent-canvas-updates.cjs +2 -0
  192. package/dist/api/agent-canvas-updates.cjs.map +1 -0
  193. package/dist/api/agent-canvas-updates.d.ts +7 -0
  194. package/dist/api/agent-canvas-updates.js +19 -0
  195. package/dist/api/agent-canvas-updates.js.map +1 -0
  196. package/dist/api/agent-server-adapter.cjs +1 -1
  197. package/dist/api/agent-server-adapter.cjs.map +1 -1
  198. package/dist/api/agent-server-adapter.d.ts +3 -0
  199. package/dist/api/agent-server-adapter.js +1 -0
  200. package/dist/api/agent-server-adapter.js.map +1 -1
  201. package/dist/api/agent-server-compatibility.cjs +1 -1
  202. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  203. package/dist/api/agent-server-compatibility.d.ts +5 -0
  204. package/dist/api/agent-server-compatibility.js +24 -15
  205. package/dist/api/agent-server-compatibility.js.map +1 -1
  206. package/dist/api/agent-server-config.cjs +1 -1
  207. package/dist/api/agent-server-config.cjs.map +1 -1
  208. package/dist/api/agent-server-config.js +11 -8
  209. package/dist/api/agent-server-config.js.map +1 -1
  210. package/dist/api/automation-service/automation-service.api.cjs +2 -0
  211. package/dist/api/automation-service/automation-service.api.cjs.map +1 -0
  212. package/dist/api/automation-service/automation-service.api.d.ts +3 -0
  213. package/dist/api/automation-service/automation-service.api.js +283 -0
  214. package/dist/api/automation-service/automation-service.api.js.map +1 -0
  215. package/dist/api/client-source.cjs +1 -1
  216. package/dist/api/client-source.cjs.map +1 -1
  217. package/dist/api/client-source.d.ts +3 -0
  218. package/dist/api/client-source.js +4 -4
  219. package/dist/api/client-source.js.map +1 -1
  220. package/dist/api/cloud/settings-service.api.cjs.map +1 -1
  221. package/dist/api/cloud/settings-service.api.js.map +1 -1
  222. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  223. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  224. package/dist/api/conversation-service/agent-server-conversation-service.api.js +111 -109
  225. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  226. package/dist/api/mcp-health/mcp-health-store.cjs +2 -0
  227. package/dist/api/mcp-health/mcp-health-store.cjs.map +1 -0
  228. package/dist/api/mcp-health/mcp-health-store.d.ts +22 -0
  229. package/dist/api/mcp-health/mcp-health-store.js +45 -0
  230. package/dist/api/mcp-health/mcp-health-store.js.map +1 -0
  231. package/dist/api/mcp-health/probe-mcp-server-health.cjs +2 -0
  232. package/dist/api/mcp-health/probe-mcp-server-health.cjs.map +1 -0
  233. package/dist/api/mcp-health/probe-mcp-server-health.d.ts +31 -0
  234. package/dist/api/mcp-health/probe-mcp-server-health.js +57 -0
  235. package/dist/api/mcp-health/probe-mcp-server-health.js.map +1 -0
  236. package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
  237. package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
  238. package/dist/api/mcp-service/mcp-service.api.js +66 -46
  239. package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
  240. package/dist/api/plugins-management-service.cjs.map +1 -1
  241. package/dist/api/plugins-management-service.d.ts +6 -1
  242. package/dist/api/plugins-management-service.js.map +1 -1
  243. package/dist/api/plugins-service.d.ts +30 -2
  244. package/dist/api/secrets-service.cjs +1 -1
  245. package/dist/api/secrets-service.cjs.map +1 -1
  246. package/dist/api/secrets-service.d.ts +6 -6
  247. package/dist/api/secrets-service.js +8 -3
  248. package/dist/api/secrets-service.js.map +1 -1
  249. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  250. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  251. package/dist/api/settings-service/settings-service.api.d.ts +1 -1
  252. package/dist/api/settings-service/settings-service.api.js +1 -0
  253. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  254. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.cjs +1 -1
  255. package/dist/components/conversation-events/chat/event-message-components/critic-result-display.js +5 -5
  256. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  257. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  258. package/dist/components/features/backends/backend-form-modal.d.ts +4 -1
  259. package/dist/components/features/backends/backend-form-modal.js +15 -16
  260. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  261. package/dist/components/features/backends/backend-selector.cjs +1 -1
  262. package/dist/components/features/backends/backend-selector.cjs.map +1 -1
  263. package/dist/components/features/backends/backend-selector.js +103 -102
  264. package/dist/components/features/backends/backend-selector.js.map +1 -1
  265. package/dist/components/features/backends/manage-backends-modal.cjs +1 -1
  266. package/dist/components/features/backends/manage-backends-modal.cjs.map +1 -1
  267. package/dist/components/features/backends/manage-backends-modal.js +85 -73
  268. package/dist/components/features/backends/manage-backends-modal.js.map +1 -1
  269. package/dist/components/features/chat/chat-interface.cjs +2 -2
  270. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  271. package/dist/components/features/chat/chat-interface.js +131 -129
  272. package/dist/components/features/chat/chat-interface.js.map +1 -1
  273. package/dist/components/features/chat/confirmation-mode-enabled.cjs +1 -1
  274. package/dist/components/features/chat/confirmation-mode-enabled.js +4 -4
  275. package/dist/components/features/chat/skill-install-restart-banner.cjs +2 -0
  276. package/dist/components/features/chat/skill-install-restart-banner.cjs.map +1 -0
  277. package/dist/components/features/chat/skill-install-restart-banner.d.ts +11 -0
  278. package/dist/components/features/chat/skill-install-restart-banner.js +63 -0
  279. package/dist/components/features/chat/skill-install-restart-banner.js.map +1 -0
  280. package/dist/components/features/conversation/right-panel-toggle.cjs +1 -1
  281. package/dist/components/features/conversation/right-panel-toggle.cjs.map +1 -1
  282. package/dist/components/features/conversation/right-panel-toggle.js +1 -1
  283. package/dist/components/features/conversation/right-panel-toggle.js.map +1 -1
  284. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  285. package/dist/components/features/conversation-panel/conversation-panel.js +10 -10
  286. package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
  287. package/dist/components/features/mcp-page/custom-server-editor.cjs.map +1 -1
  288. package/dist/components/features/mcp-page/custom-server-editor.js +78 -77
  289. package/dist/components/features/mcp-page/custom-server-editor.js.map +1 -1
  290. package/dist/components/features/mcp-page/install-server-modal.cjs +1 -1
  291. package/dist/components/features/mcp-page/install-server-modal.cjs.map +1 -1
  292. package/dist/components/features/mcp-page/install-server-modal.d.ts +4 -1
  293. package/dist/components/features/mcp-page/install-server-modal.js +135 -136
  294. package/dist/components/features/mcp-page/install-server-modal.js.map +1 -1
  295. package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
  296. package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
  297. package/dist/components/features/mcp-page/installed-server-card.js +40 -34
  298. package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
  299. package/dist/components/features/mcp-page/mcp-server-health.cjs +2 -0
  300. package/dist/components/features/mcp-page/mcp-server-health.cjs.map +1 -0
  301. package/dist/components/features/mcp-page/mcp-server-health.d.ts +18 -0
  302. package/dist/components/features/mcp-page/mcp-server-health.js +117 -0
  303. package/dist/components/features/mcp-page/mcp-server-health.js.map +1 -0
  304. package/dist/components/features/plugins/build-plugins-view-model.d.ts +10 -1
  305. package/dist/components/features/plugins/plugin-files-section.d.ts +13 -0
  306. package/dist/components/features/settings/agent-canvas-update-card.cjs +2 -0
  307. package/dist/components/features/settings/agent-canvas-update-card.cjs.map +1 -0
  308. package/dist/components/features/settings/agent-canvas-update-card.d.ts +7 -0
  309. package/dist/components/features/settings/agent-canvas-update-card.js +170 -0
  310. package/dist/components/features/settings/agent-canvas-update-card.js.map +1 -0
  311. package/dist/components/features/settings/integrations-settings-link.cjs +2 -0
  312. package/dist/components/features/settings/integrations-settings-link.cjs.map +1 -0
  313. package/dist/components/features/settings/integrations-settings-link.d.ts +5 -0
  314. package/dist/components/features/settings/integrations-settings-link.js +41 -0
  315. package/dist/components/features/settings/integrations-settings-link.js.map +1 -0
  316. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.cjs +1 -1
  317. package/dist/components/features/settings/llm-profiles/llm-settings-local-view.js +1 -1
  318. package/dist/components/features/settings/settings-desktop-sidebar.cjs +1 -1
  319. package/dist/components/features/settings/settings-desktop-sidebar.cjs.map +1 -1
  320. package/dist/components/features/settings/settings-desktop-sidebar.js +31 -21
  321. package/dist/components/features/settings/settings-desktop-sidebar.js.map +1 -1
  322. package/dist/components/features/settings/settings-mobile-drawer.cjs +1 -1
  323. package/dist/components/features/settings/settings-mobile-drawer.cjs.map +1 -1
  324. package/dist/components/features/settings/settings-mobile-drawer.js +40 -30
  325. package/dist/components/features/settings/settings-mobile-drawer.js.map +1 -1
  326. package/dist/components/features/skills/extensions-navigation.cjs +1 -1
  327. package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
  328. package/dist/components/features/skills/extensions-navigation.js +54 -33
  329. package/dist/components/features/skills/extensions-navigation.js.map +1 -1
  330. package/dist/config/defaults.cjs +1 -1
  331. package/dist/config/defaults.cjs.map +1 -1
  332. package/dist/config/defaults.js +7 -3
  333. package/dist/config/defaults.js.map +1 -1
  334. package/dist/constants/acp-providers.cjs.map +1 -1
  335. package/dist/constants/acp-providers.js.map +1 -1
  336. package/dist/contexts/conversation-websocket-context.cjs +2 -2
  337. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  338. package/dist/contexts/conversation-websocket-context.js +169 -166
  339. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  340. package/dist/hooks/mutation/use-delete-llm-profile.cjs +1 -1
  341. package/dist/hooks/mutation/use-delete-llm-profile.js +1 -0
  342. package/dist/hooks/mutation/use-delete-mcp-server.cjs +1 -1
  343. package/dist/hooks/mutation/use-delete-mcp-server.cjs.map +1 -1
  344. package/dist/hooks/mutation/use-delete-mcp-server.js +8 -6
  345. package/dist/hooks/mutation/use-delete-mcp-server.js.map +1 -1
  346. package/dist/hooks/mutation/use-rename-llm-profile.cjs +1 -1
  347. package/dist/hooks/mutation/use-rename-llm-profile.js +1 -0
  348. package/dist/hooks/mutation/use-save-settings.cjs +1 -1
  349. package/dist/hooks/mutation/use-save-settings.js +3 -3
  350. package/dist/hooks/mutation/use-update-mcp-server.cjs +1 -1
  351. package/dist/hooks/mutation/use-update-mcp-server.cjs.map +1 -1
  352. package/dist/hooks/mutation/use-update-mcp-server.js +11 -9
  353. package/dist/hooks/mutation/use-update-mcp-server.js.map +1 -1
  354. package/dist/hooks/query/query-keys.cjs +1 -1
  355. package/dist/hooks/query/query-keys.cjs.map +1 -1
  356. package/dist/hooks/query/query-keys.d.ts +4 -0
  357. package/dist/hooks/query/query-keys.js +2 -2
  358. package/dist/hooks/query/query-keys.js.map +1 -1
  359. package/dist/hooks/query/use-automation-sdk-version.cjs +2 -0
  360. package/dist/hooks/query/use-automation-sdk-version.cjs.map +1 -0
  361. package/dist/hooks/query/use-automation-sdk-version.d.ts +3 -0
  362. package/dist/hooks/query/use-automation-sdk-version.js +63 -0
  363. package/dist/hooks/query/use-automation-sdk-version.js.map +1 -0
  364. package/dist/hooks/query/use-latest-agent-canvas-version.cjs +2 -0
  365. package/dist/hooks/query/use-latest-agent-canvas-version.cjs.map +1 -0
  366. package/dist/hooks/query/use-latest-agent-canvas-version.d.ts +10 -0
  367. package/dist/hooks/query/use-latest-agent-canvas-version.js +19 -0
  368. package/dist/hooks/query/use-latest-agent-canvas-version.js.map +1 -0
  369. package/dist/hooks/query/use-plugin-file-content.d.ts +7 -0
  370. package/dist/hooks/query/use-resolved-workspaces.cjs +1 -1
  371. package/dist/hooks/query/use-resolved-workspaces.cjs.map +1 -1
  372. package/dist/hooks/query/use-resolved-workspaces.js +25 -26
  373. package/dist/hooks/query/use-resolved-workspaces.js.map +1 -1
  374. package/dist/hooks/query/use-search-subdirs.cjs +1 -1
  375. package/dist/hooks/query/use-search-subdirs.cjs.map +1 -1
  376. package/dist/hooks/query/use-search-subdirs.d.ts +5 -0
  377. package/dist/hooks/query/use-search-subdirs.js +16 -4
  378. package/dist/hooks/query/use-search-subdirs.js.map +1 -1
  379. package/dist/hooks/query/use-task-polling.cjs +1 -1
  380. package/dist/hooks/query/use-task-polling.cjs.map +1 -1
  381. package/dist/hooks/query/use-task-polling.d.ts +19 -3
  382. package/dist/hooks/query/use-task-polling.js +36 -35
  383. package/dist/hooks/query/use-task-polling.js.map +1 -1
  384. package/dist/hooks/use-device-flow.cjs +1 -1
  385. package/dist/hooks/use-device-flow.js +6 -6
  386. package/dist/hooks/use-llm-configured.cjs +1 -1
  387. package/dist/hooks/use-llm-configured.js +3 -3
  388. package/dist/hooks/use-mcp-server-health.cjs +2 -0
  389. package/dist/hooks/use-mcp-server-health.cjs.map +1 -0
  390. package/dist/hooks/use-mcp-server-health.d.ts +11 -0
  391. package/dist/hooks/use-mcp-server-health.js +18 -0
  392. package/dist/hooks/use-mcp-server-health.js.map +1 -0
  393. package/dist/hooks/use-skill-installs.cjs +2 -0
  394. package/dist/hooks/use-skill-installs.cjs.map +1 -0
  395. package/dist/hooks/use-skill-installs.d.ts +10 -0
  396. package/dist/hooks/use-skill-installs.js +21 -0
  397. package/dist/hooks/use-skill-installs.js.map +1 -0
  398. package/dist/hooks/use-tracking.cjs +1 -1
  399. package/dist/hooks/use-tracking.cjs.map +1 -1
  400. package/dist/hooks/use-tracking.d.ts +3 -3
  401. package/dist/hooks/use-tracking.js +67 -52
  402. package/dist/hooks/use-tracking.js.map +1 -1
  403. package/dist/i18n/declaration.cjs +1 -1
  404. package/dist/i18n/declaration.cjs.map +1 -1
  405. package/dist/i18n/declaration.d.ts +35 -1
  406. package/dist/i18n/declaration.js +1 -1
  407. package/dist/i18n/declaration.js.map +1 -1
  408. package/dist/i18n/translation.cjs +3 -3
  409. package/dist/i18n/translation.cjs.map +1 -1
  410. package/dist/i18n/translation.js +623 -45
  411. package/dist/i18n/translation.js.map +1 -1
  412. package/dist/index.cjs +1 -1
  413. package/dist/index.js +1 -2
  414. package/dist/lib/index.cjs +1 -1
  415. package/dist/lib/index.d.ts +0 -1
  416. package/dist/lib/index.js +0 -1
  417. package/dist/locales/ar/openhands.json +38 -4
  418. package/dist/locales/ca/openhands.json +38 -4
  419. package/dist/locales/de/openhands.json +38 -4
  420. package/dist/locales/en/openhands.json +38 -4
  421. package/dist/locales/es/openhands.json +38 -4
  422. package/dist/locales/fr/openhands.json +38 -4
  423. package/dist/locales/it/openhands.json +38 -4
  424. package/dist/locales/ja/openhands.json +38 -4
  425. package/dist/locales/ko-KR/openhands.json +38 -4
  426. package/dist/locales/no/openhands.json +38 -4
  427. package/dist/locales/pt/openhands.json +38 -4
  428. package/dist/locales/tr/openhands.json +38 -4
  429. package/dist/locales/uk/openhands.json +38 -4
  430. package/dist/locales/zh-CN/openhands.json +38 -4
  431. package/dist/locales/zh-TW/openhands.json +38 -4
  432. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian-rovo.cjs +2 -0
  433. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian-rovo.cjs.map +1 -0
  434. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian-rovo.js +44 -0
  435. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian-rovo.js.map +1 -0
  436. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.cjs +1 -1
  437. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.cjs.map +1 -1
  438. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.js +13 -0
  439. package/dist/node_modules/@openhands/extensions/integrations/catalog/atlassian.js.map +1 -1
  440. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs +1 -1
  441. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.cjs.map +1 -1
  442. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js +21 -1
  443. package/dist/node_modules/@openhands/extensions/integrations/catalog/github.js.map +1 -1
  444. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.cjs +1 -1
  445. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.cjs.map +1 -1
  446. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.js +1 -1
  447. package/dist/node_modules/@openhands/extensions/integrations/catalog/linear.js.map +1 -1
  448. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-outlook.cjs +1 -1
  449. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-outlook.cjs.map +1 -1
  450. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-outlook.js +13 -0
  451. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-outlook.js.map +1 -1
  452. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-teams.cjs +1 -1
  453. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-teams.cjs.map +1 -1
  454. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-teams.js +13 -0
  455. package/dist/node_modules/@openhands/extensions/integrations/catalog/microsoft-teams.js.map +1 -1
  456. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs +1 -1
  457. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.cjs.map +1 -1
  458. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js +13 -0
  459. package/dist/node_modules/@openhands/extensions/integrations/catalog/notion.js.map +1 -1
  460. package/dist/node_modules/@openhands/extensions/integrations/catalog/onedrive.cjs +1 -1
  461. package/dist/node_modules/@openhands/extensions/integrations/catalog/onedrive.cjs.map +1 -1
  462. package/dist/node_modules/@openhands/extensions/integrations/catalog/onedrive.js +13 -0
  463. package/dist/node_modules/@openhands/extensions/integrations/catalog/onedrive.js.map +1 -1
  464. package/dist/node_modules/@openhands/extensions/integrations/catalog/sharepoint.cjs +1 -1
  465. package/dist/node_modules/@openhands/extensions/integrations/catalog/sharepoint.cjs.map +1 -1
  466. package/dist/node_modules/@openhands/extensions/integrations/catalog/sharepoint.js +13 -0
  467. package/dist/node_modules/@openhands/extensions/integrations/catalog/sharepoint.js.map +1 -1
  468. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.cjs +1 -1
  469. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.cjs.map +1 -1
  470. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.js +16 -2
  471. package/dist/node_modules/@openhands/extensions/integrations/catalog/slack.js.map +1 -1
  472. package/dist/node_modules/@openhands/extensions/integrations/catalog/vanta.cjs +2 -0
  473. package/dist/node_modules/@openhands/extensions/integrations/catalog/vanta.cjs.map +1 -0
  474. package/dist/node_modules/@openhands/extensions/integrations/catalog/vanta.js +46 -0
  475. package/dist/node_modules/@openhands/extensions/integrations/catalog/vanta.js.map +1 -0
  476. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs +1 -1
  477. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.cjs.map +1 -1
  478. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js +74 -70
  479. package/dist/node_modules/@openhands/extensions/integrations/catalog-index.js.map +1 -1
  480. package/dist/node_modules/@openhands/extensions/skills/index.cjs +123 -3
  481. package/dist/node_modules/@openhands/extensions/skills/index.cjs.map +1 -1
  482. package/dist/node_modules/@openhands/extensions/skills/index.js +7 -1
  483. package/dist/node_modules/@openhands/extensions/skills/index.js.map +1 -1
  484. package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs +2 -0
  485. package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs.map +1 -0
  486. package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js +9 -0
  487. package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js.map +1 -0
  488. package/dist/node_modules/lucide-react/dist/esm/icons/info.cjs +2 -0
  489. package/dist/node_modules/lucide-react/dist/esm/icons/info.cjs.map +1 -0
  490. package/dist/node_modules/lucide-react/dist/esm/icons/info.js +21 -0
  491. package/dist/node_modules/lucide-react/dist/esm/icons/info.js.map +1 -0
  492. package/dist/package.cjs +1 -1
  493. package/dist/package.cjs.map +1 -1
  494. package/dist/package.js +10 -3
  495. package/dist/package.js.map +1 -1
  496. package/dist/routes/app-settings.cjs +1 -1
  497. package/dist/routes/app-settings.cjs.map +1 -1
  498. package/dist/routes/app-settings.js +113 -67
  499. package/dist/routes/app-settings.js.map +1 -1
  500. package/dist/routes/conversation.cjs +1 -1
  501. package/dist/routes/conversation.cjs.map +1 -1
  502. package/dist/routes/conversation.js +1 -1
  503. package/dist/routes/conversation.js.map +1 -1
  504. package/dist/routes/mcp.cjs +1 -1
  505. package/dist/routes/mcp.cjs.map +1 -1
  506. package/dist/routes/mcp.js +1 -0
  507. package/dist/routes/mcp.js.map +1 -1
  508. package/dist/services/cloud-funnel-analytics.cjs +1 -1
  509. package/dist/services/cloud-funnel-analytics.cjs.map +1 -1
  510. package/dist/services/cloud-funnel-analytics.d.ts +2 -2
  511. package/dist/services/cloud-funnel-analytics.js +18 -16
  512. package/dist/services/cloud-funnel-analytics.js.map +1 -1
  513. package/dist/services/settings.cjs +1 -1
  514. package/dist/services/settings.cjs.map +1 -1
  515. package/dist/services/settings.js +1 -0
  516. package/dist/services/settings.js.map +1 -1
  517. package/dist/services/telemetry-context.cjs +2 -0
  518. package/dist/services/telemetry-context.cjs.map +1 -0
  519. package/dist/services/telemetry-context.d.ts +17 -0
  520. package/dist/services/telemetry-context.js +27 -0
  521. package/dist/services/telemetry-context.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.d.ts +7 -5
  525. package/dist/services/telemetry.js +132 -123
  526. package/dist/services/telemetry.js.map +1 -1
  527. package/dist/stores/skill-install-banner-store.cjs +2 -0
  528. package/dist/stores/skill-install-banner-store.cjs.map +1 -0
  529. package/dist/stores/skill-install-banner-store.d.ts +32 -0
  530. package/dist/stores/skill-install-banner-store.js +14 -0
  531. package/dist/stores/skill-install-banner-store.js.map +1 -0
  532. package/dist/stores/use-event-store.cjs +1 -1
  533. package/dist/stores/use-event-store.cjs.map +1 -1
  534. package/dist/stores/use-event-store.js +35 -35
  535. package/dist/stores/use-event-store.js.map +1 -1
  536. package/dist/types/mcp-health.cjs +2 -0
  537. package/dist/types/mcp-health.cjs.map +1 -0
  538. package/dist/types/mcp-health.d.ts +21 -0
  539. package/dist/types/mcp-health.js +6 -0
  540. package/dist/types/mcp-health.js.map +1 -0
  541. package/dist/types/settings.d.ts +1 -0
  542. package/dist/utils/handle-event-for-ui.cjs +1 -1
  543. package/dist/utils/handle-event-for-ui.cjs.map +1 -1
  544. package/dist/utils/handle-event-for-ui.d.ts +5 -0
  545. package/dist/utils/handle-event-for-ui.js +71 -57
  546. package/dist/utils/handle-event-for-ui.js.map +1 -1
  547. package/dist/utils/mcp-credential-validation.cjs +1 -1
  548. package/dist/utils/mcp-credential-validation.cjs.map +1 -1
  549. package/dist/utils/mcp-credential-validation.js +32 -16
  550. package/dist/utils/mcp-credential-validation.js.map +1 -1
  551. package/dist/utils/mcp-server-health-key.cjs +2 -0
  552. package/dist/utils/mcp-server-health-key.cjs.map +1 -0
  553. package/dist/utils/mcp-server-health-key.d.ts +16 -0
  554. package/dist/utils/mcp-server-health-key.js +23 -0
  555. package/dist/utils/mcp-server-health-key.js.map +1 -0
  556. package/dist/utils/mcp-test-error-message.cjs +2 -0
  557. package/dist/utils/mcp-test-error-message.cjs.map +1 -0
  558. package/dist/utils/mcp-test-error-message.d.ts +8 -0
  559. package/dist/utils/mcp-test-error-message.js +14 -0
  560. package/dist/utils/mcp-test-error-message.js.map +1 -0
  561. package/dist/utils/redact-mcp-secrets.cjs +2 -0
  562. package/dist/utils/redact-mcp-secrets.cjs.map +1 -0
  563. package/dist/utils/redact-mcp-secrets.d.ts +13 -0
  564. package/dist/utils/redact-mcp-secrets.js +57 -0
  565. package/dist/utils/redact-mcp-secrets.js.map +1 -0
  566. package/dist/utils/settings-utils.cjs.map +1 -1
  567. package/dist/utils/settings-utils.js.map +1 -1
  568. package/dist/utils/skill-install-events.cjs +3 -0
  569. package/dist/utils/skill-install-events.cjs.map +1 -0
  570. package/dist/utils/skill-install-events.d.ts +16 -0
  571. package/dist/utils/skill-install-events.js +26 -0
  572. package/dist/utils/skill-install-events.js.map +1 -0
  573. package/dist/utils/title-llm-profile.cjs +2 -0
  574. package/dist/utils/title-llm-profile.cjs.map +1 -0
  575. package/dist/utils/title-llm-profile.d.ts +2 -0
  576. package/dist/utils/title-llm-profile.js +11 -0
  577. package/dist/utils/title-llm-profile.js.map +1 -0
  578. package/package.json +10 -3
  579. package/scripts/check-sdk-version-sync.mjs +6 -6
  580. package/scripts/check-translation-completeness.cjs +2 -0
  581. package/scripts/dev-process-utils.mjs +62 -1
  582. package/scripts/dev-safe.mjs +2 -2
  583. package/scripts/dev-static.mjs +5 -3
  584. package/scripts/dev-with-automation.mjs +98 -14
  585. package/scripts/download-node.mjs +382 -0
  586. package/scripts/download-uv.mjs +204 -0
  587. package/scripts/logger.mjs +64 -35
  588. package/build/assets/agent-server-compatibility-BWN-EIQn.js +0 -1
  589. package/build/assets/agent-server-conversation-service.api-DSPIkMwJ.js +0 -41
  590. package/build/assets/agent-settings-CgCnqaAA.js +0 -2
  591. package/build/assets/agent-settings-DDkSavD7.js +0 -1
  592. package/build/assets/api-key-entry-screen-iZc7n8AI.js +0 -1
  593. package/build/assets/app-settings-B5zyipxJ.js +0 -1
  594. package/build/assets/automation-export-BwRX6H32.js +0 -2
  595. package/build/assets/automations-list-lHAVzMoA.js +0 -1
  596. package/build/assets/backend-form-modal-RGcu909r.js +0 -1
  597. package/build/assets/browser-store-BI6uklCG.js +0 -1
  598. package/build/assets/chat-send-button-BZHG2hAW.js +0 -1
  599. package/build/assets/client-source-BZK8vRs2.js +0 -1
  600. package/build/assets/cloud-funnel-analytics-Zcv57hfT.js +0 -1
  601. package/build/assets/conversation-BoCYx8n2.js +0 -89
  602. package/build/assets/conversation-DgHqMjnY.js +0 -1
  603. package/build/assets/conversation-panel-78Rxh_-I.js +0 -1
  604. package/build/assets/conversation-state-store-B2Bp32XI.js +0 -1
  605. package/build/assets/declaration-BGsHjPEW.js +0 -1
  606. package/build/assets/extensions-hub-CCbLA7IM.js +0 -1
  607. package/build/assets/extensions-navigation-CPNjMNvR.js +0 -1
  608. package/build/assets/files-tab-store-DJsPxYdg.js +0 -1
  609. package/build/assets/files-tab-y4K7EXBO.js +0 -1
  610. package/build/assets/git-provider-icon-ITvsH4Ko.js +0 -1
  611. package/build/assets/home-BwbeOWwU.js +0 -1
  612. package/build/assets/home-DKPGQ5P3.js +0 -1
  613. package/build/assets/index-home-Bko2Itv5.js +0 -1
  614. package/build/assets/install-server-modal-Qk1gwyI8.js +0 -1
  615. package/build/assets/lesson-plan-DMCPdtrf.js +0 -4
  616. package/build/assets/llm-not-configured-banner-C1ktyb3C.js +0 -27
  617. package/build/assets/llm-settings-BBsZFi9l.js +0 -1
  618. package/build/assets/llm-settings-DFkJQr_w.js +0 -1
  619. package/build/assets/manage-backends-modal-CKQ46rvw.js +0 -1
  620. package/build/assets/manifest-03478bbf.js +0 -1
  621. package/build/assets/markdown-renderer-B4V2OMFP.js +0 -2
  622. package/build/assets/mcp-BapoJsDc.js +0 -9
  623. package/build/assets/messages-CUBFMY5o.js +0 -40
  624. package/build/assets/objectWithoutPropertiesLoose-BgYIAs2y.js +0 -1
  625. package/build/assets/onboarding-6NMkdNxV.js +0 -1
  626. package/build/assets/onboarding-modal-Q4AnpIdg.js +0 -1
  627. package/build/assets/recommended-automations-launcher-Bj7PuHY1.js +0 -58
  628. package/build/assets/refresh-cw-B9B7-vsy.js +0 -1
  629. package/build/assets/root-CDnuTToz.js +0 -2
  630. package/build/assets/root-CYWeTrWE.css +0 -1
  631. package/build/assets/root-layout-DfYVlcRG.js +0 -2
  632. package/build/assets/sdk-section-page-DLLNrYjI.js +0 -1
  633. package/build/assets/secrets-settings-CHy72A5C.js +0 -1
  634. package/build/assets/settings-B9S8UVQr.js +0 -1
  635. package/build/assets/settings-index-BKDl2CQ-.js +0 -1
  636. package/build/assets/settings-modal-DeK11CyT.js +0 -1
  637. package/build/assets/shared-conversation-9atDzTR4.js +0 -1
  638. package/build/assets/sidebar-mobile-menu-toggle-giMMZctn.js +0 -1
  639. package/build/assets/skills-plugins-Tropusio.js +0 -1
  640. package/build/assets/skills-settings-BvzBBCWk.js +0 -2
  641. package/build/assets/telemetry-C5JMEfqB.js +0 -2
  642. package/build/assets/use-activate-agent-profile-DlykqEfN.js +0 -1
  643. package/build/assets/use-backends-health-CLHPitJM.js +0 -1
  644. package/build/assets/use-config-B5r8erYt.js +0 -1
  645. package/build/assets/use-event-store-Bbaz51GR.js +0 -4
  646. package/build/assets/use-llm-profiles-Dr5__SlZ.js +0 -1
  647. package/build/assets/use-plugins-marketplace-BplU65ae.js +0 -1
  648. package/build/assets/use-runtime-is-ready-vCFOcLFa.js +0 -1
  649. package/build/assets/use-settings-CyxSlyTQ.js +0 -1
  650. package/build/assets/use-settings-nav-items-zEkSO_Bf.js +0 -16
  651. package/build/assets/use-tracking-D2Qg5Lpd.js +0 -1
  652. package/build/assets/useMutationState-OnAsCLA_.js +0 -1
  653. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BD7v71DM.js +0 -2
  654. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BT05Osu4.js +0 -28
  655. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-COhHANvV.js +0 -2
  656. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DgzWSMRc.js +0 -2
  657. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DqZatR3B.js +0 -7
  658. package/build/assets/vendor~index-home~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-FghQ3Z1a.js +0 -1
  659. package/build/assets/vendor~root-layout~index-home~home~conversation-panel~conversation-BTvhGHHG.js +0 -1
  660. package/build/assets/vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-C4-GDgiF.js +0 -1
  661. package/dist/components/features/analytics/telemetry-consent-banner.cjs +0 -2
  662. package/dist/components/features/analytics/telemetry-consent-banner.cjs.map +0 -1
  663. package/dist/components/features/analytics/telemetry-consent-banner.d.ts +0 -29
  664. package/dist/components/features/analytics/telemetry-consent-banner.js +0 -59
  665. package/dist/components/features/analytics/telemetry-consent-banner.js.map +0 -1
  666. /package/build/assets/{__vite-browser-external-BQUj0sYu.js → __vite-browser-external-CKHVBTNC.js} +0 -0
  667. /package/build/assets/{agent-server-ui-style-scope-qhnhZHBV.js → agent-server-ui-style-scope-BvSa9on7.js} +0 -0
  668. /package/build/assets/{automation-LZB0MjdV.js → automation-BGiPmxFR.js} +0 -0
  669. /package/build/assets/{canvas-ui-COgjltGz.js → canvas-ui-CC4EJo-H.js} +0 -0
  670. /package/build/assets/{checkmark-h7udgXMM.js → checkmark-ygQB1AtV.js} +0 -0
  671. /package/build/assets/{close-DLFb5kfv.js → close-BjSA0HWT.js} +0 -0
  672. /package/build/assets/{code-tag-DYlAhEQY.js → code-tag-D9aPPbOe.js} +0 -0
  673. /package/build/assets/{common-CxbUL3nR.js → common-CCuqQy_S.js} +0 -0
  674. /package/build/assets/{conversation-local-storage-F6zVMM--.js → conversation-local-storage-BDjWPg0Z.js} +0 -0
  675. /package/build/assets/{copy-BLmGeJcd.js → copy-Dce7sOvS.js} +0 -0
  676. /package/build/assets/{copy-D1YGjP-C.js → copy-iPJAncAj.js} +0 -0
  677. /package/build/assets/{dist-CodhvuAP.js → dist-D9PhjOaq.js} +0 -0
  678. /package/build/assets/{environment-switch-store-DkjlKWzN.js → environment-switch-store-DHfcz4M4.js} +0 -0
  679. /package/build/assets/{extension-module-card-classes-9V3J9J_b.js → extension-module-card-classes-BpebKiN6.js} +0 -0
  680. /package/build/assets/{external-link-lhdITPqq.js → external-link-CpPMXxrV.js} +0 -0
  681. /package/build/assets/{folder-Bk311brm.js → folder-BabbYnbF.js} +0 -0
  682. /package/build/assets/{format-time-delta-DhDbkD7V.js → format-time-delta-BkAmJEPT.js} +0 -0
  683. /package/build/assets/{get-language-from-path-DBqBsCWH.js → get-language-from-path-CPNnuVhI.js} +0 -0
  684. /package/build/assets/{git-status-mapper-DnL9OC8_.js → git-status-mapper-BIXK0AAG.js} +0 -0
  685. /package/build/assets/{iconBase-Dz51Zn5x.js → iconBase-Ch-fOe8B.js} +0 -0
  686. /package/build/assets/{link-external-CaBmUEs_.js → link-external-DfKQroKX.js} +0 -0
  687. /package/build/assets/{list-todo-CAtvkzcy.js → list-todo-6J6zc8oS.js} +0 -0
  688. /package/build/assets/{llm-subscription-DtsX5Lhg.js → llm-subscription-Bvu8BLXc.js} +0 -0
  689. /package/build/assets/{loader-circle-CsAsfLIs.js → loader-circle-DGsqG5qw.js} +0 -0
  690. /package/build/assets/{map-provider-BbIb12oh.js → map-provider-BSvXvVR0.js} +0 -0
  691. /package/build/assets/{middleware-DYHYNAnP.js → middleware-_izt7V6U.js} +0 -0
  692. /package/build/assets/{modal-backdrop-zVwfOJZr.js → modal-backdrop-B7H42xHG.js} +0 -0
  693. /package/build/assets/{navigation-context-DPs2G0X8.js → navigation-context-cvyvI1WA.js} +0 -0
  694. /package/build/assets/{openhands-logo-bcNw5bkY.js → openhands-logo-pEw0nKFP.js} +0 -0
  695. /package/build/assets/{pencil-CWhApFIT.js → pencil-DmSqWUA3.js} +0 -0
  696. /package/build/assets/{react-DPDgL6du.js → react-MKQOkha9.js} +0 -0
  697. /package/build/assets/{search-BgOCR7_c.js → search-B5D5EuSn.js} +0 -0
  698. /package/build/assets/{search-dIQ09UY2.js → search-vnYL5jwV.js} +0 -0
  699. /package/build/assets/{settings-gear-MXCyZW0x.js → settings-gear-BOwF59Bs.js} +0 -0
  700. /package/build/assets/{settings-section-header-context-COMOIaxV.js → settings-section-header-context-CGgHZXrf.js} +0 -0
  701. /package/build/assets/{skills-EqzxYBvT.js → skills-arUOHLcS.js} +0 -0
  702. /package/build/assets/{square-chevron-right-DE-JN-2Z.js → square-chevron-right-EizCGtgq.js} +0 -0
  703. /package/build/assets/{switch-skeleton-DqQwalyu.js → switch-skeleton-BjPRx1QD.js} +0 -0
  704. /package/build/assets/{terminal-Cgf___rK.js → terminal-a57bHgZs.js} +0 -0
  705. /package/build/assets/{trash-2-BU6BlW4i.js → trash-2-CF3hilrY.js} +0 -0
  706. /package/build/assets/{u-check-circle-BqoIxNz_.js → u-check-circle-WGqHdtfO.js} +0 -0
  707. /package/build/assets/{u-check-circle-half-MsShT8Ec.js → u-check-circle-half-LiwZkuFe.js} +0 -0
  708. /package/build/assets/{u-edit-DL5LgKon.js → u-edit-BwHepAPi.js} +0 -0
  709. /package/build/assets/{use-breakpoint-BP3lqLMx.js → use-breakpoint-B-O0lQ84.js} +0 -0
  710. /package/build/assets/{use-click-outside-element-D9BYdANT.js → use-click-outside-element-DLQIMQrz.js} +0 -0
  711. /package/build/assets/{use-is-authed-BezEyfeh.js → use-is-authed-BSeo4coy.js} +0 -0
  712. /package/build/assets/{use-onboarding-completion-C_Swglzp.js → use-onboarding-completion-C0hPNGpw.js} +0 -0
  713. /package/build/assets/{v4-DbCkTSoU.js → v4-q4yWTlMz.js} +0 -0
  714. /package/build/assets/{vendor~browser-By2MVv_N.js → vendor~browser-UwB1jbAZ.js} +0 -0
  715. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-CxfzCmkN.js → vendor~conversation-panel~conversation~alert-banner-DHDkknt5.js} +0 -0
  716. /package/build/assets/{vendor~index-home~home~conversation-panel~conversation-ChmYLVgz.js → vendor~index-home~home~conversation-panel~conversation-Bff9cq5J.js} +0 -0
  717. /package/build/assets/{vendor~module-D5a_AClf.js → vendor~module-D4DeFV4J.js} +0 -0
  718. /package/build/assets/{vendor~terminal-CnKZILnC.js → vendor~terminal-B7Eiak0-.js} +0 -0
  719. /package/build/assets/{x-ZE4iplpF.js → x-DQB_00VJ.js} +0 -0
  720. /package/build/assets/{x-mark-Batp6gXa.js → x-mark-B32XuEwS.js} +0 -0
@@ -1,53 +1,53 @@
1
1
  import { create as e } from "../node_modules/zustand/esm/react.js";
2
2
  import { isStreamingDeltaEvent as t } from "../types/agent-server/type-guards.js";
3
- import { handleEventForUI as n, mergeStreamingDeltaEvent as r } from "../utils/handle-event-for-ui.js";
3
+ import { handleEventForUI as n, isSameStreamingSender as r, mergeStreamingDeltaEvent as i } from "../utils/handle-event-for-ui.js";
4
4
  //#region src/stores/use-event-store.ts
5
- var i = (e) => "id" in e ? e.id : void 0, a = (e) => "timestamp" in e ? e.timestamp : void 0, o = (e, t) => {
6
- let n = a(e), r = a(t);
5
+ var a = (e) => "id" in e ? e.id : void 0, o = (e) => "timestamp" in e ? e.timestamp : void 0, s = (e, t) => {
6
+ let n = o(e), r = o(t);
7
7
  return !n && !r ? 0 : n ? r ? n.localeCompare(r) : -1 : 1;
8
- }, s = (e, t) => {
8
+ }, c = (e, t) => {
9
9
  if (e.length === 0) return !1;
10
- let n = e[e.length - 1], r = a(n), i = a(t);
10
+ let n = e[e.length - 1], r = o(n), i = o(t);
11
11
  return !r || !i ? !1 : i < r;
12
- }, c = (e, a) => {
13
- let o = i(a);
14
- if (o !== void 0 && e.eventIds.has(o)) return e;
15
- let s = o === void 0 ? e.eventIds : new Set(e.eventIds).add(o), c = e.events.length - 1, l = e.events[c], u = l && t(a) && t(l), d = [...e.events];
16
- return u ? d[c] = r(a, l) : d.push(a), {
12
+ }, l = (e, o) => {
13
+ let s = a(o);
14
+ if (s !== void 0 && e.eventIds.has(s)) return e;
15
+ let c = s === void 0 ? e.eventIds : new Set(e.eventIds).add(s), l = e.events.length - 1, u = e.events[l], d = u && t(o) && t(u) && r(o, u), f = [...e.events];
16
+ return d ? f[l] = i(o, u) : f.push(o), {
17
17
  ...e,
18
- events: d,
19
- eventIds: s,
20
- uiEvents: n(a, e.uiEvents)
18
+ events: f,
19
+ eventIds: c,
20
+ uiEvents: n(o, e.uiEvents)
21
21
  };
22
- }, l = (e) => ({
22
+ }, u = (e) => ({
23
23
  ...e,
24
- events: [...e.events].sort(o),
25
- uiEvents: [...e.uiEvents].sort(o)
26
- }), u = (e, t) => {
27
- let n = c(e, t);
28
- return n === e ? e : !s(e.events, t) && !s(e.uiEvents, t) ? n : l(n);
29
- }, d = e()((e) => ({
24
+ events: [...e.events].sort(s),
25
+ uiEvents: [...e.uiEvents].sort(s)
26
+ }), d = (e, t) => {
27
+ let n = l(e, t);
28
+ return n === e ? e : !c(e.events, t) && !c(e.uiEvents, t) ? n : u(n);
29
+ }, f = e()((e) => ({
30
30
  events: [],
31
31
  eventIds: /* @__PURE__ */ new Set(),
32
32
  uiEvents: [],
33
33
  loadedConversationId: null,
34
- addEvent: (t) => e((e) => u(e, t)),
35
- addEvents: (a) => e((e) => {
36
- if (a.length === 0) return e;
37
- let o = new Set(e.eventIds), s = [...e.events], c = [...e.uiEvents], u = !1;
38
- for (let e of a) {
39
- let a = i(e);
40
- if (!(a !== void 0 && o.has(a))) {
41
- u = !0, a !== void 0 && o.add(a);
42
- let i = s.length - 1, l = s[i];
43
- l && t(e) && t(l) ? s[i] = r(e, l) : s.push(e), c = n(e, c);
34
+ addEvent: (t) => e((e) => d(e, t)),
35
+ addEvents: (o) => e((e) => {
36
+ if (o.length === 0) return e;
37
+ let s = new Set(e.eventIds), c = [...e.events], l = [...e.uiEvents], d = !1;
38
+ for (let e of o) {
39
+ let o = a(e);
40
+ if (!(o !== void 0 && s.has(o))) {
41
+ d = !0, o !== void 0 && s.add(o);
42
+ let a = c.length - 1, u = c[a];
43
+ u && t(e) && t(u) && r(e, u) ? c[a] = i(e, u) : c.push(e), l = n(e, l);
44
44
  }
45
45
  }
46
- return u ? l({
46
+ return d ? u({
47
47
  ...e,
48
- events: s,
49
- eventIds: o,
50
- uiEvents: c
48
+ events: c,
49
+ eventIds: s,
50
+ uiEvents: l
51
51
  }) : e;
52
52
  }),
53
53
  clearEvents: () => e(() => ({
@@ -64,6 +64,6 @@ var i = (e) => "id" in e ? e.id : void 0, a = (e) => "timestamp" in e ? e.timest
64
64
  }))
65
65
  }));
66
66
  //#endregion
67
- export { d as useEventStore };
67
+ export { f as useEventStore };
68
68
 
69
69
  //# sourceMappingURL=use-event-store.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"use-event-store.js","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n handleEventForUI,\n mergeStreamingDeltaEvent,\n} from \"#/utils/handle-event-for-ui\";\nimport { isStreamingDeltaEvent } from \"#/types/agent-server/type-guards\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n /**\n * The conversation whose events currently populate the store. The store is\n * global (not keyed by conversation), so the conversation route uses this to\n * tell a genuine conversation switch apart from a remount of the *same*\n * conversation (e.g. navigating to Settings and back) — only the former\n * should clear the accumulated events.\n */\n loadedConversationId: string | null;\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n /**\n * Clear all events. Also resets `loadedConversationId` to `null` so the\n * store never claims to hold a conversation whose events have been wiped —\n * the invariant (`loadedConversationId` reflects the conversation whose\n * events are in the arrays) holds even for a standalone clear.\n */\n clearEvents: () => void;\n /**\n * Atomically clear all events and record which conversation is now loaded.\n * Collapsing the reset and the bookkeeping into a single `set` keeps the\n * store invariant enforced at the boundary, rather than relying on every\n * call-site to invoke a clear and a `loadedConversationId` setter in the\n * right order.\n */\n clearEventsForConversation: (conversationId: string | null) => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n const lastEventIndex = state.events.length - 1;\n const lastEvent = state.events[lastEventIndex];\n const shouldMergeStreamingDelta =\n lastEvent &&\n isStreamingDeltaEvent(event) &&\n isStreamingDeltaEvent(lastEvent);\n const events = [...state.events];\n if (shouldMergeStreamingDelta) {\n events[lastEventIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n } else {\n events.push(event);\n }\n\n return {\n ...state,\n events,\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n\n const lastEventIndex = events.length - 1;\n const lastEvent = events[lastEventIndex];\n if (\n lastEvent &&\n isStreamingDeltaEvent(event) &&\n isStreamingDeltaEvent(lastEvent)\n ) {\n events[lastEventIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n } else {\n events.push(event);\n }\n\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n })),\n clearEventsForConversation: (conversationId: string | null) =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: conversationId,\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"use-event-store.js","names":[],"sources":["../../src/stores/use-event-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n handleEventForUI,\n isSameStreamingSender,\n mergeStreamingDeltaEvent,\n} from \"#/utils/handle-event-for-ui\";\nimport { isStreamingDeltaEvent } from \"#/types/agent-server/type-guards\";\n\nexport type OHEvent = OpenHandsEvent & {\n isFromPlanningAgent?: boolean;\n};\n\nconst getEventId = (event: OHEvent): string | number | undefined =>\n \"id\" in event ? event.id : undefined;\n\nconst getEventTimestamp = (event: OHEvent): string | undefined =>\n \"timestamp\" in event ? event.timestamp : undefined;\n\n/**\n * Compare two events by timestamp for sorting.\n * Events without timestamps are placed at the end.\n */\nconst compareEventsByTimestamp = (a: OHEvent, b: OHEvent): number => {\n const timestampA = getEventTimestamp(a);\n const timestampB = getEventTimestamp(b);\n\n // Events without timestamps go to the end\n if (!timestampA && !timestampB) return 0;\n if (!timestampA) return 1;\n if (!timestampB) return -1;\n\n // Compare ISO timestamp strings (lexicographic comparison works for ISO format)\n return timestampA.localeCompare(timestampB);\n};\n\n/**\n * Check if the new event needs sorting (i.e., it's out of order).\n * Returns true if the new event's timestamp is earlier than the last event's timestamp.\n */\nconst needsSorting = (events: OHEvent[], newEvent: OHEvent): boolean => {\n if (events.length === 0) return false;\n\n const lastEvent = events[events.length - 1];\n const lastTimestamp = getEventTimestamp(lastEvent);\n const newTimestamp = getEventTimestamp(newEvent);\n\n // If either event doesn't have a timestamp, don't sort\n if (!lastTimestamp || !newTimestamp) return false;\n\n // Sort needed if new event's timestamp is earlier than last event's timestamp\n return newTimestamp < lastTimestamp;\n};\n\nexport interface EventState {\n events: OHEvent[];\n eventIds: Set<string | number>;\n uiEvents: OHEvent[];\n /**\n * The conversation whose events currently populate the store. The store is\n * global (not keyed by conversation), so the conversation route uses this to\n * tell a genuine conversation switch apart from a remount of the *same*\n * conversation (e.g. navigating to Settings and back) — only the former\n * should clear the accumulated events.\n */\n loadedConversationId: string | null;\n addEvent: (event: OHEvent) => void;\n /**\n * Bulk-insert events. Used for the initial REST history load and for\n * \"scroll up to load older\" pagination. Newly-added events are de-duped\n * against the existing store and the combined list is re-sorted by\n * timestamp so older pages drop into the correct position.\n */\n addEvents: (events: OHEvent[]) => void;\n /**\n * Clear all events. Also resets `loadedConversationId` to `null` so the\n * store never claims to hold a conversation whose events have been wiped —\n * the invariant (`loadedConversationId` reflects the conversation whose\n * events are in the arrays) holds even for a standalone clear.\n */\n clearEvents: () => void;\n /**\n * Atomically clear all events and record which conversation is now loaded.\n * Collapsing the reset and the bookkeeping into a single `set` keeps the\n * store invariant enforced at the boundary, rather than relying on every\n * call-site to invoke a clear and a `loadedConversationId` setter in the\n * right order.\n */\n clearEventsForConversation: (conversationId: string | null) => void;\n}\n\nconst appendEvent = (state: EventState, event: OHEvent): EventState => {\n // Deduplicate: skip if event with same id already exists (O(1) lookup)\n const eventId = getEventId(event);\n if (eventId !== undefined && state.eventIds.has(eventId)) {\n return state;\n }\n\n const newEventIds =\n eventId !== undefined\n ? new Set(state.eventIds).add(eventId)\n : state.eventIds;\n\n const lastEventIndex = state.events.length - 1;\n const lastEvent = state.events[lastEventIndex];\n const shouldMergeStreamingDelta =\n lastEvent &&\n isStreamingDeltaEvent(event) &&\n isStreamingDeltaEvent(lastEvent) &&\n isSameStreamingSender(event, lastEvent);\n const events = [...state.events];\n if (shouldMergeStreamingDelta) {\n events[lastEventIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n } else {\n events.push(event);\n }\n\n return {\n ...state,\n events,\n eventIds: newEventIds,\n uiEvents: handleEventForUI(event, state.uiEvents),\n };\n};\n\nconst sortEventState = (state: EventState): EventState => ({\n ...state,\n events: [...state.events].sort(compareEventsByTimestamp),\n uiEvents: [...state.uiEvents].sort(compareEventsByTimestamp),\n});\n\nconst applyAddEvent = (state: EventState, event: OHEvent): EventState => {\n const next = appendEvent(state, event);\n if (next === state) {\n return state;\n }\n\n if (\n !needsSorting(state.events, event) &&\n !needsSorting(state.uiEvents, event)\n ) {\n return next;\n }\n\n return sortEventState(next);\n};\n\nexport const useEventStore = create<EventState>()((set) => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n addEvent: (event: OHEvent) => set((state) => applyAddEvent(state, event)),\n addEvents: (incoming: OHEvent[]) =>\n set((state) => {\n if (incoming.length === 0) return state;\n\n const eventIds = new Set(state.eventIds);\n const events = [...state.events];\n let uiEvents = [...state.uiEvents];\n let added = false;\n\n for (const event of incoming) {\n const eventId = getEventId(event);\n const isDuplicate = eventId !== undefined && eventIds.has(eventId);\n\n if (!isDuplicate) {\n added = true;\n if (eventId !== undefined) {\n eventIds.add(eventId);\n }\n\n const lastEventIndex = events.length - 1;\n const lastEvent = events[lastEventIndex];\n if (\n lastEvent &&\n isStreamingDeltaEvent(event) &&\n isStreamingDeltaEvent(lastEvent) &&\n isSameStreamingSender(event, lastEvent)\n ) {\n events[lastEventIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n } else {\n events.push(event);\n }\n\n uiEvents = handleEventForUI(event, uiEvents);\n }\n }\n\n if (!added) {\n return state;\n }\n\n return sortEventState({\n ...state,\n events,\n eventIds,\n uiEvents,\n });\n }),\n clearEvents: () =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: null,\n })),\n clearEventsForConversation: (conversationId: string | null) =>\n set(() => ({\n events: [],\n eventIds: new Set(),\n uiEvents: [],\n loadedConversationId: conversationId,\n })),\n}));\n\n// In dev builds, expose the store on `window` so that fixture/preview\n// scripts (e.g. .pr/issue-132 demo capture) can inject synthetic events\n// without round-tripping through the agent-server. Tree-shaken in\n// production builds via `import.meta.env.DEV`.\nif (\n typeof window !== \"undefined\" &&\n typeof import.meta !== \"undefined\" &&\n (import.meta as { env?: { DEV?: boolean } }).env?.DEV\n) {\n (\n window as unknown as { __OH_EVENT_STORE__?: typeof useEventStore }\n ).__OH_EVENT_STORE__ = useEventStore;\n}\n"],"mappings":";;;;AAaA,IAAM,KAAc,MAClB,QAAQ,IAAQ,EAAM,KAAK,KAAA,GAEvB,KAAqB,MACzB,eAAe,IAAQ,EAAM,YAAY,KAAA,GAMrC,KAA4B,GAAY,MAAuB;CACnE,IAAM,IAAa,EAAkB,EAAE,EACjC,IAAa,EAAkB,EAAE;AAQvC,QALI,CAAC,KAAc,CAAC,IAAmB,IAClC,IACA,IAGE,EAAW,cAAc,EAAW,GAHnB,KADA;GAWpB,KAAgB,GAAmB,MAA+B;AACtE,KAAI,EAAO,WAAW,EAAG,QAAO;CAEhC,IAAM,IAAY,EAAO,EAAO,SAAS,IACnC,IAAgB,EAAkB,EAAU,EAC5C,IAAe,EAAkB,EAAS;AAMhD,QAHI,CAAC,KAAiB,CAAC,IAAqB,KAGrC,IAAe;GAwClB,KAAe,GAAmB,MAA+B;CAErE,IAAM,IAAU,EAAW,EAAM;AACjC,KAAI,MAAY,KAAA,KAAa,EAAM,SAAS,IAAI,EAAQ,CACtD,QAAO;CAGT,IAAM,IACJ,MAAY,KAAA,IAER,EAAM,WADN,IAAI,IAAI,EAAM,SAAS,CAAC,IAAI,EAAQ,EAGpC,IAAiB,EAAM,OAAO,SAAS,GACvC,IAAY,EAAM,OAAO,IACzB,IACJ,KACA,EAAsB,EAAM,IAC5B,EAAsB,EAAU,IAChC,EAAsB,GAAO,EAAU,EACnC,IAAS,CAAC,GAAG,EAAM,OAAO;AAOhC,QANI,IACF,EAAO,KAAkB,EAAyB,GAAO,EAAU,GAEnE,EAAO,KAAK,EAAM,EAGb;EACL,GAAG;EACH;EACA,UAAU;EACV,UAAU,EAAiB,GAAO,EAAM,SAAS;EAClD;GAGG,KAAkB,OAAmC;CACzD,GAAG;CACH,QAAQ,CAAC,GAAG,EAAM,OAAO,CAAC,KAAK,EAAyB;CACxD,UAAU,CAAC,GAAG,EAAM,SAAS,CAAC,KAAK,EAAyB;CAC7D,GAEK,KAAiB,GAAmB,MAA+B;CACvE,IAAM,IAAO,EAAY,GAAO,EAAM;AAYtC,QAXI,MAAS,IACJ,IAIP,CAAC,EAAa,EAAM,QAAQ,EAAM,IAClC,CAAC,EAAa,EAAM,UAAU,EAAM,GAE7B,IAGF,EAAe,EAAK;GAGhB,IAAgB,GAAoB,EAAE,OAAS;CAC1D,QAAQ,EAAE;CACV,0BAAU,IAAI,KAAK;CACnB,UAAU,EAAE;CACZ,sBAAsB;CACtB,WAAW,MAAmB,GAAK,MAAU,EAAc,GAAO,EAAM,CAAC;CACzE,YAAY,MACV,GAAK,MAAU;AACb,MAAI,EAAS,WAAW,EAAG,QAAO;EAElC,IAAM,IAAW,IAAI,IAAI,EAAM,SAAS,EAClC,IAAS,CAAC,GAAG,EAAM,OAAO,EAC5B,IAAW,CAAC,GAAG,EAAM,SAAS,EAC9B,IAAQ;AAEZ,OAAK,IAAM,KAAS,GAAU;GAC5B,IAAM,IAAU,EAAW,EAAM;AAGjC,OAAI,EAFgB,MAAY,KAAA,KAAa,EAAS,IAAI,EAAQ,GAEhD;AAEhB,IADA,IAAQ,IACJ,MAAY,KAAA,KACd,EAAS,IAAI,EAAQ;IAGvB,IAAM,IAAiB,EAAO,SAAS,GACjC,IAAY,EAAO;AAYzB,IAVE,KACA,EAAsB,EAAM,IAC5B,EAAsB,EAAU,IAChC,EAAsB,GAAO,EAAU,GAEvC,EAAO,KAAkB,EAAyB,GAAO,EAAU,GAEnE,EAAO,KAAK,EAAM,EAGpB,IAAW,EAAiB,GAAO,EAAS;;;AAQhD,SAJK,IAIE,EAAe;GACpB,GAAG;GACH;GACA;GACA;GACD,CAAC,GARO;GAST;CACJ,mBACE,SAAW;EACT,QAAQ,EAAE;EACV,0BAAU,IAAI,KAAK;EACnB,UAAU,EAAE;EACZ,sBAAsB;EACvB,EAAE;CACL,6BAA6B,MAC3B,SAAW;EACT,QAAQ,EAAE;EACV,0BAAU,IAAI,KAAK;EACnB,UAAU,EAAE;EACZ,sBAAsB;EACvB,EAAE;CACN,EAAE"}
@@ -0,0 +1,2 @@
1
+ require(`../_virtual/_rolldown/runtime.cjs`);var e={status:`unchecked`};exports.UNCHECKED_MCP_HEALTH=e;
2
+ //# sourceMappingURL=mcp-health.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-health.cjs","names":[],"sources":["../../src/types/mcp-health.ts"],"sourcesContent":["import type { ExtendedMCPTestFailureKind } from \"#/types/mcp-server\";\n\n/** How strongly the last successful check proved the server works. */\nexport type McpHealthVerification =\n // A representative read-only tool call succeeded — credentials exercised.\n | \"verified\"\n // Connect + tools/list only — proves connectivity, not credentials.\n | \"connectivity-only\";\n\nexport type McpServerHealth =\n | { status: \"unchecked\" }\n | { status: \"checking\"; checkId: number }\n | {\n status: \"healthy\";\n verification: McpHealthVerification;\n toolCount: number;\n checkedAt: number;\n }\n | {\n status: \"failed\";\n kind: ExtendedMCPTestFailureKind;\n /** Redacted, display-safe error detail. */\n error: string;\n checkedAt: number;\n };\n\nexport const UNCHECKED_MCP_HEALTH: McpServerHealth = { status: \"unchecked\" };\n"],"mappings":"6CA0BA,IAAa,EAAwC,CAAE,OAAQ,YAAa"}
@@ -0,0 +1,21 @@
1
+ import type { ExtendedMCPTestFailureKind } from "#/types/mcp-server";
2
+ /** How strongly the last successful check proved the server works. */
3
+ export type McpHealthVerification = "verified" | "connectivity-only";
4
+ export type McpServerHealth = {
5
+ status: "unchecked";
6
+ } | {
7
+ status: "checking";
8
+ checkId: number;
9
+ } | {
10
+ status: "healthy";
11
+ verification: McpHealthVerification;
12
+ toolCount: number;
13
+ checkedAt: number;
14
+ } | {
15
+ status: "failed";
16
+ kind: ExtendedMCPTestFailureKind;
17
+ /** Redacted, display-safe error detail. */
18
+ error: string;
19
+ checkedAt: number;
20
+ };
21
+ export declare const UNCHECKED_MCP_HEALTH: McpServerHealth;
@@ -0,0 +1,6 @@
1
+ //#region src/types/mcp-health.ts
2
+ var e = { status: "unchecked" };
3
+ //#endregion
4
+ export { e as UNCHECKED_MCP_HEALTH };
5
+
6
+ //# sourceMappingURL=mcp-health.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mcp-health.js","names":[],"sources":["../../src/types/mcp-health.ts"],"sourcesContent":["import type { ExtendedMCPTestFailureKind } from \"#/types/mcp-server\";\n\n/** How strongly the last successful check proved the server works. */\nexport type McpHealthVerification =\n // A representative read-only tool call succeeded — credentials exercised.\n | \"verified\"\n // Connect + tools/list only — proves connectivity, not credentials.\n | \"connectivity-only\";\n\nexport type McpServerHealth =\n | { status: \"unchecked\" }\n | { status: \"checking\"; checkId: number }\n | {\n status: \"healthy\";\n verification: McpHealthVerification;\n toolCount: number;\n checkedAt: number;\n }\n | {\n status: \"failed\";\n kind: ExtendedMCPTestFailureKind;\n /** Redacted, display-safe error detail. */\n error: string;\n checkedAt: number;\n };\n\nexport const UNCHECKED_MCP_HEALTH: McpServerHealth = { status: \"unchecked\" };\n"],"mappings":";AA0BA,IAAa,IAAwC,EAAE,QAAQ,aAAa"}
@@ -136,6 +136,7 @@ export type Settings = {
136
136
  email_verified?: boolean;
137
137
  git_user_name?: string;
138
138
  git_user_email?: string;
139
+ title_llm_profile?: string | null;
139
140
  agent_settings_schema?: SettingsSchema | null;
140
141
  agent_settings?: Record<string, SettingsValue> | null;
141
142
  conversation_settings_schema?: SettingsSchema | null;
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../types/agent-server/type-guards.cjs`),t=require(`../components/conversation-events/chat/event-thought-helpers.cjs`);var n=(e,t)=>({...t,content:`${t.content??``}${e.content??``}`||null,reasoning_content:`${t.reasoning_content??``}${e.reasoning_content??``}`||null}),r=(e,t)=>({...e,content:`${e.content??``}${t}`||null}),i=t=>{for(let n=t.length-1;n>=0;--n){let r=t[n];if(e.isMessageEvent(r)&&r.source===`user`)return n}return-1},a=e=>e.filter(e=>e.type===`text`).map(e=>e.text).join(``),o=t=>e.isActionEvent(t)&&t.action.kind===`FinishAction`?t.action.message:e.isMessageEvent(t)&&t.source===`agent`?a(t.llm_message.content):null,s=(e,t)=>{let n=0,r=0;for(let i of t){let t=e.indexOf(i,n);if(t===-1)return{matched:!1,lastMatchEnd:r};r=t+i.length,n=r}return{matched:!0,lastMatchEnd:r}},c=t=>{let n=i(t);return t.map((e,t)=>({event:e,index:t})).filter(t=>t.index>n&&e.isStreamingDeltaEvent(t.event)&&(t.event.content?.length??0)>0)},l=t=>{let n=[];for(let r=t.length-1;r>=0;--r){let i=t[r];if(!e.isStreamingDeltaEvent(i))break;(i.content?.length??0)>0&&n.unshift({event:i,index:r})}return n},u=(e,t)=>{let n=t.join(``);for(let t of[n,n.trimEnd()])if(t&&e.startsWith(t))return{matched:!0,lastMatchEnd:t.length};let r=t.length-1;return s(e,t.map((e,t)=>t===r?e.trimEnd():e))},d=(t,n)=>{let i=c(n);if(i.length===0)return null;let a=o(t);if(!a)return null;let{matched:s,lastMatchEnd:l}=u(a,i.map(({event:e})=>e.content??``));if(!s)return null;let d=[...n],f=a.slice(l),p=i.at(-1)?.index,m=p===void 0?void 0:d[p];return f&&p!==void 0&&m&&e.isStreamingDeltaEvent(m)&&(d[p]=r(m,f)),d},f=(n,r)=>{let i=a(n.thought);if(!i)return null;let o=l(r);if(o.length===0||!u(i,o.map(({event:e})=>e.content??``)).matched)return null;let s=t.getReasoningContent(n).trim().length>0,c=new Set(o.map(({index:e})=>e)),d=[];return r.forEach((t,n)=>{if(!c.has(n)||!e.isStreamingDeltaEvent(t)){d.push(t);return}!s&&t.reasoning_content&&d.push({...t,content:null})}),d},p=(t,r)=>{let i=[...r];if(e.isStreamingDeltaEvent(t)){if(t.content===null&&t.reasoning_content===null)return i;let r=i.length-1,a=i[r];return a&&e.isStreamingDeltaEvent(a)?(i[r]=n(t,a),i):(i.push(t),i)}if(e.isActionEvent(t)&&t.action.kind===`FinishAction`||e.isMessageEvent(t)&&t.source===`agent`){let e=d(t,i);if(e)return e}if(e.isActionEvent(t)&&t.action.kind!==`FinishAction`&&t.action.kind!==`ThinkAction`){let e=f(t,i);if(e)return e.push(t),e}if(e.isACPToolCallEvent(t)){let n=i.findIndex(n=>e.isACPToolCallEvent(n)&&n.tool_call_id===t.tool_call_id);return n===-1?i.push(t):i[n]=t,i}if(e.isObservationEvent(t)){if(t.observation.kind===`ThinkObservation`||t.observation.kind===`FinishObservation`)return i;let e=i.findIndex(e=>e.id===t.action_id);e===-1?i.push(t):i[e]=t}else i.push(t);return i};exports.handleEventForUI=p,exports.mergeStreamingDeltaEvent=n;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../types/agent-server/type-guards.cjs`),t=require(`../components/conversation-events/chat/event-thought-helpers.cjs`);var n=(e,t)=>({...t,content:`${t.content??``}${e.content??``}`||null,reasoning_content:`${t.reasoning_content??``}${e.reasoning_content??``}`||null}),r=(e,t)=>!!e.isFromPlanningAgent==!!t.isFromPlanningAgent,i=t=>{for(let n=t.length-1;n>=0;--n){let r=t[n];if(e.isMessageEvent(r)&&r.source===`user`)return n}return-1},a=e=>e.filter(e=>e.type===`text`).map(e=>e.text).join(``),o=(e,t)=>{let n=0,r=0;for(let i of t){let t=e.indexOf(i,n);if(t===-1)return{matched:!1,lastMatchEnd:r};r=t+i.length,n=r}return{matched:!0,lastMatchEnd:r}},s=(t,n)=>{let a=i(t);return t.map((e,t)=>({event:e,index:t})).filter(t=>t.index>a&&e.isStreamingDeltaEvent(t.event)&&(t.event.content?.length??0)>0&&r(n,t.event))},c=(t,n)=>{let r=[];for(let i=t.length-1;i>=0;--i){let a=t[i];if(!e.isStreamingDeltaEvent(a))break;n(a)&&r.unshift({event:a,index:i})}return r},l=e=>c(e,e=>(e.content?.length??0)>0),u=(e,t)=>c(e,e=>!!e.reasoning_content&&r(t,e)),d=(t,n,r)=>{let i=new Set(n.map(({index:e})=>e)),a=[];return t.forEach((t,n)=>{if(!i.has(n)||!e.isStreamingDeltaEvent(t)){a.push(t);return}!r&&t.reasoning_content&&a.push({...t,content:null})}),a},f=(e,t)=>{let n=t.join(``),r=new Set([n,n.trimEnd(),n.trimStart(),n.trim()]);for(let t of r)if(t&&e.startsWith(t))return{matched:!0,lastMatchEnd:t.length};let i=t.length-1;return o(e,t.map((e,t)=>t===i?e.trimEnd():e))},p=n=>e.isActionEvent(n)?t.getReasoningContent(n).trim().length>0:e.isMessageEvent(n)&&n.source===`agent`?t.splitInlineThink(a(n.llm_message.content)).reasoning.length>0:!1,m=(e,t)=>{let n=s(t,e);if(n.length===0)return null;let r=d(t,n,p(e));return r.push(e),r},h=(e,n)=>{let r=a(e.thought);if(!r)return null;let i=l(n);return i.length===0||!f(r,i.map(({event:e})=>e.content??``)).matched?null:d(n,i,t.getReasoningContent(e).trim().length>0)},g=(n,r)=>{if(t.getReasoningContent(n).trim().length===0)return null;let i=u(r,n);if(i.length===0)return null;let a=new Set(i.map(({index:e})=>e)),o=[];return r.forEach((t,n)=>{if(!a.has(n)||!e.isStreamingDeltaEvent(t)){o.push(t);return}t.content&&o.push({...t,reasoning_content:null})}),o},_=(t,i)=>{let a=[...i];if(e.isStreamingDeltaEvent(t)){if(t.content===null&&t.reasoning_content===null)return a;let i=a.length-1,o=a[i];return o&&e.isStreamingDeltaEvent(o)&&r(t,o)?(a[i]=n(t,o),a):(a.push(t),a)}if(e.isActionEvent(t)&&t.action.kind===`FinishAction`||e.isMessageEvent(t)&&t.source===`agent`){let e=m(t,a);if(e)return e}if(e.isActionEvent(t)&&t.action.kind!==`FinishAction`&&t.action.kind!==`ThinkAction`){let e=h(t,a)??g(t,a);if(e)return e.push(t),e}if(e.isACPToolCallEvent(t)){let n=a.findIndex(n=>e.isACPToolCallEvent(n)&&n.tool_call_id===t.tool_call_id);return n===-1?a.push(t):a[n]=t,a}if(e.isObservationEvent(t)){if(t.observation.kind===`ThinkObservation`||t.observation.kind===`FinishObservation`)return a;let e=a.findIndex(e=>e.id===t.action_id);e===-1?a.push(t):a[e]=t}else a.push(t);return a};exports.handleEventForUI=_,exports.isSameStreamingSender=r,exports.mergeStreamingDeltaEvent=n;
2
2
  //# sourceMappingURL=handle-event-for-ui.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle-event-for-ui.cjs","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import {\n ActionEvent,\n ImageContent,\n OpenHandsEvent,\n TextContent,\n} from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { StreamingDeltaEvent } from \"#/types/agent-server/core/events/streaming-delta-event\";\nimport { getReasoningContent } from \"#/components/conversation-events/chat/event-thought-helpers\";\n\nexport const mergeStreamingDeltaEvent = (\n incoming: StreamingDeltaEvent,\n existing: StreamingDeltaEvent,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${incoming.content ?? \"\"}` || null,\n reasoning_content:\n `${existing.reasoning_content ?? \"\"}${incoming.reasoning_content ?? \"\"}` ||\n null,\n});\n\nconst appendContentToStreamingDeltaEvent = (\n existing: StreamingDeltaEvent,\n content: string,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${content}` || null,\n});\n\nconst findLastUserMessageIndex = (events: OpenHandsEvent[]): number => {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n if (isMessageEvent(event) && event.source === \"user\") {\n return index;\n }\n }\n return -1;\n};\n\n// Join text blocks WITHOUT a separator: streaming deltas concatenate content\n// tokens directly with no separator between LLM content blocks, so using \"\\n\"\n// here would cause startsWith/findTextSegmentsInOrder to miss when reconciling\n// a multi-block message/thought against the already-rendered streaming delta.\nconst joinTextBlocks = (blocks: (TextContent | ImageContent)[]): string =>\n blocks\n .filter((block) => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\nconst getFinalAgentText = (event: OpenHandsEvent): string | null => {\n if (isActionEvent(event) && event.action.kind === \"FinishAction\") {\n return event.action.message;\n }\n\n if (isMessageEvent(event) && event.source === \"agent\") {\n return joinTextBlocks(event.llm_message.content);\n }\n\n return null;\n};\n\nconst findTextSegmentsInOrder = (\n text: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n let searchStart = 0;\n let lastMatchEnd = 0;\n\n for (const segment of segments) {\n const index = text.indexOf(segment, searchStart);\n if (index === -1) {\n return { matched: false, lastMatchEnd };\n }\n lastMatchEnd = index + segment.length;\n searchStart = lastMatchEnd;\n }\n\n return { matched: true, lastMatchEnd };\n};\n\n// Content-bearing streaming deltas of the current turn (after the last user\n// message). Reasoning-only deltas are excluded: reasoning renders in its own\n// collapsed bubble and never overlaps the message text being reconciled.\nconst getCurrentTurnContentDeltas = (\n uiEvents: OpenHandsEvent[],\n): { event: StreamingDeltaEvent; index: number }[] => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n return uiEvents\n .map((event, index) => ({ event, index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n item.index > lastUserMessageIndex &&\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0,\n );\n};\n\n// The current step's streaming delta(s): the trailing run at the end of\n// `uiEvents`. Earlier steps' deltas are separated by their observations, so\n// this never folds an earlier step's delta into the current one.\nconst getTrailingContentDeltas = (\n uiEvents: OpenHandsEvent[],\n): { event: StreamingDeltaEvent; index: number }[] => {\n const deltas: { event: StreamingDeltaEvent; index: number }[] = [];\n for (let index = uiEvents.length - 1; index >= 0; index -= 1) {\n const event = uiEvents[index];\n if (!isStreamingDeltaEvent(event)) {\n break;\n }\n if ((event.content?.length ?? 0) > 0) {\n deltas.unshift({ event, index });\n }\n }\n return deltas;\n};\n\n// Whether the streamed `segments` (in order) reconcile against `targetText`.\n// `lastMatchEnd` is the offset past the matched text, so callers can recover\n// any not-yet-streamed suffix. The SDK strips the finalized text, so it may\n// lack trailing whitespace the model streamed; the match tolerates that by\n// also trying the trailing-trimmed streamed text.\nconst matchStreamedSegments = (\n targetText: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n const streamedText = segments.join(\"\");\n for (const candidate of [streamedText, streamedText.trimEnd()]) {\n if (candidate && targetText.startsWith(candidate)) {\n return { matched: true, lastMatchEnd: candidate.length };\n }\n }\n // Segments may be interleaved with not-yet-streamed text; locate them in\n // order, trimming the last segment's trailing whitespace.\n const lastIndex = segments.length - 1;\n const searchSegments = segments.map((segment, index) =>\n index === lastIndex ? segment.trimEnd() : segment,\n );\n return findTextSegmentsInOrder(targetText, searchSegments);\n};\n\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const contentStreamingDeltas = getCurrentTurnContentDeltas(uiEvents);\n if (contentStreamingDeltas.length === 0) {\n return null;\n }\n\n const finalText = getFinalAgentText(finalEvent);\n if (!finalText) {\n return null;\n }\n\n const streamingSegments = contentStreamingDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n const { matched, lastMatchEnd } = matchStreamedSegments(\n finalText,\n streamingSegments,\n );\n if (!matched) {\n return null;\n }\n\n const nextUiEvents = [...uiEvents];\n const unstreamedSuffix = finalText.slice(lastMatchEnd);\n\n const lastDeltaIndex = contentStreamingDeltas.at(-1)?.index;\n const lastDelta =\n lastDeltaIndex === undefined ? undefined : nextUiEvents[lastDeltaIndex];\n if (\n unstreamedSuffix &&\n lastDeltaIndex !== undefined &&\n lastDelta &&\n isStreamingDeltaEvent(lastDelta)\n ) {\n nextUiEvents[lastDeltaIndex] = appendContentToStreamingDeltaEvent(\n lastDelta,\n unstreamedSuffix,\n );\n }\n\n // Intentionally return nextUiEvents WITHOUT appending finalEvent.\n // The last content-bearing streaming delta (possibly extended with\n // unstreamedSuffix above) becomes the canonical final rendered bubble for\n // this turn. Appending finalEvent here would display the assistant message\n // twice.\n return nextUiEvents;\n};\n\n/**\n * Reconcile the current turn's streaming delta when an intermediate\n * (tool-calling) `ActionEvent` arrives. With `stream=true` the step's\n * pre-tool-call text is streamed as delta `content`, then the action's\n * `thought` repeats it and the chat hoists that into its own message (see\n * `group-events.ts`), so the text would render twice (issue #1534).\n *\n * Unlike `finalizeStreamingDeltasInPlace` (which drops the final event and\n * keeps the delta), the action must stay — it owns the tool call. So the\n * streamed text is cleared from the delta instead, and the delta is kept only\n * to carry reasoning the action itself lacks (for many models the delta is the\n * sole reasoning carrier), otherwise dropped.\n *\n * Only the current step's trailing delta run is considered. Returns the updated\n * array, or `null` when there is nothing to reconcile.\n */\nconst supersedeStreamedThoughtWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const thoughtText = joinTextBlocks(action.thought);\n if (!thoughtText) {\n return null;\n }\n\n const contentDeltas = getTrailingContentDeltas(uiEvents);\n if (contentDeltas.length === 0) {\n return null;\n }\n\n const streamingSegments = contentDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n // Only strip when the streamed text is the action's rendered thought.\n if (!matchStreamedSegments(thoughtText, streamingSegments).matched) {\n return null;\n }\n\n // Keeping the delta's reasoning would duplicate the action's own \"Thinking\".\n const actionRendersReasoning = getReasoningContent(action).trim().length > 0;\n const indexesToStrip = new Set(contentDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n // Keep the delta only to render reasoning the action itself lacks.\n if (!actionRendersReasoning && event.reasoning_content) {\n nextUiEvents.push({ ...event, content: null });\n }\n });\n\n return nextUiEvents;\n};\n\n/**\n * Handles adding an event to the UI events array\n * Replaces actions with observations when they arrive (so UI shows observation instead of action)\n * Exception: ThinkAction is NOT replaced because the thought content is in the action, not in the observation\n *\n * ACPToolCallEvent merge: the SDK emits two events per ``tool_call_id`` — an\n * early ``started`` event (``pending`` / ``in_progress``) and one terminal\n * (completed / failed) event, the action->observation pair for a tool call.\n * Replace the started entry in place with the terminal one so a single card\n * updates from running to its result, exactly like an observation superseding\n * its action below.\n */\nexport const handleEventForUI = (\n event: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] => {\n const newUiEvents = [...uiEvents];\n\n if (isStreamingDeltaEvent(event)) {\n if (event.content === null && event.reasoning_content === null) {\n return newUiEvents;\n }\n\n const lastIndex = newUiEvents.length - 1;\n const lastEvent = newUiEvents[lastIndex];\n if (lastEvent && isStreamingDeltaEvent(lastEvent)) {\n newUiEvents[lastIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n return newUiEvents;\n }\n\n newUiEvents.push(event);\n return newUiEvents;\n }\n\n if (\n (isActionEvent(event) && event.action.kind === \"FinishAction\") ||\n (isMessageEvent(event) && event.source === \"agent\")\n ) {\n const finalizedUiEvents = finalizeStreamingDeltasInPlace(\n event,\n newUiEvents,\n );\n if (finalizedUiEvents) {\n // The reconciled streaming delta intentionally replaces this final event\n // for rendering. Today streamed agent responses only render text and\n // reasoning content; if final-event metadata such as activated\n // microagents becomes meaningful for streamed responses, add a rendered\n // wrapper that carries both the stable delta identity and that metadata.\n return finalizedUiEvents;\n }\n }\n\n // Intermediate tool-calling action whose thought was streamed: clear the\n // duplicated text from the delta (issue #1534). ThinkAction is excluded — its\n // thought renders through its own collapsible, not a hoisted thought.\n if (\n isActionEvent(event) &&\n event.action.kind !== \"FinishAction\" &&\n event.action.kind !== \"ThinkAction\"\n ) {\n const reconciledUiEvents = supersedeStreamedThoughtWithAction(\n event,\n newUiEvents,\n );\n if (reconciledUiEvents) {\n reconciledUiEvents.push(event);\n return reconciledUiEvents;\n }\n }\n\n if (isACPToolCallEvent(event)) {\n const existingIndex = newUiEvents.findIndex(\n (uiEvent) =>\n isACPToolCallEvent(uiEvent) &&\n uiEvent.tool_call_id === event.tool_call_id,\n );\n if (existingIndex !== -1) {\n newUiEvents[existingIndex] = event;\n } else {\n newUiEvents.push(event);\n }\n return newUiEvents;\n }\n\n if (isObservationEvent(event)) {\n // Don't add ThinkObservation at all - we keep the ThinkAction instead\n // The thought content is in the action, not the observation\n if (event.observation.kind === \"ThinkObservation\") {\n return newUiEvents;\n }\n\n // Don't add FinishObservation at all - we keep the FinishAction instead\n // Both contain the same message content, so we only need to display one\n // This also prevents duplicate messages when events arrive out of order due to React batching\n if (event.observation.kind === \"FinishObservation\") {\n return newUiEvents;\n }\n\n // Find and replace the corresponding action from uiEvents\n const actionIndex = newUiEvents.findIndex(\n (uiEvent) => uiEvent.id === event.action_id,\n );\n if (actionIndex !== -1) {\n newUiEvents[actionIndex] = event;\n } else {\n // Action not found in uiEvents, just add the observation\n newUiEvents.push(event);\n }\n } else {\n // For non-observation events, just add them to uiEvents\n newUiEvents.push(event);\n }\n\n return newUiEvents;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"handle-event-for-ui.cjs","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import {\n ActionEvent,\n ImageContent,\n OpenHandsEvent,\n TextContent,\n} from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { StreamingDeltaEvent } from \"#/types/agent-server/core/events/streaming-delta-event\";\nimport {\n getReasoningContent,\n splitInlineThink,\n} from \"#/components/conversation-events/chat/event-thought-helpers\";\n\nexport const mergeStreamingDeltaEvent = (\n incoming: StreamingDeltaEvent,\n existing: StreamingDeltaEvent,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${incoming.content ?? \"\"}` || null,\n reasoning_content:\n `${existing.reasoning_content ?? \"\"}${incoming.reasoning_content ?? \"\"}` ||\n null,\n});\n\n// Deltas only merge into one bubble when they share a sender; the planning flag\n// is the only discriminator (every delta has `source: \"agent\"`). Prevents a\n// planning-agent delta concatenating onto a main-agent one, misattributed (#1656).\nexport const isSameStreamingSender = (\n a: OpenHandsEvent & { isFromPlanningAgent?: boolean },\n b: OpenHandsEvent & { isFromPlanningAgent?: boolean },\n): boolean => Boolean(a.isFromPlanningAgent) === Boolean(b.isFromPlanningAgent);\n\nconst findLastUserMessageIndex = (events: OpenHandsEvent[]): number => {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n if (isMessageEvent(event) && event.source === \"user\") {\n return index;\n }\n }\n return -1;\n};\n\n// Join text blocks WITHOUT a separator: streaming deltas concatenate content\n// tokens directly with no separator between LLM content blocks, so using \"\\n\"\n// here would cause startsWith/findTextSegmentsInOrder to miss when reconciling\n// a multi-block message/thought against the already-rendered streaming delta.\nconst joinTextBlocks = (blocks: (TextContent | ImageContent)[]): string =>\n blocks\n .filter((block) => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\nconst findTextSegmentsInOrder = (\n text: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n let searchStart = 0;\n let lastMatchEnd = 0;\n\n for (const segment of segments) {\n const index = text.indexOf(segment, searchStart);\n if (index === -1) {\n return { matched: false, lastMatchEnd };\n }\n lastMatchEnd = index + segment.length;\n searchStart = lastMatchEnd;\n }\n\n return { matched: true, lastMatchEnd };\n};\n\n// Content-bearing streaming deltas of the current turn (after the last user\n// message) that share the final event's sender. Reasoning-only deltas are\n// excluded: reasoning renders in its own collapsed bubble and never overlaps\n// the message text being reconciled. Sender scoping matters because the main\n// and planning sockets share this event store — without it, one agent's final\n// event would strip the other agent's still-live streamed deltas (#1656).\nconst getCurrentTurnContentDeltas = (\n uiEvents: OpenHandsEvent[],\n finalEvent: OpenHandsEvent,\n): { event: StreamingDeltaEvent; index: number }[] => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n return uiEvents\n .map((event, index) => ({ event, index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n item.index > lastUserMessageIndex &&\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0 &&\n isSameStreamingSender(finalEvent, item.event),\n );\n};\n\n// The current step's streaming delta(s): the trailing run at the end of\n// `uiEvents`. Earlier steps' deltas are separated by their observations, so\n// this never folds an earlier step's delta into the current one.\nconst getTrailingDeltas = (\n uiEvents: OpenHandsEvent[],\n selects: (event: StreamingDeltaEvent) => boolean,\n): { event: StreamingDeltaEvent; index: number }[] => {\n const deltas: { event: StreamingDeltaEvent; index: number }[] = [];\n for (let index = uiEvents.length - 1; index >= 0; index -= 1) {\n const event = uiEvents[index];\n if (!isStreamingDeltaEvent(event)) {\n break;\n }\n if (selects(event)) {\n deltas.unshift({ event, index });\n }\n }\n return deltas;\n};\n\nconst getTrailingContentDeltas = (uiEvents: OpenHandsEvent[]) =>\n getTrailingDeltas(uiEvents, (event) => (event.content?.length ?? 0) > 0);\n\n// Sender-scoped: the main and planning sockets share this event store, so a\n// main-agent action must not strip the planning agent's live reasoning (#1656).\nconst getTrailingReasoningDeltas = (\n uiEvents: OpenHandsEvent[],\n finalEvent: OpenHandsEvent,\n) =>\n getTrailingDeltas(\n uiEvents,\n (event) =>\n Boolean(event.reasoning_content) &&\n isSameStreamingSender(finalEvent, event),\n );\n\n// Strip the streamed content deltas, keeping a delta only when it carries\n// reasoning the replacement itself won't render (many models stream reasoning\n// solely through the delta). Shared by the finalize and intermediate-action\n// reconciliation paths.\nconst supersedeStreamingContent = (\n uiEvents: OpenHandsEvent[],\n contentDeltas: { event: StreamingDeltaEvent; index: number }[],\n replacementRendersReasoning: boolean,\n): OpenHandsEvent[] => {\n const indexesToStrip = new Set(contentDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n\n // Keep the delta only to render reasoning the replacement itself lacks.\n if (!replacementRendersReasoning && event.reasoning_content) {\n nextUiEvents.push({ ...event, content: null });\n }\n });\n return nextUiEvents;\n};\n\n// Whether the streamed `segments` (in order) reconcile against `targetText`.\n// `lastMatchEnd` is the offset past the matched text, so callers can recover\n// any not-yet-streamed suffix. The SDK strips the finalized text, so tolerate\n// leading and trailing whitespace that appeared only in the stream.\nconst matchStreamedSegments = (\n targetText: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n const streamedText = segments.join(\"\");\n const candidates = new Set([\n streamedText,\n streamedText.trimEnd(),\n streamedText.trimStart(),\n streamedText.trim(),\n ]);\n for (const candidate of candidates) {\n if (candidate && targetText.startsWith(candidate)) {\n return { matched: true, lastMatchEnd: candidate.length };\n }\n }\n // Segments may be interleaved with not-yet-streamed text; locate them in\n // order, trimming the last segment's trailing whitespace.\n const lastIndex = segments.length - 1;\n const searchSegments = segments.map((segment, index) =>\n index === lastIndex ? segment.trimEnd() : segment,\n );\n return findTextSegmentsInOrder(targetText, searchSegments);\n};\n\n// Whether the finalized event renders its own reasoning: an ActionEvent via\n// reasoning_content/thinking_blocks, an agent MessageEvent via an inline\n// <think> block in its content. Decides if a replaced delta's reasoning must\n// be preserved separately.\nconst eventRendersReasoning = (event: OpenHandsEvent): boolean => {\n if (isActionEvent(event)) {\n return getReasoningContent(event).trim().length > 0;\n }\n\n if (isMessageEvent(event) && event.source === \"agent\") {\n return (\n splitInlineThink(joinTextBlocks(event.llm_message.content)).reasoning\n .length > 0\n );\n }\n\n return false;\n};\n\n// The final MessageEvent/FinishAction is authoritative for the turn's text. Drop\n// the provisional streamed deltas and render the canonical final event instead,\n// so the message is rendered exactly once (never holey or duplicated) and its\n// metadata — critic_result, activated_microagents — renders too. Stream-only\n// reasoning is preserved. Returns null when there is no streamed content to\n// reconcile, leaving the caller to append the final event normally.\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const contentStreamingDeltas = getCurrentTurnContentDeltas(\n uiEvents,\n finalEvent,\n );\n if (contentStreamingDeltas.length === 0) {\n return null;\n }\n\n const nextUiEvents = supersedeStreamingContent(\n uiEvents,\n contentStreamingDeltas,\n eventRendersReasoning(finalEvent),\n );\n nextUiEvents.push(finalEvent);\n return nextUiEvents;\n};\n\n/**\n * Reconcile the current turn's streaming delta when an intermediate\n * (tool-calling) `ActionEvent` arrives. With `stream=true` the step's\n * pre-tool-call text is streamed as delta `content`, then the action's\n * `thought` repeats it and the chat hoists that into its own message (see\n * `group-events.ts`), so the text would render twice (issue #1534).\n *\n * The action must stay because it owns the tool call. The streamed text is\n * cleared from the delta, and the delta is kept only to carry reasoning the\n * action itself lacks (for many models the delta is the sole reasoning\n * carrier), otherwise dropped.\n *\n * Only the current step's trailing delta run is considered. Returns the updated\n * array, or `null` when there is nothing to reconcile.\n */\nconst supersedeStreamedThoughtWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const thoughtText = joinTextBlocks(action.thought);\n if (!thoughtText) {\n return null;\n }\n\n const contentDeltas = getTrailingContentDeltas(uiEvents);\n if (contentDeltas.length === 0) {\n return null;\n }\n\n const streamingSegments = contentDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n // Only strip when the streamed text is the action's rendered thought.\n if (!matchStreamedSegments(thoughtText, streamingSegments).matched) {\n return null;\n }\n\n // Keeping the delta's reasoning would duplicate the action's own \"Thinking\".\n return supersedeStreamingContent(\n uiEvents,\n contentDeltas,\n getReasoningContent(action).trim().length > 0,\n );\n};\n\n// Drop the current step's streamed reasoning when the action renders that\n// reasoning itself, so \"Thinking\" renders once. Unlike\n// `supersedeStreamedThoughtWithAction` this does not require the streamed text\n// to match the thought, which models streaming reasoning-only deltas never do.\nconst supersedeStreamedReasoningWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n if (getReasoningContent(action).trim().length === 0) {\n return null;\n }\n\n const reasoningDeltas = getTrailingReasoningDeltas(uiEvents, action);\n if (reasoningDeltas.length === 0) {\n return null;\n }\n\n const indexesToStrip = new Set(reasoningDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n\n // Keep the delta only for streamed text the action itself lacks.\n if (event.content) {\n nextUiEvents.push({ ...event, reasoning_content: null });\n }\n });\n return nextUiEvents;\n};\n\n/**\n * Handles adding an event to the UI events array\n * Replaces actions with observations when they arrive (so UI shows observation instead of action)\n * Exception: ThinkAction is NOT replaced because the thought content is in the action, not in the observation\n *\n * ACPToolCallEvent merge: the SDK emits two events per ``tool_call_id`` — an\n * early ``started`` event (``pending`` / ``in_progress``) and one terminal\n * (completed / failed) event, the action->observation pair for a tool call.\n * Replace the started entry in place with the terminal one so a single card\n * updates from running to its result, exactly like an observation superseding\n * its action below.\n */\nexport const handleEventForUI = (\n event: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] => {\n const newUiEvents = [...uiEvents];\n\n if (isStreamingDeltaEvent(event)) {\n if (event.content === null && event.reasoning_content === null) {\n return newUiEvents;\n }\n\n const lastIndex = newUiEvents.length - 1;\n const lastEvent = newUiEvents[lastIndex];\n if (\n lastEvent &&\n isStreamingDeltaEvent(lastEvent) &&\n isSameStreamingSender(event, lastEvent)\n ) {\n newUiEvents[lastIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n return newUiEvents;\n }\n\n newUiEvents.push(event);\n return newUiEvents;\n }\n\n if (\n (isActionEvent(event) && event.action.kind === \"FinishAction\") ||\n (isMessageEvent(event) && event.source === \"agent\")\n ) {\n const finalizedUiEvents = finalizeStreamingDeltasInPlace(\n event,\n newUiEvents,\n );\n if (finalizedUiEvents) {\n return finalizedUiEvents;\n }\n }\n\n // Intermediate tool-calling action whose thought was streamed: clear the\n // duplicated text from the delta (issue #1534). ThinkAction is excluded — its\n // thought renders through its own collapsible, not a hoisted thought.\n if (\n isActionEvent(event) &&\n event.action.kind !== \"FinishAction\" &&\n event.action.kind !== \"ThinkAction\"\n ) {\n const reconciledUiEvents =\n supersedeStreamedThoughtWithAction(event, newUiEvents) ??\n supersedeStreamedReasoningWithAction(event, newUiEvents);\n if (reconciledUiEvents) {\n reconciledUiEvents.push(event);\n return reconciledUiEvents;\n }\n }\n\n if (isACPToolCallEvent(event)) {\n const existingIndex = newUiEvents.findIndex(\n (uiEvent) =>\n isACPToolCallEvent(uiEvent) &&\n uiEvent.tool_call_id === event.tool_call_id,\n );\n if (existingIndex !== -1) {\n newUiEvents[existingIndex] = event;\n } else {\n newUiEvents.push(event);\n }\n return newUiEvents;\n }\n\n if (isObservationEvent(event)) {\n // Don't add ThinkObservation at all - we keep the ThinkAction instead\n // The thought content is in the action, not the observation\n if (event.observation.kind === \"ThinkObservation\") {\n return newUiEvents;\n }\n\n // Don't add FinishObservation at all - we keep the FinishAction instead\n // Both contain the same message content, so we only need to display one\n // This also prevents duplicate messages when events arrive out of order due to React batching\n if (event.observation.kind === \"FinishObservation\") {\n return newUiEvents;\n }\n\n // Find and replace the corresponding action from uiEvents\n const actionIndex = newUiEvents.findIndex(\n (uiEvent) => uiEvent.id === event.action_id,\n );\n if (actionIndex !== -1) {\n newUiEvents[actionIndex] = event;\n } else {\n // Action not found in uiEvents, just add the observation\n newUiEvents.push(event);\n }\n } else {\n // For non-observation events, just add them to uiEvents\n newUiEvents.push(event);\n }\n\n return newUiEvents;\n};\n"],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  import { OpenHandsEvent } from "#/types/agent-server/core";
2
2
  import { StreamingDeltaEvent } from "#/types/agent-server/core/events/streaming-delta-event";
3
3
  export declare const mergeStreamingDeltaEvent: (incoming: StreamingDeltaEvent, existing: StreamingDeltaEvent) => StreamingDeltaEvent;
4
+ export declare const isSameStreamingSender: (a: OpenHandsEvent & {
5
+ isFromPlanningAgent?: boolean;
6
+ }, b: OpenHandsEvent & {
7
+ isFromPlanningAgent?: boolean;
8
+ }) => boolean;
4
9
  /**
5
10
  * Handles adding an event to the UI events array
6
11
  * Replaces actions with observations when they arrive (so UI shows observation instead of action)
@@ -1,20 +1,17 @@
1
1
  import { isACPToolCallEvent as e, isActionEvent as t, isMessageEvent as n, isObservationEvent as r, isStreamingDeltaEvent as i } from "../types/agent-server/type-guards.js";
2
- import { getReasoningContent as a } from "../components/conversation-events/chat/event-thought-helpers.js";
2
+ import { getReasoningContent as a, splitInlineThink as o } from "../components/conversation-events/chat/event-thought-helpers.js";
3
3
  //#region src/utils/handle-event-for-ui.ts
4
- var o = (e, t) => ({
4
+ var s = (e, t) => ({
5
5
  ...t,
6
6
  content: `${t.content ?? ""}${e.content ?? ""}` || null,
7
7
  reasoning_content: `${t.reasoning_content ?? ""}${e.reasoning_content ?? ""}` || null
8
- }), s = (e, t) => ({
9
- ...e,
10
- content: `${e.content ?? ""}${t}` || null
11
- }), c = (e) => {
8
+ }), c = (e, t) => !!e.isFromPlanningAgent == !!t.isFromPlanningAgent, l = (e) => {
12
9
  for (let t = e.length - 1; t >= 0; --t) {
13
10
  let r = e[t];
14
11
  if (n(r) && r.source === "user") return t;
15
12
  }
16
13
  return -1;
17
- }, l = (e) => e.filter((e) => e.type === "text").map((e) => e.text).join(""), u = (e) => t(e) && e.action.kind === "FinishAction" ? e.action.message : n(e) && e.source === "agent" ? l(e.llm_message.content) : null, d = (e, t) => {
14
+ }, u = (e) => e.filter((e) => e.type === "text").map((e) => e.text).join(""), d = (e, t) => {
18
15
  let n = 0, r = 0;
19
16
  for (let i of t) {
20
17
  let t = e.indexOf(i, n);
@@ -28,83 +25,100 @@ var o = (e, t) => ({
28
25
  matched: !0,
29
26
  lastMatchEnd: r
30
27
  };
31
- }, f = (e) => {
32
- let t = c(e);
28
+ }, f = (e, t) => {
29
+ let n = l(e);
33
30
  return e.map((e, t) => ({
34
31
  event: e,
35
32
  index: t
36
- })).filter((e) => e.index > t && i(e.event) && (e.event.content?.length ?? 0) > 0);
37
- }, p = (e) => {
38
- let t = [];
39
- for (let n = e.length - 1; n >= 0; --n) {
40
- let r = e[n];
41
- if (!i(r)) break;
42
- (r.content?.length ?? 0) > 0 && t.unshift({
43
- event: r,
44
- index: n
33
+ })).filter((e) => e.index > n && i(e.event) && (e.event.content?.length ?? 0) > 0 && c(t, e.event));
34
+ }, p = (e, t) => {
35
+ let n = [];
36
+ for (let r = e.length - 1; r >= 0; --r) {
37
+ let a = e[r];
38
+ if (!i(a)) break;
39
+ t(a) && n.unshift({
40
+ event: a,
41
+ index: r
45
42
  });
46
43
  }
47
- return t;
48
- }, m = (e, t) => {
49
- let n = t.join("");
50
- for (let t of [n, n.trimEnd()]) if (t && e.startsWith(t)) return {
44
+ return n;
45
+ }, m = (e) => p(e, (e) => (e.content?.length ?? 0) > 0), h = (e, t) => p(e, (e) => !!e.reasoning_content && c(t, e)), g = (e, t, n) => {
46
+ let r = new Set(t.map(({ index: e }) => e)), a = [];
47
+ return e.forEach((e, t) => {
48
+ if (!r.has(t) || !i(e)) {
49
+ a.push(e);
50
+ return;
51
+ }
52
+ !n && e.reasoning_content && a.push({
53
+ ...e,
54
+ content: null
55
+ });
56
+ }), a;
57
+ }, _ = (e, t) => {
58
+ let n = t.join(""), r = new Set([
59
+ n,
60
+ n.trimEnd(),
61
+ n.trimStart(),
62
+ n.trim()
63
+ ]);
64
+ for (let t of r) if (t && e.startsWith(t)) return {
51
65
  matched: !0,
52
66
  lastMatchEnd: t.length
53
67
  };
54
- let r = t.length - 1;
55
- return d(e, t.map((e, t) => t === r ? e.trimEnd() : e));
56
- }, h = (e, t) => {
57
- let n = f(t);
68
+ let i = t.length - 1;
69
+ return d(e, t.map((e, t) => t === i ? e.trimEnd() : e));
70
+ }, v = (e) => t(e) ? a(e).trim().length > 0 : n(e) && e.source === "agent" ? o(u(e.llm_message.content)).reasoning.length > 0 : !1, y = (e, t) => {
71
+ let n = f(t, e);
58
72
  if (n.length === 0) return null;
59
- let r = u(e);
60
- if (!r) return null;
61
- let { matched: a, lastMatchEnd: o } = m(r, n.map(({ event: e }) => e.content ?? ""));
62
- if (!a) return null;
63
- let c = [...t], l = r.slice(o), d = n.at(-1)?.index, p = d === void 0 ? void 0 : c[d];
64
- return l && d !== void 0 && p && i(p) && (c[d] = s(p, l)), c;
65
- }, g = (e, t) => {
66
- let n = l(e.thought);
73
+ let r = g(t, n, v(e));
74
+ return r.push(e), r;
75
+ }, b = (e, t) => {
76
+ let n = u(e.thought);
67
77
  if (!n) return null;
68
- let r = p(t);
69
- if (r.length === 0 || !m(n, r.map(({ event: e }) => e.content ?? "")).matched) return null;
70
- let o = a(e).trim().length > 0, s = new Set(r.map(({ index: e }) => e)), c = [];
78
+ let r = m(t);
79
+ return r.length === 0 || !_(n, r.map(({ event: e }) => e.content ?? "")).matched ? null : g(t, r, a(e).trim().length > 0);
80
+ }, x = (e, t) => {
81
+ if (a(e).trim().length === 0) return null;
82
+ let n = h(t, e);
83
+ if (n.length === 0) return null;
84
+ let r = new Set(n.map(({ index: e }) => e)), o = [];
71
85
  return t.forEach((e, t) => {
72
- if (!s.has(t) || !i(e)) {
73
- c.push(e);
86
+ if (!r.has(t) || !i(e)) {
87
+ o.push(e);
74
88
  return;
75
89
  }
76
- !o && e.reasoning_content && c.push({
90
+ e.content && o.push({
77
91
  ...e,
78
- content: null
92
+ reasoning_content: null
79
93
  });
80
- }), c;
81
- }, _ = (a, s) => {
82
- let c = [...s];
94
+ }), o;
95
+ }, S = (a, o) => {
96
+ let l = [...o];
83
97
  if (i(a)) {
84
- if (a.content === null && a.reasoning_content === null) return c;
85
- let e = c.length - 1, t = c[e];
86
- return t && i(t) ? (c[e] = o(a, t), c) : (c.push(a), c);
98
+ if (a.content === null && a.reasoning_content === null) return l;
99
+ let e = l.length - 1, t = l[e];
100
+ return t && i(t) && c(a, t) ? (l[e] = s(a, t), l) : (l.push(a), l);
87
101
  }
88
102
  if (t(a) && a.action.kind === "FinishAction" || n(a) && a.source === "agent") {
89
- let e = h(a, c);
103
+ let e = y(a, l);
90
104
  if (e) return e;
91
105
  }
92
106
  if (t(a) && a.action.kind !== "FinishAction" && a.action.kind !== "ThinkAction") {
93
- let e = g(a, c);
107
+ let e = b(a, l) ?? x(a, l);
94
108
  if (e) return e.push(a), e;
95
109
  }
96
110
  if (e(a)) {
97
- let t = c.findIndex((t) => e(t) && t.tool_call_id === a.tool_call_id);
98
- return t === -1 ? c.push(a) : c[t] = a, c;
111
+ let t = l.findIndex((t) => e(t) && t.tool_call_id === a.tool_call_id);
112
+ return t === -1 ? l.push(a) : l[t] = a, l;
99
113
  }
100
114
  if (r(a)) {
101
- if (a.observation.kind === "ThinkObservation" || a.observation.kind === "FinishObservation") return c;
102
- let e = c.findIndex((e) => e.id === a.action_id);
103
- e === -1 ? c.push(a) : c[e] = a;
104
- } else c.push(a);
105
- return c;
115
+ if (a.observation.kind === "ThinkObservation" || a.observation.kind === "FinishObservation") return l;
116
+ let e = l.findIndex((e) => e.id === a.action_id);
117
+ e === -1 ? l.push(a) : l[e] = a;
118
+ } else l.push(a);
119
+ return l;
106
120
  };
107
121
  //#endregion
108
- export { _ as handleEventForUI, o as mergeStreamingDeltaEvent };
122
+ export { S as handleEventForUI, c as isSameStreamingSender, s as mergeStreamingDeltaEvent };
109
123
 
110
124
  //# sourceMappingURL=handle-event-for-ui.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"handle-event-for-ui.js","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import {\n ActionEvent,\n ImageContent,\n OpenHandsEvent,\n TextContent,\n} from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { StreamingDeltaEvent } from \"#/types/agent-server/core/events/streaming-delta-event\";\nimport { getReasoningContent } from \"#/components/conversation-events/chat/event-thought-helpers\";\n\nexport const mergeStreamingDeltaEvent = (\n incoming: StreamingDeltaEvent,\n existing: StreamingDeltaEvent,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${incoming.content ?? \"\"}` || null,\n reasoning_content:\n `${existing.reasoning_content ?? \"\"}${incoming.reasoning_content ?? \"\"}` ||\n null,\n});\n\nconst appendContentToStreamingDeltaEvent = (\n existing: StreamingDeltaEvent,\n content: string,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${content}` || null,\n});\n\nconst findLastUserMessageIndex = (events: OpenHandsEvent[]): number => {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n if (isMessageEvent(event) && event.source === \"user\") {\n return index;\n }\n }\n return -1;\n};\n\n// Join text blocks WITHOUT a separator: streaming deltas concatenate content\n// tokens directly with no separator between LLM content blocks, so using \"\\n\"\n// here would cause startsWith/findTextSegmentsInOrder to miss when reconciling\n// a multi-block message/thought against the already-rendered streaming delta.\nconst joinTextBlocks = (blocks: (TextContent | ImageContent)[]): string =>\n blocks\n .filter((block) => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\nconst getFinalAgentText = (event: OpenHandsEvent): string | null => {\n if (isActionEvent(event) && event.action.kind === \"FinishAction\") {\n return event.action.message;\n }\n\n if (isMessageEvent(event) && event.source === \"agent\") {\n return joinTextBlocks(event.llm_message.content);\n }\n\n return null;\n};\n\nconst findTextSegmentsInOrder = (\n text: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n let searchStart = 0;\n let lastMatchEnd = 0;\n\n for (const segment of segments) {\n const index = text.indexOf(segment, searchStart);\n if (index === -1) {\n return { matched: false, lastMatchEnd };\n }\n lastMatchEnd = index + segment.length;\n searchStart = lastMatchEnd;\n }\n\n return { matched: true, lastMatchEnd };\n};\n\n// Content-bearing streaming deltas of the current turn (after the last user\n// message). Reasoning-only deltas are excluded: reasoning renders in its own\n// collapsed bubble and never overlaps the message text being reconciled.\nconst getCurrentTurnContentDeltas = (\n uiEvents: OpenHandsEvent[],\n): { event: StreamingDeltaEvent; index: number }[] => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n return uiEvents\n .map((event, index) => ({ event, index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n item.index > lastUserMessageIndex &&\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0,\n );\n};\n\n// The current step's streaming delta(s): the trailing run at the end of\n// `uiEvents`. Earlier steps' deltas are separated by their observations, so\n// this never folds an earlier step's delta into the current one.\nconst getTrailingContentDeltas = (\n uiEvents: OpenHandsEvent[],\n): { event: StreamingDeltaEvent; index: number }[] => {\n const deltas: { event: StreamingDeltaEvent; index: number }[] = [];\n for (let index = uiEvents.length - 1; index >= 0; index -= 1) {\n const event = uiEvents[index];\n if (!isStreamingDeltaEvent(event)) {\n break;\n }\n if ((event.content?.length ?? 0) > 0) {\n deltas.unshift({ event, index });\n }\n }\n return deltas;\n};\n\n// Whether the streamed `segments` (in order) reconcile against `targetText`.\n// `lastMatchEnd` is the offset past the matched text, so callers can recover\n// any not-yet-streamed suffix. The SDK strips the finalized text, so it may\n// lack trailing whitespace the model streamed; the match tolerates that by\n// also trying the trailing-trimmed streamed text.\nconst matchStreamedSegments = (\n targetText: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n const streamedText = segments.join(\"\");\n for (const candidate of [streamedText, streamedText.trimEnd()]) {\n if (candidate && targetText.startsWith(candidate)) {\n return { matched: true, lastMatchEnd: candidate.length };\n }\n }\n // Segments may be interleaved with not-yet-streamed text; locate them in\n // order, trimming the last segment's trailing whitespace.\n const lastIndex = segments.length - 1;\n const searchSegments = segments.map((segment, index) =>\n index === lastIndex ? segment.trimEnd() : segment,\n );\n return findTextSegmentsInOrder(targetText, searchSegments);\n};\n\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const contentStreamingDeltas = getCurrentTurnContentDeltas(uiEvents);\n if (contentStreamingDeltas.length === 0) {\n return null;\n }\n\n const finalText = getFinalAgentText(finalEvent);\n if (!finalText) {\n return null;\n }\n\n const streamingSegments = contentStreamingDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n const { matched, lastMatchEnd } = matchStreamedSegments(\n finalText,\n streamingSegments,\n );\n if (!matched) {\n return null;\n }\n\n const nextUiEvents = [...uiEvents];\n const unstreamedSuffix = finalText.slice(lastMatchEnd);\n\n const lastDeltaIndex = contentStreamingDeltas.at(-1)?.index;\n const lastDelta =\n lastDeltaIndex === undefined ? undefined : nextUiEvents[lastDeltaIndex];\n if (\n unstreamedSuffix &&\n lastDeltaIndex !== undefined &&\n lastDelta &&\n isStreamingDeltaEvent(lastDelta)\n ) {\n nextUiEvents[lastDeltaIndex] = appendContentToStreamingDeltaEvent(\n lastDelta,\n unstreamedSuffix,\n );\n }\n\n // Intentionally return nextUiEvents WITHOUT appending finalEvent.\n // The last content-bearing streaming delta (possibly extended with\n // unstreamedSuffix above) becomes the canonical final rendered bubble for\n // this turn. Appending finalEvent here would display the assistant message\n // twice.\n return nextUiEvents;\n};\n\n/**\n * Reconcile the current turn's streaming delta when an intermediate\n * (tool-calling) `ActionEvent` arrives. With `stream=true` the step's\n * pre-tool-call text is streamed as delta `content`, then the action's\n * `thought` repeats it and the chat hoists that into its own message (see\n * `group-events.ts`), so the text would render twice (issue #1534).\n *\n * Unlike `finalizeStreamingDeltasInPlace` (which drops the final event and\n * keeps the delta), the action must stay — it owns the tool call. So the\n * streamed text is cleared from the delta instead, and the delta is kept only\n * to carry reasoning the action itself lacks (for many models the delta is the\n * sole reasoning carrier), otherwise dropped.\n *\n * Only the current step's trailing delta run is considered. Returns the updated\n * array, or `null` when there is nothing to reconcile.\n */\nconst supersedeStreamedThoughtWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const thoughtText = joinTextBlocks(action.thought);\n if (!thoughtText) {\n return null;\n }\n\n const contentDeltas = getTrailingContentDeltas(uiEvents);\n if (contentDeltas.length === 0) {\n return null;\n }\n\n const streamingSegments = contentDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n // Only strip when the streamed text is the action's rendered thought.\n if (!matchStreamedSegments(thoughtText, streamingSegments).matched) {\n return null;\n }\n\n // Keeping the delta's reasoning would duplicate the action's own \"Thinking\".\n const actionRendersReasoning = getReasoningContent(action).trim().length > 0;\n const indexesToStrip = new Set(contentDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n // Keep the delta only to render reasoning the action itself lacks.\n if (!actionRendersReasoning && event.reasoning_content) {\n nextUiEvents.push({ ...event, content: null });\n }\n });\n\n return nextUiEvents;\n};\n\n/**\n * Handles adding an event to the UI events array\n * Replaces actions with observations when they arrive (so UI shows observation instead of action)\n * Exception: ThinkAction is NOT replaced because the thought content is in the action, not in the observation\n *\n * ACPToolCallEvent merge: the SDK emits two events per ``tool_call_id`` — an\n * early ``started`` event (``pending`` / ``in_progress``) and one terminal\n * (completed / failed) event, the action->observation pair for a tool call.\n * Replace the started entry in place with the terminal one so a single card\n * updates from running to its result, exactly like an observation superseding\n * its action below.\n */\nexport const handleEventForUI = (\n event: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] => {\n const newUiEvents = [...uiEvents];\n\n if (isStreamingDeltaEvent(event)) {\n if (event.content === null && event.reasoning_content === null) {\n return newUiEvents;\n }\n\n const lastIndex = newUiEvents.length - 1;\n const lastEvent = newUiEvents[lastIndex];\n if (lastEvent && isStreamingDeltaEvent(lastEvent)) {\n newUiEvents[lastIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n return newUiEvents;\n }\n\n newUiEvents.push(event);\n return newUiEvents;\n }\n\n if (\n (isActionEvent(event) && event.action.kind === \"FinishAction\") ||\n (isMessageEvent(event) && event.source === \"agent\")\n ) {\n const finalizedUiEvents = finalizeStreamingDeltasInPlace(\n event,\n newUiEvents,\n );\n if (finalizedUiEvents) {\n // The reconciled streaming delta intentionally replaces this final event\n // for rendering. Today streamed agent responses only render text and\n // reasoning content; if final-event metadata such as activated\n // microagents becomes meaningful for streamed responses, add a rendered\n // wrapper that carries both the stable delta identity and that metadata.\n return finalizedUiEvents;\n }\n }\n\n // Intermediate tool-calling action whose thought was streamed: clear the\n // duplicated text from the delta (issue #1534). ThinkAction is excluded — its\n // thought renders through its own collapsible, not a hoisted thought.\n if (\n isActionEvent(event) &&\n event.action.kind !== \"FinishAction\" &&\n event.action.kind !== \"ThinkAction\"\n ) {\n const reconciledUiEvents = supersedeStreamedThoughtWithAction(\n event,\n newUiEvents,\n );\n if (reconciledUiEvents) {\n reconciledUiEvents.push(event);\n return reconciledUiEvents;\n }\n }\n\n if (isACPToolCallEvent(event)) {\n const existingIndex = newUiEvents.findIndex(\n (uiEvent) =>\n isACPToolCallEvent(uiEvent) &&\n uiEvent.tool_call_id === event.tool_call_id,\n );\n if (existingIndex !== -1) {\n newUiEvents[existingIndex] = event;\n } else {\n newUiEvents.push(event);\n }\n return newUiEvents;\n }\n\n if (isObservationEvent(event)) {\n // Don't add ThinkObservation at all - we keep the ThinkAction instead\n // The thought content is in the action, not the observation\n if (event.observation.kind === \"ThinkObservation\") {\n return newUiEvents;\n }\n\n // Don't add FinishObservation at all - we keep the FinishAction instead\n // Both contain the same message content, so we only need to display one\n // This also prevents duplicate messages when events arrive out of order due to React batching\n if (event.observation.kind === \"FinishObservation\") {\n return newUiEvents;\n }\n\n // Find and replace the corresponding action from uiEvents\n const actionIndex = newUiEvents.findIndex(\n (uiEvent) => uiEvent.id === event.action_id,\n );\n if (actionIndex !== -1) {\n newUiEvents[actionIndex] = event;\n } else {\n // Action not found in uiEvents, just add the observation\n newUiEvents.push(event);\n }\n } else {\n // For non-observation events, just add them to uiEvents\n newUiEvents.push(event);\n }\n\n return newUiEvents;\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"handle-event-for-ui.js","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import {\n ActionEvent,\n ImageContent,\n OpenHandsEvent,\n TextContent,\n} from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { StreamingDeltaEvent } from \"#/types/agent-server/core/events/streaming-delta-event\";\nimport {\n getReasoningContent,\n splitInlineThink,\n} from \"#/components/conversation-events/chat/event-thought-helpers\";\n\nexport const mergeStreamingDeltaEvent = (\n incoming: StreamingDeltaEvent,\n existing: StreamingDeltaEvent,\n): StreamingDeltaEvent => ({\n ...existing,\n content: `${existing.content ?? \"\"}${incoming.content ?? \"\"}` || null,\n reasoning_content:\n `${existing.reasoning_content ?? \"\"}${incoming.reasoning_content ?? \"\"}` ||\n null,\n});\n\n// Deltas only merge into one bubble when they share a sender; the planning flag\n// is the only discriminator (every delta has `source: \"agent\"`). Prevents a\n// planning-agent delta concatenating onto a main-agent one, misattributed (#1656).\nexport const isSameStreamingSender = (\n a: OpenHandsEvent & { isFromPlanningAgent?: boolean },\n b: OpenHandsEvent & { isFromPlanningAgent?: boolean },\n): boolean => Boolean(a.isFromPlanningAgent) === Boolean(b.isFromPlanningAgent);\n\nconst findLastUserMessageIndex = (events: OpenHandsEvent[]): number => {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index];\n if (isMessageEvent(event) && event.source === \"user\") {\n return index;\n }\n }\n return -1;\n};\n\n// Join text blocks WITHOUT a separator: streaming deltas concatenate content\n// tokens directly with no separator between LLM content blocks, so using \"\\n\"\n// here would cause startsWith/findTextSegmentsInOrder to miss when reconciling\n// a multi-block message/thought against the already-rendered streaming delta.\nconst joinTextBlocks = (blocks: (TextContent | ImageContent)[]): string =>\n blocks\n .filter((block) => block.type === \"text\")\n .map((block) => block.text)\n .join(\"\");\n\nconst findTextSegmentsInOrder = (\n text: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n let searchStart = 0;\n let lastMatchEnd = 0;\n\n for (const segment of segments) {\n const index = text.indexOf(segment, searchStart);\n if (index === -1) {\n return { matched: false, lastMatchEnd };\n }\n lastMatchEnd = index + segment.length;\n searchStart = lastMatchEnd;\n }\n\n return { matched: true, lastMatchEnd };\n};\n\n// Content-bearing streaming deltas of the current turn (after the last user\n// message) that share the final event's sender. Reasoning-only deltas are\n// excluded: reasoning renders in its own collapsed bubble and never overlaps\n// the message text being reconciled. Sender scoping matters because the main\n// and planning sockets share this event store — without it, one agent's final\n// event would strip the other agent's still-live streamed deltas (#1656).\nconst getCurrentTurnContentDeltas = (\n uiEvents: OpenHandsEvent[],\n finalEvent: OpenHandsEvent,\n): { event: StreamingDeltaEvent; index: number }[] => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n return uiEvents\n .map((event, index) => ({ event, index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n item.index > lastUserMessageIndex &&\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0 &&\n isSameStreamingSender(finalEvent, item.event),\n );\n};\n\n// The current step's streaming delta(s): the trailing run at the end of\n// `uiEvents`. Earlier steps' deltas are separated by their observations, so\n// this never folds an earlier step's delta into the current one.\nconst getTrailingDeltas = (\n uiEvents: OpenHandsEvent[],\n selects: (event: StreamingDeltaEvent) => boolean,\n): { event: StreamingDeltaEvent; index: number }[] => {\n const deltas: { event: StreamingDeltaEvent; index: number }[] = [];\n for (let index = uiEvents.length - 1; index >= 0; index -= 1) {\n const event = uiEvents[index];\n if (!isStreamingDeltaEvent(event)) {\n break;\n }\n if (selects(event)) {\n deltas.unshift({ event, index });\n }\n }\n return deltas;\n};\n\nconst getTrailingContentDeltas = (uiEvents: OpenHandsEvent[]) =>\n getTrailingDeltas(uiEvents, (event) => (event.content?.length ?? 0) > 0);\n\n// Sender-scoped: the main and planning sockets share this event store, so a\n// main-agent action must not strip the planning agent's live reasoning (#1656).\nconst getTrailingReasoningDeltas = (\n uiEvents: OpenHandsEvent[],\n finalEvent: OpenHandsEvent,\n) =>\n getTrailingDeltas(\n uiEvents,\n (event) =>\n Boolean(event.reasoning_content) &&\n isSameStreamingSender(finalEvent, event),\n );\n\n// Strip the streamed content deltas, keeping a delta only when it carries\n// reasoning the replacement itself won't render (many models stream reasoning\n// solely through the delta). Shared by the finalize and intermediate-action\n// reconciliation paths.\nconst supersedeStreamingContent = (\n uiEvents: OpenHandsEvent[],\n contentDeltas: { event: StreamingDeltaEvent; index: number }[],\n replacementRendersReasoning: boolean,\n): OpenHandsEvent[] => {\n const indexesToStrip = new Set(contentDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n\n // Keep the delta only to render reasoning the replacement itself lacks.\n if (!replacementRendersReasoning && event.reasoning_content) {\n nextUiEvents.push({ ...event, content: null });\n }\n });\n return nextUiEvents;\n};\n\n// Whether the streamed `segments` (in order) reconcile against `targetText`.\n// `lastMatchEnd` is the offset past the matched text, so callers can recover\n// any not-yet-streamed suffix. The SDK strips the finalized text, so tolerate\n// leading and trailing whitespace that appeared only in the stream.\nconst matchStreamedSegments = (\n targetText: string,\n segments: string[],\n): { matched: boolean; lastMatchEnd: number } => {\n const streamedText = segments.join(\"\");\n const candidates = new Set([\n streamedText,\n streamedText.trimEnd(),\n streamedText.trimStart(),\n streamedText.trim(),\n ]);\n for (const candidate of candidates) {\n if (candidate && targetText.startsWith(candidate)) {\n return { matched: true, lastMatchEnd: candidate.length };\n }\n }\n // Segments may be interleaved with not-yet-streamed text; locate them in\n // order, trimming the last segment's trailing whitespace.\n const lastIndex = segments.length - 1;\n const searchSegments = segments.map((segment, index) =>\n index === lastIndex ? segment.trimEnd() : segment,\n );\n return findTextSegmentsInOrder(targetText, searchSegments);\n};\n\n// Whether the finalized event renders its own reasoning: an ActionEvent via\n// reasoning_content/thinking_blocks, an agent MessageEvent via an inline\n// <think> block in its content. Decides if a replaced delta's reasoning must\n// be preserved separately.\nconst eventRendersReasoning = (event: OpenHandsEvent): boolean => {\n if (isActionEvent(event)) {\n return getReasoningContent(event).trim().length > 0;\n }\n\n if (isMessageEvent(event) && event.source === \"agent\") {\n return (\n splitInlineThink(joinTextBlocks(event.llm_message.content)).reasoning\n .length > 0\n );\n }\n\n return false;\n};\n\n// The final MessageEvent/FinishAction is authoritative for the turn's text. Drop\n// the provisional streamed deltas and render the canonical final event instead,\n// so the message is rendered exactly once (never holey or duplicated) and its\n// metadata — critic_result, activated_microagents — renders too. Stream-only\n// reasoning is preserved. Returns null when there is no streamed content to\n// reconcile, leaving the caller to append the final event normally.\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const contentStreamingDeltas = getCurrentTurnContentDeltas(\n uiEvents,\n finalEvent,\n );\n if (contentStreamingDeltas.length === 0) {\n return null;\n }\n\n const nextUiEvents = supersedeStreamingContent(\n uiEvents,\n contentStreamingDeltas,\n eventRendersReasoning(finalEvent),\n );\n nextUiEvents.push(finalEvent);\n return nextUiEvents;\n};\n\n/**\n * Reconcile the current turn's streaming delta when an intermediate\n * (tool-calling) `ActionEvent` arrives. With `stream=true` the step's\n * pre-tool-call text is streamed as delta `content`, then the action's\n * `thought` repeats it and the chat hoists that into its own message (see\n * `group-events.ts`), so the text would render twice (issue #1534).\n *\n * The action must stay because it owns the tool call. The streamed text is\n * cleared from the delta, and the delta is kept only to carry reasoning the\n * action itself lacks (for many models the delta is the sole reasoning\n * carrier), otherwise dropped.\n *\n * Only the current step's trailing delta run is considered. Returns the updated\n * array, or `null` when there is nothing to reconcile.\n */\nconst supersedeStreamedThoughtWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const thoughtText = joinTextBlocks(action.thought);\n if (!thoughtText) {\n return null;\n }\n\n const contentDeltas = getTrailingContentDeltas(uiEvents);\n if (contentDeltas.length === 0) {\n return null;\n }\n\n const streamingSegments = contentDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n // Only strip when the streamed text is the action's rendered thought.\n if (!matchStreamedSegments(thoughtText, streamingSegments).matched) {\n return null;\n }\n\n // Keeping the delta's reasoning would duplicate the action's own \"Thinking\".\n return supersedeStreamingContent(\n uiEvents,\n contentDeltas,\n getReasoningContent(action).trim().length > 0,\n );\n};\n\n// Drop the current step's streamed reasoning when the action renders that\n// reasoning itself, so \"Thinking\" renders once. Unlike\n// `supersedeStreamedThoughtWithAction` this does not require the streamed text\n// to match the thought, which models streaming reasoning-only deltas never do.\nconst supersedeStreamedReasoningWithAction = (\n action: ActionEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n if (getReasoningContent(action).trim().length === 0) {\n return null;\n }\n\n const reasoningDeltas = getTrailingReasoningDeltas(uiEvents, action);\n if (reasoningDeltas.length === 0) {\n return null;\n }\n\n const indexesToStrip = new Set(reasoningDeltas.map(({ index }) => index));\n const nextUiEvents: OpenHandsEvent[] = [];\n uiEvents.forEach((event, index) => {\n if (!indexesToStrip.has(index) || !isStreamingDeltaEvent(event)) {\n nextUiEvents.push(event);\n return;\n }\n\n // Keep the delta only for streamed text the action itself lacks.\n if (event.content) {\n nextUiEvents.push({ ...event, reasoning_content: null });\n }\n });\n return nextUiEvents;\n};\n\n/**\n * Handles adding an event to the UI events array\n * Replaces actions with observations when they arrive (so UI shows observation instead of action)\n * Exception: ThinkAction is NOT replaced because the thought content is in the action, not in the observation\n *\n * ACPToolCallEvent merge: the SDK emits two events per ``tool_call_id`` — an\n * early ``started`` event (``pending`` / ``in_progress``) and one terminal\n * (completed / failed) event, the action->observation pair for a tool call.\n * Replace the started entry in place with the terminal one so a single card\n * updates from running to its result, exactly like an observation superseding\n * its action below.\n */\nexport const handleEventForUI = (\n event: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] => {\n const newUiEvents = [...uiEvents];\n\n if (isStreamingDeltaEvent(event)) {\n if (event.content === null && event.reasoning_content === null) {\n return newUiEvents;\n }\n\n const lastIndex = newUiEvents.length - 1;\n const lastEvent = newUiEvents[lastIndex];\n if (\n lastEvent &&\n isStreamingDeltaEvent(lastEvent) &&\n isSameStreamingSender(event, lastEvent)\n ) {\n newUiEvents[lastIndex] = mergeStreamingDeltaEvent(event, lastEvent);\n return newUiEvents;\n }\n\n newUiEvents.push(event);\n return newUiEvents;\n }\n\n if (\n (isActionEvent(event) && event.action.kind === \"FinishAction\") ||\n (isMessageEvent(event) && event.source === \"agent\")\n ) {\n const finalizedUiEvents = finalizeStreamingDeltasInPlace(\n event,\n newUiEvents,\n );\n if (finalizedUiEvents) {\n return finalizedUiEvents;\n }\n }\n\n // Intermediate tool-calling action whose thought was streamed: clear the\n // duplicated text from the delta (issue #1534). ThinkAction is excluded — its\n // thought renders through its own collapsible, not a hoisted thought.\n if (\n isActionEvent(event) &&\n event.action.kind !== \"FinishAction\" &&\n event.action.kind !== \"ThinkAction\"\n ) {\n const reconciledUiEvents =\n supersedeStreamedThoughtWithAction(event, newUiEvents) ??\n supersedeStreamedReasoningWithAction(event, newUiEvents);\n if (reconciledUiEvents) {\n reconciledUiEvents.push(event);\n return reconciledUiEvents;\n }\n }\n\n if (isACPToolCallEvent(event)) {\n const existingIndex = newUiEvents.findIndex(\n (uiEvent) =>\n isACPToolCallEvent(uiEvent) &&\n uiEvent.tool_call_id === event.tool_call_id,\n );\n if (existingIndex !== -1) {\n newUiEvents[existingIndex] = event;\n } else {\n newUiEvents.push(event);\n }\n return newUiEvents;\n }\n\n if (isObservationEvent(event)) {\n // Don't add ThinkObservation at all - we keep the ThinkAction instead\n // The thought content is in the action, not the observation\n if (event.observation.kind === \"ThinkObservation\") {\n return newUiEvents;\n }\n\n // Don't add FinishObservation at all - we keep the FinishAction instead\n // Both contain the same message content, so we only need to display one\n // This also prevents duplicate messages when events arrive out of order due to React batching\n if (event.observation.kind === \"FinishObservation\") {\n return newUiEvents;\n }\n\n // Find and replace the corresponding action from uiEvents\n const actionIndex = newUiEvents.findIndex(\n (uiEvent) => uiEvent.id === event.action_id,\n );\n if (actionIndex !== -1) {\n newUiEvents[actionIndex] = event;\n } else {\n // Action not found in uiEvents, just add the observation\n newUiEvents.push(event);\n }\n } else {\n // For non-observation events, just add them to uiEvents\n newUiEvents.push(event);\n }\n\n return newUiEvents;\n};\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./mcp-marketplace-utils.cjs`),t=require(`../node_modules/@openhands/extensions/integrations/index.cjs`);var n=new Set([`invalid_auth`,`not_authed`,`account_inactive`,`token_revoked`,`token_expired`]),r={slack:{toolCall:{name:`slack_list_channels`,arguments:{limit:1}},interpret:e=>{if(e.is_error)return e.text||`tool call failed`;try{let t=JSON.parse(e.text);if(t?.ok===!1&&n.has(t.error))return String(t.error)}catch{}return null}}};function i(n){let i=e.findCatalogEntryForServer(n,e.getMcpMarketplaceCatalog(t.default));return i?r[i.id]:void 0}exports.getCredentialValidationForServer=i;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`./mcp-marketplace-utils.cjs`),t=require(`../node_modules/@openhands/extensions/integrations/index.cjs`);var n=new Set([`invalid_auth`,`not_authed`,`account_inactive`,`token_revoked`,`token_expired`]),r=e=>e.is_error?e.text||`tool call failed`:null,i={github:{toolCall:{name:`get_me`,arguments:{}},interpret:r},linear:{toolCall:{name:`list_teams`,arguments:{}},interpret:r},slack:{toolCall:{name:`slack_list_channels`,arguments:{limit:1}},interpret:e=>{if(e.is_error)return e.text||`tool call failed`;try{let t=JSON.parse(e.text);if(t?.ok===!1&&n.has(t.error))return String(t.error)}catch{}return null}}};function a(n){let r=e.findCatalogEntryForServer(n,e.getMcpMarketplaceCatalog(t.default));return r?i[r.id]:void 0}exports.getCredentialValidationForServer=a;
2
2
  //# sourceMappingURL=mcp-credential-validation.cjs.map