@openhands/agent-canvas 1.0.0-rc.1 → 1.0.0-rc.3

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 (460) hide show
  1. package/README.md +2 -2
  2. package/README.windows.md +2 -2
  3. package/build/assets/{QueryClientProvider-w1cZWY41.js → QueryClientProvider-7oLZ1RtQ.js} +1 -1
  4. package/build/assets/{Trans-BJeYqz2A.js → Trans-rF21Jwln.js} +1 -1
  5. package/build/assets/acp-providers-C55k29rf.js +1 -0
  6. package/build/assets/active-backend-context-CQTk4wD8.js +1 -0
  7. package/build/assets/add-backend-modal-BdYxoUdL.js +1 -0
  8. package/build/assets/agent-server-client-options-Dvgeb3x4.js +1 -0
  9. package/build/assets/agent-server-compatibility-BANjmMTo.js +1 -0
  10. package/build/assets/agent-server-conversation-service.api-js3oYcdU.js +5 -0
  11. package/build/assets/{agent-settings-BXBaybB_.js → agent-settings-BNrffu3I.js} +1 -1
  12. package/build/assets/{alert-banner-NeUl1-PQ.js → alert-banner-BzU93oDh.js} +1 -1
  13. package/build/assets/{analytics-consent-form-modal-DxkThW4K.js → analytics-consent-form-modal-Ce-_Skcd.js} +1 -1
  14. package/build/assets/api-key-entry-screen-CGyS-Cna.js +1 -0
  15. package/build/assets/{app-settings-C-U6jONZ.js → app-settings-DmOs4oik.js} +1 -1
  16. package/build/assets/automation-detail-DLbCVJCw.js +1 -0
  17. package/build/assets/{automations-list-BLJzAd-p.js → automations-list-Ces5shz5.js} +1 -1
  18. package/build/assets/{back-nav-button-DgkK0Ka6.js → back-nav-button-BDM9vr_o.js} +1 -1
  19. package/build/assets/backend-form-modal-BUy-PzZN.js +1 -0
  20. package/build/assets/{backend-synced-settings-badge-BktJcGgH.js → backend-synced-settings-badge-1A63yxGs.js} +1 -1
  21. package/build/assets/{base-modal-DZCNv0A4.js → base-modal-Ba5WcNb6.js} +1 -1
  22. package/build/assets/{brand-button-LBFNic99.js → brand-button-BoiPxAGm.js} +1 -1
  23. package/build/assets/browser-B8bp1IqT.js +5 -0
  24. package/build/assets/browser-store-C5uQbbIi.js +1 -0
  25. package/build/assets/{browser-tab-be3QvXg9.js → browser-tab-CiBRWB4X.js} +1 -1
  26. package/build/assets/chat-send-button-qKFZuB9E.js +1 -0
  27. package/build/assets/check-BDAbW7je.js +1 -0
  28. package/build/assets/{checkmark-Rmpruj7q.js → checkmark-SEZPnU2I.js} +1 -1
  29. package/build/assets/{chevron-down-KhWYEjeW.js → chevron-down-DUxWwzTm.js} +1 -1
  30. package/build/assets/{chevron-left-small-6nyFCWVQ.js → chevron-left-small-x9_-ucHG.js} +1 -1
  31. package/build/assets/{circle-plus-check-toggle-DquBwJ_6.js → circle-plus-check-toggle-CboDp7XB.js} +1 -1
  32. package/build/assets/{close-D_o3d8QM.js → close-CbOsKMRg.js} +1 -1
  33. package/build/assets/{code-tag-DhsjDB-v.js → code-tag-C0vR_IQH.js} +1 -1
  34. package/build/assets/{color-themes-0biOprdo.js → color-themes-B9n7pBQl.js} +1 -1
  35. package/build/assets/{combobox-caret-CO7eozIY.js → combobox-caret-DwMmhrrA.js} +1 -1
  36. package/build/assets/{command-store-UzKGSUC2.js → command-store-BUWgE-vZ.js} +1 -1
  37. package/build/assets/{condenser-settings-BulzYEuW.js → condenser-settings-FCBjvqSo.js} +1 -1
  38. package/build/assets/{confirmation-modal-CMAtd9R0.js → confirmation-modal-BPgWqWRI.js} +1 -1
  39. package/build/assets/{context-menu-list-item-D0swnhFt.js → context-menu-list-item-CHWCx1Mc.js} +1 -1
  40. package/build/assets/conversation-CT8AvxEH.js +1 -0
  41. package/build/assets/conversation-panel-DqDDs4WZ.js +1 -0
  42. package/build/assets/conversation-service.api-y_eB_43m.js +1 -0
  43. package/build/assets/conversation-state-store-DpgQaK_O.js +1 -0
  44. package/build/assets/conversation-store-CiYGBud3.js +6 -0
  45. package/build/assets/{conversation-tab-empty-state-DYjKsg_c.js → conversation-tab-empty-state-Ba5hbJhn.js} +1 -1
  46. package/build/assets/conversation-vNuLKgz6.js +19 -0
  47. package/build/assets/conversation-websocket-context-Bb4XBXoc.js +3 -0
  48. package/build/assets/{copy-BM0RpLez.js → copy-CAxUvM-U.js} +1 -1
  49. package/build/assets/{custom-toast-handlers-BohXx7uh.js → custom-toast-handlers-fgD4IYsu.js} +1 -1
  50. package/build/assets/{declaration-DaUdB2Wg.js → declaration-IA661TBv.js} +1 -1
  51. package/build/assets/{device-verify-DiEJqpJb.js → device-verify-BOQz2LJQ.js} +1 -1
  52. package/build/assets/{dist-xtCm0O6P.js → dist-B-SKiGDl.js} +1 -1
  53. package/build/assets/dist-CeJSjcAI.js +1 -0
  54. package/build/assets/{dropdown-classes-Vqz86I0R.js → dropdown-classes-lT1LUsbO.js} +1 -1
  55. package/build/assets/edit-automation-modal-C-oC5tis.js +1 -0
  56. package/build/assets/ellipsis-button-CiLx8dqc.js +1 -0
  57. package/build/assets/{entry.client-BvKgdCQ9.js → entry.client-Db3BpFL_.js} +2 -2
  58. package/build/assets/{enum-filter-dropdown-DdFgk0EM.js → enum-filter-dropdown-DFwoVtOw.js} +1 -1
  59. package/build/assets/{environment-switch-overlay-CuBuZOaa.js → environment-switch-overlay-Cow6h62p.js} +1 -1
  60. package/build/assets/{extensions-hub-Dayqvv0n.js → extensions-hub-BxZbAtjP.js} +1 -1
  61. package/build/assets/extensions-navigation-BIb-vAa2.js +1 -0
  62. package/build/assets/{file-DwHCkWZT.js → file-BJCSojij.js} +1 -1
  63. package/build/assets/files-tab-DpulQlIo.js +1 -0
  64. package/build/assets/files-tab-store-CZAEwv3x.js +1 -0
  65. package/build/assets/{folder-2h1hR1Qb.js → folder-wShAU0y7.js} +1 -1
  66. package/build/assets/git-control-bar-branch-button-D1uam-nI.js +27 -0
  67. package/build/assets/{globe-qFjFNG6J.js → globe-CQ_5xwpo.js} +1 -1
  68. package/build/assets/home-sJAFzI6v.js +1 -0
  69. package/build/assets/{i18n-zDndR1Ne.js → i18n-CyvU1o95.js} +1 -1
  70. package/build/assets/install-server-modal-BjEzlLF5.js +1 -0
  71. package/build/assets/{launch-I00QV8YT.js → launch-DgtB1JTX.js} +1 -1
  72. package/build/assets/{lesson-plan-C18uB_56.js → lesson-plan-B607buca.js} +1 -1
  73. package/build/assets/{link-external-DtcdPFbw.js → link-external-DCsHkAj4.js} +1 -1
  74. package/build/assets/llm-client-BnqeDPua.js +1 -0
  75. package/build/assets/llm-settings-B8kPJ0Of.js +1 -0
  76. package/build/assets/llm-settings-CjUpPsvA.js +1 -0
  77. package/build/assets/{loading-spinner-DNwR4--Z.js → loading-spinner-CFuA0UNt.js} +1 -1
  78. package/build/assets/manage-backends-modal-CTA-2x4F.js +1 -0
  79. package/build/assets/manifest-d3e8504d.js +1 -0
  80. package/build/assets/{markdown-renderer-D6B-u2nM.js → markdown-renderer-oOUs3tw5.js} +1 -1
  81. package/build/assets/mcp-client-xEdbDxOL.js +1 -0
  82. package/build/assets/mcp-kJYdM8aJ.js +9 -0
  83. package/build/assets/messages-BFJXB6MW.js +36 -0
  84. package/build/assets/{modal-backdrop-BDqI1zBV.js → modal-backdrop-B1si6TUd.js} +1 -1
  85. package/build/assets/{modal-body-CCLCqX1J.js → modal-body-2Po2nl1e.js} +1 -1
  86. package/build/assets/{modal-classes-DwTdT3IK.js → modal-classes-9XTtWCtF.js} +1 -1
  87. package/build/assets/{modal-close-button-gQgKqUf-.js → modal-close-button-DY-rVmld.js} +1 -1
  88. package/build/assets/model-selector-z_QOzaRV.js +1 -0
  89. package/build/assets/{navigation-context-aNGUUtdq.js → navigation-context-CszaA-CJ.js} +1 -1
  90. package/build/assets/{navigation-link-CYkF2y3K.js → navigation-link-DXg4oo29.js} +1 -1
  91. package/build/assets/onboarding-CZ_7nd-M.js +1 -0
  92. package/build/assets/{openhands-logo-CHmtDV-t.js → openhands-logo-B-IDE1ER.js} +1 -1
  93. package/build/assets/{option-service.api-CGNANEcT.js → option-service.api-DHOGfYKh.js} +1 -1
  94. package/build/assets/organization-service.api-D79cdERN.js +1 -0
  95. package/build/assets/path-utils-0KWEiRs9.js +1 -0
  96. package/build/assets/{pencil-Dr0b2jL1.js → pencil-COslZGUC.js} +1 -1
  97. package/build/assets/{plan-components--aLlpASH.js → plan-components-DsiDjcDi.js} +1 -1
  98. package/build/assets/{planner-tab-B-5EeCEm.js → planner-tab-CNmJiZ22.js} +1 -1
  99. package/build/assets/plus-D8aJZRkD.js +1 -0
  100. package/build/assets/profiles-client-CesbAK-7.js +1 -0
  101. package/build/assets/{providers-DknP6O2g.js → providers-Bpq3xFRA.js} +1 -1
  102. package/build/assets/proxy-wIasY2Po.js +1 -0
  103. package/build/assets/{query-client-config-CWWGQWvw.js → query-client-config-CITeuHD7.js} +1 -1
  104. package/build/assets/{recommended-automations-launcher-BIul0osB.js → recommended-automations-launcher-B1kfmFTQ.js} +8 -10
  105. package/build/assets/root-CIZ7Rv1X.js +2 -0
  106. package/build/assets/root-layout-B5ijp9At.js +2 -0
  107. package/build/assets/{sdk-section-page-VmtJWH3A.js → sdk-section-page-DTyvCc52.js} +1 -1
  108. package/build/assets/{sdk-settings-schema-DFievvEK.js → sdk-settings-schema-B0KrqqPX.js} +1 -1
  109. package/build/assets/{search-BeVRXvX7.js → search-BqXT2Ied.js} +1 -1
  110. package/build/assets/secrets-service-rJqZtDmI.js +1 -0
  111. package/build/assets/{secrets-settings-BLMvCkKm.js → secrets-settings-BQNSDLKS.js} +1 -1
  112. package/build/assets/server-client-Cz8PyIbN.js +1 -0
  113. package/build/assets/settings-BUlJrO_h.js +1 -0
  114. package/build/assets/settings-client-BUlG0Gzq.js +1 -0
  115. package/build/assets/{settings-dropdown-input-DA_pzHWE.js → settings-dropdown-input-BUk4m23x.js} +1 -1
  116. package/build/assets/{settings-gear-aNebYlCy.js → settings-gear-DFmoMp-6.js} +1 -1
  117. package/build/assets/{settings-index-CycvkOoq.js → settings-index-DE91Eahc.js} +1 -1
  118. package/build/assets/{settings-input-8y5p4kze.js → settings-input-DsoZj8Dm.js} +1 -1
  119. package/build/assets/{settings-list-classes-Qk7zl0Wu.js → settings-list-classes-D8VAVZmi.js} +1 -1
  120. package/build/assets/{settings-modal-DdntdOGP.js → settings-modal-CCaPYVqW.js} +1 -1
  121. package/build/assets/{settings-section-header-context-B77tsYlx.js → settings-section-header-context-D61smD-W.js} +1 -1
  122. package/build/assets/settings-service.api-4u2RKC8k.js +1 -0
  123. package/build/assets/{settings-switch-ba4DuiNO.js → settings-switch-BQiAS9ga.js} +1 -1
  124. package/build/assets/{settings-utils-BxzHqLmZ.js → settings-utils-chxTa1vn.js} +1 -1
  125. package/build/assets/shared-conversation-sBPLAawM.js +1 -0
  126. package/build/assets/{sidebar-mobile-menu-toggle-C0mmabKj.js → sidebar-mobile-menu-toggle-BDXWzhyB.js} +1 -1
  127. package/build/assets/{sidebar-nav-link-BsYdDFfb.js → sidebar-nav-link-Bhlzlhp8.js} +1 -1
  128. package/build/assets/{skill-card-pill-row-BhUlGcYB.js → skill-card-pill-row-Bg5joQf6.js} +1 -1
  129. package/build/assets/{skills-TYjOUQ2d.js → skills-BDOWVle-.js} +1 -1
  130. package/build/assets/skills-client-Cr9F5306.js +1 -0
  131. package/build/assets/{skills-plugins-D0pdqgKa.js → skills-plugins-fihjo1KZ.js} +1 -1
  132. package/build/assets/{skills-settings-VqKTkmVl.js → skills-settings-NQnuMwZP.js} +1 -1
  133. package/build/assets/{styled-tooltip-TCp7svY3.js → styled-tooltip-GXy1qxsO.js} +1 -1
  134. package/build/assets/suspense-C9MBE_9N.js +1 -0
  135. package/build/assets/{switch-skeleton-cKrdaYGj.js → switch-skeleton-QpdcdxRP.js} +1 -1
  136. package/build/assets/{task-list-tab-BiizRsY3.js → task-list-tab-Hl9BuVfq.js} +1 -1
  137. package/build/assets/telemetry-j9SLrtY7.js +2 -0
  138. package/build/assets/{terminal-BSYITdM0.js → terminal-BUww3Ssl.js} +1 -1
  139. package/build/assets/{terminal-CpgZx6go.js → terminal-DRwe8--D.js} +1 -1
  140. package/build/assets/{toggle-switch-CUgOZqZu.js → toggle-switch-DDr-DnEc.js} +1 -1
  141. package/build/assets/{trash-2-CbVljPko.js → trash-2-DAKXV2Wm.js} +1 -1
  142. package/build/assets/{typography-Bvw0IxaN.js → typography-Cx7uw7z3.js} +1 -1
  143. package/build/assets/{u-check-circle-u9QiS4Fr.js → u-check-circle-CftRwky1.js} +1 -1
  144. package/build/assets/{u-check-circle-half-DjpjzWu3.js → u-check-circle-half-sGVk0BtL.js} +1 -1
  145. package/build/assets/{u-circuit-DlBlOwx9.js → u-circuit-Cg9tH5qb.js} +1 -1
  146. package/build/assets/{u-edit-BIYzjs3v.js → u-edit-foF02hwH.js} +1 -1
  147. package/build/assets/{use-active-conversation-q1wT8LI5.js → use-active-conversation-DJGoI9Mc.js} +1 -1
  148. package/build/assets/use-agent-settings-schema-DtusObuC.js +1 -0
  149. package/build/assets/{use-agent-state-CCHlVqvY.js → use-agent-state-BXgWhFh6.js} +1 -1
  150. package/build/assets/{use-cloud-current-user-id-BAKf91Zx.js → use-cloud-current-user-id-e1Pk7NxQ.js} +1 -1
  151. package/build/assets/use-config-DSzkljTq.js +1 -0
  152. package/build/assets/use-create-conversation-CzvaVA5A.js +1 -0
  153. package/build/assets/use-event-store-DSpvASbC.js +1 -0
  154. package/build/assets/use-get-secrets-DiLgP147.js +1 -0
  155. package/build/assets/use-handle-plan-click-CUZwmKIk.js +1 -0
  156. package/build/assets/use-is-authed-fNsj-Adj.js +1 -0
  157. package/build/assets/{use-launch-skill-in-chat-3ydwpi_M.js → use-launch-skill-in-chat-ClRJlIx7.js} +1 -1
  158. package/build/assets/use-llm-profiles-CyNVoVqm.js +1 -0
  159. package/build/assets/use-runtime-is-ready-CWkGQFsb.js +1 -0
  160. package/build/assets/{use-save-settings-rE9aA29R.js → use-save-settings-CP23emUY.js} +1 -1
  161. package/build/assets/use-settings-0qFqC-r6.js +1 -0
  162. package/build/assets/{use-settings-nav-items-C7MOWj09.js → use-settings-nav-items-C6YxUn4h.js} +1 -1
  163. package/build/assets/use-skills-BWHATXK-.js +1 -0
  164. package/build/assets/{use-task-list-YMkSzdDv.js → use-task-list-JPudBRv3.js} +1 -1
  165. package/build/assets/{use-tracking-DQYdZpxi.js → use-tracking-CjLZgh8X.js} +1 -1
  166. package/build/assets/use-unified-vscode-url-DSn2tT08.js +1 -0
  167. package/build/assets/use-user-conversation-CQ5IwnZk.js +1 -0
  168. package/build/assets/{useMutation-DDo48A8t.js → useMutation-7hG0GuPx.js} +1 -1
  169. package/build/assets/useQuery-JDs8UaWj.js +1 -0
  170. package/build/assets/{useTranslation-CEcjrme-.js → useTranslation-CbJtty1g.js} +1 -1
  171. package/build/assets/{utils-CdgBzLA7.js → utils-CVcuFUYj.js} +1 -1
  172. package/build/assets/{vendor~browser-DWk6fNtJ.js → vendor~browser-Dwwc84Zf.js} +1 -1
  173. package/build/assets/{vendor~browser-tab-NZdVoI2Z.js → vendor~browser-tab-Bhohe29F.js} +1 -1
  174. package/build/assets/{vendor~conversation-panel~conversation-gp03cWZW.js → vendor~conversation-panel~conversation-DYHL7QoY.js} +1 -1
  175. package/build/assets/{vendor~conversation-panel~conversation~index-BZ5C6Xpz.js → vendor~conversation-panel~conversation~index-Be58Romv.js} +1 -1
  176. package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-BodGsxSf.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~oli4dvxu-CBEOG8NF.js} +1 -1
  177. package/build/assets/{vendor~files-tab-Buz36Y-q.js → vendor~files-tab-B447_Zns.js} +1 -1
  178. package/build/assets/{vendor~home~conversation-panel~conversation-DG0H5SkJ.js → vendor~home~conversation-panel~conversation-DZ-F7J6T.js} +1 -1
  179. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CXivI4Ym.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B4oeCCli.js} +1 -1
  180. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-9Il_wz8U.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CQQAW8hY.js} +1 -1
  181. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-1pTajrpX.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DFsI4CLy.js} +1 -1
  182. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Ceeqkj0k.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DcvlNgbn.js} +1 -1
  183. package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B7I1ZxCx.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-ojk_J4bB.js} +1 -1
  184. package/build/assets/{vendor~launch-DXL78kBf.js → vendor~launch-BdXJCmwc.js} +1 -1
  185. package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-CfAc3nMS.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DToubnIU.js} +1 -1
  186. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation-DkwcKRtv.js → vendor~root-layout~home~conversation-panel~conversation-Cg0nXqVs.js} +1 -1
  187. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-DSqEbr0N.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-Dr8Ly0at.js} +1 -1
  188. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~iguv7bgw-BC9XTECT.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~bok0tgtf-DveauQfg.js} +1 -1
  189. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~k776hupu-D0XUSHNN.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~jpfhx3ls-zdl_Rltz.js} +2 -2
  190. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~jxrvuot9-BaCzvZac.js +48 -0
  191. package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-CcFtthyg.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BwbkftxT.js} +1 -1
  192. package/build/assets/{vendor~root-layout~home~mcp~automations-list-cNHi83v_.js → vendor~root-layout~home~mcp~automations-list-CtdIEhjQ.js} +1 -1
  193. package/build/assets/{vendor~root-layout~home~mcp~automations-list-BnIlGhjl.js → vendor~root-layout~home~mcp~automations-list-DGOI7kQz.js} +1 -1
  194. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BGWUbqUq.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-CbuXadI-.js} +1 -1
  195. package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-BuCSnjsW.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-iPAfRWNQ.js} +2 -2
  196. package/build/assets/{verification-settings-CIqtxWat.js → verification-settings-Cm02KmeI.js} +1 -1
  197. package/build/assets/{vscode-tab-DEt72yJX.js → vscode-tab-AF70Yke0.js} +1 -1
  198. package/build/assets/{waiting-for-runtime-message-DSjJfeoj.js → waiting-for-runtime-message-Bg27u9JB.js} +1 -1
  199. package/build/assets/x-8AbJWTbX.js +1 -0
  200. package/build/assets/{x-mark-DsJ9tDD0.js → x-mark-Cn-YJVbN.js} +1 -1
  201. package/build/index.html +4 -4
  202. package/build/locales/ar/openhands.json +2 -1
  203. package/build/locales/ca/openhands.json +2 -1
  204. package/build/locales/de/openhands.json +2 -1
  205. package/build/locales/en/openhands.json +2 -1
  206. package/build/locales/es/openhands.json +2 -1
  207. package/build/locales/fr/openhands.json +2 -1
  208. package/build/locales/it/openhands.json +2 -1
  209. package/build/locales/ja/openhands.json +2 -1
  210. package/build/locales/ko-KR/openhands.json +2 -1
  211. package/build/locales/no/openhands.json +2 -1
  212. package/build/locales/pt/openhands.json +2 -1
  213. package/build/locales/tr/openhands.json +2 -1
  214. package/build/locales/uk/openhands.json +2 -1
  215. package/build/locales/zh-CN/openhands.json +2 -1
  216. package/build/locales/zh-TW/openhands.json +2 -1
  217. package/config/defaults.json +1 -1
  218. package/dist/api/agent-server-adapter.cjs +3 -3
  219. package/dist/api/agent-server-adapter.cjs.map +1 -1
  220. package/dist/api/agent-server-adapter.js +94 -86
  221. package/dist/api/agent-server-adapter.js.map +1 -1
  222. package/dist/api/app-preferences-store.cjs.map +1 -1
  223. package/dist/api/app-preferences-store.d.ts +6 -2
  224. package/dist/api/app-preferences-store.js.map +1 -1
  225. package/dist/api/cloud/proxy.cjs +1 -1
  226. package/dist/api/cloud/proxy.cjs.map +1 -1
  227. package/dist/api/cloud/proxy.d.ts +18 -6
  228. package/dist/api/cloud/proxy.js +1 -1
  229. package/dist/api/cloud/proxy.js.map +1 -1
  230. package/dist/api/conversation-metadata-store.cjs.map +1 -1
  231. package/dist/api/conversation-metadata-store.d.ts +8 -0
  232. package/dist/api/conversation-metadata-store.js.map +1 -1
  233. package/dist/api/conversation-service/agent-server-conversation-service.types.d.ts +9 -0
  234. package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs +1 -1
  235. package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs.map +1 -1
  236. package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js +9 -9
  237. package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map +1 -1
  238. package/dist/components/conversation-events/chat/event-message.cjs +1 -1
  239. package/dist/components/conversation-events/chat/event-message.cjs.map +1 -1
  240. package/dist/components/conversation-events/chat/event-message.js +80 -71
  241. package/dist/components/conversation-events/chat/event-message.js.map +1 -1
  242. package/dist/components/features/automations/recommended-automations-launcher.d.ts +7 -1
  243. package/dist/components/features/chat/switch-profile-button.cjs +1 -1
  244. package/dist/components/features/chat/switch-profile-button.cjs.map +1 -1
  245. package/dist/components/features/chat/switch-profile-button.js +5 -5
  246. package/dist/components/features/chat/switch-profile-button.js.map +1 -1
  247. package/dist/components/features/conversation-panel/skills-modal.cjs +1 -1
  248. package/dist/components/features/conversation-panel/skills-modal.cjs.map +1 -1
  249. package/dist/components/features/conversation-panel/skills-modal.js +34 -37
  250. package/dist/components/features/conversation-panel/skills-modal.js.map +1 -1
  251. package/dist/components/features/home/workspace-selection-form.d.ts +1 -0
  252. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +8 -0
  253. package/dist/components/features/settings/llm-profiles/profile-actions-menu.cjs +1 -1
  254. package/dist/components/features/settings/llm-profiles/profile-actions-menu.js +1 -0
  255. package/dist/components/features/sidebar/sidebar.cjs +1 -1
  256. package/dist/components/features/sidebar/sidebar.js +6 -6
  257. package/dist/components/features/skills/extensions-navigation.cjs +1 -1
  258. package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
  259. package/dist/components/features/skills/extensions-navigation.d.ts +1 -9
  260. package/dist/components/features/skills/extensions-navigation.js +31 -50
  261. package/dist/components/features/skills/extensions-navigation.js.map +1 -1
  262. package/dist/constants/acp-providers.cjs +1 -1
  263. package/dist/constants/acp-providers.js +1 -1
  264. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  265. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  266. package/dist/hooks/mutation/use-create-conversation.js +26 -14
  267. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  268. package/dist/hooks/mutation/use-switch-llm-profile-and-log.cjs +1 -1
  269. package/dist/hooks/mutation/use-switch-llm-profile-and-log.cjs.map +1 -1
  270. package/dist/hooks/mutation/use-switch-llm-profile-and-log.js +26 -15
  271. package/dist/hooks/mutation/use-switch-llm-profile-and-log.js.map +1 -1
  272. package/dist/i18n/declaration.cjs +1 -1
  273. package/dist/i18n/declaration.cjs.map +1 -1
  274. package/dist/i18n/declaration.d.ts +1 -0
  275. package/dist/i18n/declaration.js +1 -1
  276. package/dist/i18n/declaration.js.map +1 -1
  277. package/dist/i18n/translation.cjs +1 -1
  278. package/dist/i18n/translation.cjs.map +1 -1
  279. package/dist/i18n/translation.js +32 -15
  280. package/dist/i18n/translation.js.map +1 -1
  281. package/dist/index.cjs +1 -1
  282. package/dist/index.js +10 -10
  283. package/dist/lib/index.cjs +1 -1
  284. package/dist/lib/index.js +1 -1
  285. package/dist/locales/ar/openhands.json +2 -1
  286. package/dist/locales/ca/openhands.json +2 -1
  287. package/dist/locales/de/openhands.json +2 -1
  288. package/dist/locales/en/openhands.json +2 -1
  289. package/dist/locales/es/openhands.json +2 -1
  290. package/dist/locales/fr/openhands.json +2 -1
  291. package/dist/locales/it/openhands.json +2 -1
  292. package/dist/locales/ja/openhands.json +2 -1
  293. package/dist/locales/ko-KR/openhands.json +2 -1
  294. package/dist/locales/no/openhands.json +2 -1
  295. package/dist/locales/pt/openhands.json +2 -1
  296. package/dist/locales/tr/openhands.json +2 -1
  297. package/dist/locales/uk/openhands.json +2 -1
  298. package/dist/locales/zh-CN/openhands.json +2 -1
  299. package/dist/locales/zh-TW/openhands.json +2 -1
  300. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs +1 -1
  301. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs.map +1 -1
  302. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js +10 -1
  303. package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js.map +1 -1
  304. package/dist/package.cjs +1 -1
  305. package/dist/package.cjs.map +1 -1
  306. package/dist/package.js +2 -2
  307. package/dist/package.js.map +1 -1
  308. package/dist/routes/llm-settings.cjs +1 -1
  309. package/dist/routes/llm-settings.cjs.map +1 -1
  310. package/dist/routes/llm-settings.js +21 -21
  311. package/dist/routes/llm-settings.js.map +1 -1
  312. package/dist/routes/mcp.cjs +1 -1
  313. package/dist/routes/mcp.cjs.map +1 -1
  314. package/dist/routes/mcp.d.ts +0 -1
  315. package/dist/routes/mcp.js +0 -1
  316. package/dist/routes/mcp.js.map +1 -1
  317. package/dist/types/agent-server/core/events/index.d.ts +1 -0
  318. package/dist/types/agent-server/core/events/streaming-delta-event.d.ts +7 -0
  319. package/dist/types/agent-server/core/openhands-event.d.ts +2 -2
  320. package/dist/types/agent-server/type-guards.cjs +1 -1
  321. package/dist/types/agent-server/type-guards.cjs.map +1 -1
  322. package/dist/types/agent-server/type-guards.d.ts +2 -0
  323. package/dist/types/agent-server/type-guards.js +3 -3
  324. package/dist/types/agent-server/type-guards.js.map +1 -1
  325. package/dist/utils/handle-event-for-ui.cjs +1 -1
  326. package/dist/utils/handle-event-for-ui.cjs.map +1 -1
  327. package/dist/utils/handle-event-for-ui.js +69 -13
  328. package/dist/utils/handle-event-for-ui.js.map +1 -1
  329. package/dist/utils/mcp-config.cjs +1 -1
  330. package/dist/utils/mcp-config.cjs.map +1 -1
  331. package/dist/utils/mcp-config.js +27 -19
  332. package/dist/utils/mcp-config.js.map +1 -1
  333. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  334. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  335. package/dist/utils/mcp-marketplace-utils.js +38 -18
  336. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  337. package/dist/utils/normalize-display-model.cjs +1 -1
  338. package/dist/utils/normalize-display-model.cjs.map +1 -1
  339. package/dist/utils/normalize-display-model.js +8 -7
  340. package/dist/utils/normalize-display-model.js.map +1 -1
  341. package/dist/utils/openhands-llm.cjs +1 -1
  342. package/dist/utils/openhands-llm.cjs.map +1 -1
  343. package/dist/utils/openhands-llm.d.ts +13 -0
  344. package/dist/utils/openhands-llm.js +9 -3
  345. package/dist/utils/openhands-llm.js.map +1 -1
  346. package/package.json +2 -2
  347. package/scripts/check-sdk-version-sync.mjs +6 -6
  348. package/scripts/dev-safe.mjs +71 -120
  349. package/scripts/dev-with-automation.mjs +58 -4
  350. package/scripts/runtime-services-info.mjs +199 -0
  351. package/scripts/static-server.mjs +42 -4
  352. package/build/assets/acp-providers-DJr8DlNG.js +0 -1
  353. package/build/assets/acp-route-guard-A__sWgbc.js +0 -1
  354. package/build/assets/active-backend-context-I2w666XY.js +0 -1
  355. package/build/assets/add-backend-modal-BDBDBXsJ.js +0 -1
  356. package/build/assets/agent-server-client-options-9agOSarV.js +0 -1
  357. package/build/assets/agent-server-compatibility-B7QStIcH.js +0 -1
  358. package/build/assets/agent-server-conversation-service.api-Cagoqq1V.js +0 -5
  359. package/build/assets/api-key-entry-screen-ByXA4hXH.js +0 -1
  360. package/build/assets/automation-detail-Dbmgt974.js +0 -1
  361. package/build/assets/backend-form-modal-CeB983Sj.js +0 -1
  362. package/build/assets/browser-D08Sp3ZY.js +0 -5
  363. package/build/assets/browser-store-JRrcGdlk.js +0 -1
  364. package/build/assets/chat-send-button-5qz0zj6R.js +0 -1
  365. package/build/assets/check-CZhEL6rP.js +0 -1
  366. package/build/assets/conversation-BrjF2-Ky.js +0 -1
  367. package/build/assets/conversation-HgR_TTPE.js +0 -19
  368. package/build/assets/conversation-panel-BlRcO5AC.js +0 -1
  369. package/build/assets/conversation-service.api-B_Pdmwsa.js +0 -1
  370. package/build/assets/conversation-state-store-D-w0uurj.js +0 -1
  371. package/build/assets/conversation-store-CC-isCnP.js +0 -1
  372. package/build/assets/conversation-websocket-context-G95yfL81.js +0 -3
  373. package/build/assets/dist-Bl-1K5Tv.js +0 -1
  374. package/build/assets/edit-automation-modal-CNZgSSiH.js +0 -1
  375. package/build/assets/extensions-navigation-yFLAU06N.js +0 -1
  376. package/build/assets/files-tab-CMredyYX.js +0 -1
  377. package/build/assets/files-tab-store-DLU28g8C.js +0 -1
  378. package/build/assets/git-control-bar-branch-button-D8blTNXh.js +0 -27
  379. package/build/assets/home-CWw845Rz.js +0 -1
  380. package/build/assets/install-server-modal-D8Q0xZcN.js +0 -1
  381. package/build/assets/llm-client-BqyLKgUN.js +0 -1
  382. package/build/assets/llm-settings-CAnFYAEG.js +0 -1
  383. package/build/assets/llm-settings-DotqpmCF.js +0 -1
  384. package/build/assets/manage-backends-modal-Ceo_SOcf.js +0 -1
  385. package/build/assets/manifest-61ec2d68.js +0 -1
  386. package/build/assets/mcp-EvrLVTla.js +0 -9
  387. package/build/assets/messages-Bz9TWjlh.js +0 -36
  388. package/build/assets/middleware-CfatjPYZ.js +0 -6
  389. package/build/assets/model-selector-CZOi4V7r.js +0 -1
  390. package/build/assets/onboarding-CPCKYvFh.js +0 -1
  391. package/build/assets/organization-service.api-Dn74hBTH.js +0 -1
  392. package/build/assets/path-utils-BjxzIGLp.js +0 -1
  393. package/build/assets/plus-DT-M0FA1.js +0 -1
  394. package/build/assets/profiles-client-BrqNmaDV.js +0 -1
  395. package/build/assets/proxy-sRh0WKI7.js +0 -1
  396. package/build/assets/root-CklXEh3W.js +0 -2
  397. package/build/assets/root-layout-BCA_X8XL.js +0 -2
  398. package/build/assets/secrets-service-DVtlLWY8.js +0 -1
  399. package/build/assets/server-client-DYv_GHPl.js +0 -1
  400. package/build/assets/settings-CXvJUx_j.js +0 -1
  401. package/build/assets/settings-service.api-DxIEtvx6.js +0 -1
  402. package/build/assets/shared-conversation-x41nZQi7.js +0 -1
  403. package/build/assets/sidebar-store-DnQAJAE5.js +0 -1
  404. package/build/assets/telemetry-fQFd-8V3.js +0 -2
  405. package/build/assets/use-agent-settings-schema-Yxf7KGyG.js +0 -1
  406. package/build/assets/use-config-DwfigQ_Y.js +0 -1
  407. package/build/assets/use-create-conversation-BEzddjXn.js +0 -1
  408. package/build/assets/use-event-store-Cxqc45Sw.js +0 -1
  409. package/build/assets/use-get-secrets-BlO1BfUo.js +0 -1
  410. package/build/assets/use-handle-plan-click-Bfl0zIBr.js +0 -1
  411. package/build/assets/use-is-authed-hHndEep7.js +0 -1
  412. package/build/assets/use-llm-profiles-E-jjZMZw.js +0 -1
  413. package/build/assets/use-runtime-is-ready-DBWzvAmc.js +0 -1
  414. package/build/assets/use-settings-BPTbE7lg.js +0 -1
  415. package/build/assets/use-skills-QhoaIYGF.js +0 -1
  416. package/build/assets/use-unified-vscode-url-DFtNIC1Q.js +0 -1
  417. package/build/assets/use-user-conversation-BRAseenw.js +0 -1
  418. package/build/assets/vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~dp08i1qy-D8soyAAx.js +0 -48
  419. package/build/assets/vendor~root~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-s~jaomi49z-Cw89stA6.js +0 -1
  420. package/build/assets/x-JOBEVLW0.js +0 -1
  421. package/dist/components/features/conversation-panel/skills-runtime-waiting-state.cjs +0 -2
  422. package/dist/components/features/conversation-panel/skills-runtime-waiting-state.cjs.map +0 -1
  423. package/dist/components/features/conversation-panel/skills-runtime-waiting-state.d.ts +0 -1
  424. package/dist/components/features/conversation-panel/skills-runtime-waiting-state.js +0 -21
  425. package/dist/components/features/conversation-panel/skills-runtime-waiting-state.js.map +0 -1
  426. package/dist/utils/acp-route-guard.cjs +0 -1
  427. package/dist/utils/acp-route-guard.d.ts +0 -26
  428. package/dist/utils/acp-route-guard.js +0 -4
  429. /package/build/assets/{agent-server-ui-style-scope-BwIZYdC1.js → agent-server-ui-style-scope-Bhc5vpWO.js} +0 -0
  430. /package/build/assets/{automation-DJ_3GeXD.js → automation-BzmydDjr.js} +0 -0
  431. /package/build/assets/{common-DqjLSBOt.js → common-Cfviy7yT.js} +0 -0
  432. /package/build/assets/{context-CEQZwATj.js → context-BBqptpXX.js} +0 -0
  433. /package/build/assets/{conversation-local-storage-YmOVXxxW.js → conversation-local-storage-D5Tre_GA.js} +0 -0
  434. /package/build/assets/{createLucideIcon-Ddu8jDOQ.js → createLucideIcon-C9OEnwvX.js} +0 -0
  435. /package/build/assets/{environment-switch-store-CiurvTtK.js → environment-switch-store-BpKxp6Xq.js} +0 -0
  436. /package/build/assets/{git-status-mapper-DnL9OC8_.js → git-status-mapper-C3rfzUex.js} +0 -0
  437. /package/build/assets/{handle-capture-consent-3XrjZ8wi.js → handle-capture-consent-DDnXMC9Y.js} +0 -0
  438. /package/build/assets/{health-store-B5f0S2FY.js → health-store-d-d2ssv4.js} +0 -0
  439. /package/build/assets/{iconBase-BVhFI-0E.js → iconBase-1A_WRQ4E.js} +0 -0
  440. /package/build/assets/{map-provider-C3Z5Dx2J.js → map-provider-XSFHmdDs.js} +0 -0
  441. /package/build/assets/{objectWithoutPropertiesLoose-DSQKyRhw.js → objectWithoutPropertiesLoose-B-IA9dU9.js} +0 -0
  442. /package/build/assets/{query-keys-tAsQcc_9.js → query-keys-CQaji0wJ.js} +0 -0
  443. /package/build/assets/{react-Dy05vyj5.js → react-CuAHzoGi.js} +0 -0
  444. /package/build/assets/{retrieve-axios-error-message-BY-yIkIq.js → retrieve-axios-error-message-DbnSBc_1.js} +0 -0
  445. /package/build/assets/{sdk-settings-field-metadata-C6KMD-jZ.js → sdk-settings-field-metadata-CETNItbo.js} +0 -0
  446. /package/build/assets/{settings-DGY6n4J2.js → settings-BgL2HUsU.js} +0 -0
  447. /package/build/assets/{settings-like-page-layout-classes-DNg2vKSM.js → settings-like-page-layout-classes-DENKlZpD.js} +0 -0
  448. /package/build/assets/{use-breakpoint-DpxHDmuH.js → use-breakpoint-Dt2knceC.js} +0 -0
  449. /package/build/assets/{use-click-outside-element-DhxCUyWl.js → use-click-outside-element-Bu2A3s59.js} +0 -0
  450. /package/build/assets/{v4-khGvL7i2.js → v4-BygpdDmc.js} +0 -0
  451. /package/build/assets/{vendor~browser-BDNLFng6.js → vendor~browser-BYEwwJqV.js} +0 -0
  452. /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-D_hRW_zc.js → vendor~conversation-panel~conversation~alert-banner-BnHToS5O.js} +0 -0
  453. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~j8sdb9mk-OFpe9fX_.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~j8sdb9mk-oCzr0-9g.js} +0 -0
  454. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-BPuuVEqr.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~lpdshwee-CTmh4bwd.js} +0 -0
  455. /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~skills-settings~m~o9nrx3fm-D44TR8hL.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~skills-settings~m~o9nrx3fm-87v-LliW.js} +0 -0
  456. /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Dr3Ow7Ms.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D8cqyq4k.js} +0 -0
  457. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~skil~i4kjfqhl-B1TKKuuH.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~ntycl9e1-DspdqGPW.js} +0 -0
  458. /package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~e9ykmtgh-Fa-nXZ4M.js → vendor~root-layout~home~conversation-panel~conversation~launch~extensions-hub~skills-settin~e9ykmtgh-DMH1jSwL.js} +0 -0
  459. /package/build/assets/{vendor~terminal-0ObOedYm.js → vendor~terminal-aeP3PnKf.js} +0 -0
  460. /package/build/assets/{vscode-url-helper-BMq8JBhB.js → vscode-url-helper-DOCkV_-G.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"type-guards.cjs","names":[],"sources":["../../../src/types/agent-server/type-guards.ts"],"sourcesContent":["import {\n OpenHandsEvent,\n ObservationEvent,\n BaseEvent,\n ExecuteBashAction,\n TerminalAction,\n ExecuteBashObservation,\n PlanningFileEditorObservation,\n TerminalObservation,\n BrowserObservation,\n BrowserNavigateAction,\n SwitchLLMObservation,\n CanvasUIAction,\n} from \"./core\";\nimport { AgentErrorEvent } from \"./core/events/observation-event\";\nimport { MessageEvent } from \"./core/events/message-event\";\nimport { ActionEvent } from \"./core/events/action-event\";\nimport {\n ConversationStateUpdateEvent,\n ConversationStateUpdateEventAgentStatus,\n ConversationStateUpdateEventFullState,\n ConversationStateUpdateEventStats,\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"./core/events/conversation-state-event\";\nimport { HookExecutionEvent } from \"./core/events/hook-execution-event\";\nimport { ACPToolCallEvent } from \"./core/events/acp-tool-call-event\";\nimport { SystemPromptEvent } from \"./core/events/system-event\";\n\n/**\n * Type guard to check if an unknown value is a valid BaseEvent\n * @param value - The value to check\n * @returns true if the value is a valid BaseEvent\n */\nexport function isBaseEvent(value: unknown): value is BaseEvent {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"id\" in value &&\n \"timestamp\" in value &&\n \"source\" in value &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n typeof value.timestamp === \"string\" &&\n value.timestamp.length > 0 &&\n typeof value.source === \"string\" &&\n (value.source === \"agent\" ||\n value.source === \"user\" ||\n value.source === \"environment\" ||\n value.source === \"hook\")\n );\n}\n\n/**\n * Type guard function to check if an event is an observation event\n */\nexport const isObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent =>\n event.source === \"environment\" &&\n \"action_id\" in event &&\n \"observation\" in event &&\n event.observation !== null &&\n typeof event.observation === \"object\" &&\n \"kind\" in event.observation;\n\n/**\n * Type guard function to check if an event is an agent error event\n */\nexport const isAgentErrorEvent = (\n event: OpenHandsEvent,\n): event is AgentErrorEvent =>\n event.source === \"agent\" &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n \"error\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\" &&\n typeof event.error === \"string\";\n\n/**\n * Type guard function to check if an event is a message event (user or assistant)\n */\nexport const isMessageEvent = (event: OpenHandsEvent): event is MessageEvent =>\n \"llm_message\" in event &&\n typeof event.llm_message === \"object\" &&\n event.llm_message !== null &&\n \"role\" in event.llm_message &&\n \"content\" in event.llm_message;\n\n/**\n * Type guard function to check if an event is a user message event\n */\nexport const isUserMessageEvent = (\n event: OpenHandsEvent,\n): event is MessageEvent =>\n isMessageEvent(event) && event.llm_message.role === \"user\";\n\n/**\n * Type guard function to check if an event is an action event\n */\nexport const isActionEvent = (event: OpenHandsEvent): event is ActionEvent =>\n event.source === \"agent\" &&\n \"action\" in event &&\n event.action !== null &&\n typeof event.action === \"object\" &&\n \"kind\" in event.action &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\";\n\n/**\n * Type guard function to check if an action event is an ExecuteBashAction\n */\nexport const isExecuteBashActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<ExecuteBashAction | TerminalAction> =>\n isActionEvent(event) &&\n (event.action.kind === \"ExecuteBashAction\" ||\n event.action.kind === \"TerminalAction\");\n\n/**\n * Type guard function to check if an observation event contains terminal output\n */\nexport const isExecuteBashObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<ExecuteBashObservation | TerminalObservation> =>\n isObservationEvent(event) &&\n (event.observation.kind === \"ExecuteBashObservation\" ||\n event.observation.kind === \"TerminalObservation\");\n\n/**\n * Type guard function to check if an observation event is a PlanningFileEditorObservation\n */\nexport const isPlanningFileEditorObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<PlanningFileEditorObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"PlanningFileEditorObservation\";\n\n/**\n * Type guard function to check if an observation event is a BrowserObservation\n */\nexport const isBrowserObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<BrowserObservation> =>\n isObservationEvent(event) && event.observation.kind === \"BrowserObservation\";\n\n/**\n * Type guard function to check if an observation event is a SwitchLLMObservation\n */\nexport const isSwitchLLMObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<SwitchLLMObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"SwitchLLMObservation\";\n\n/**\n * Type guard function to check if an action event is a BrowserNavigateAction\n */\nexport const isBrowserNavigateActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<BrowserNavigateAction> =>\n isActionEvent(event) && event.action.kind === \"BrowserNavigateAction\";\n\n/**\n * Type guard for the canvas_ui custom tool's ActionEvent.\n *\n * The tool is injected via tool_module_qualnames (see canvas_ui_tool.py and\n * agent-server-adapter.ts). We discriminate on tool_name (which we control\n * via register_tool(\"canvas_ui\", ...)). The predicate narrows the event so\n * the call site can read `event.action.command` etc. without further casts.\n */\nexport const isCanvasUIActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<CanvasUIAction> =>\n isActionEvent(event) && event.tool_name === \"canvas_ui\";\n\n/**\n * Type guard function to check if an event is a system prompt event\n */\nexport const isSystemPromptEvent = (\n event: OpenHandsEvent,\n): event is SystemPromptEvent =>\n event.source === \"agent\" &&\n \"system_prompt\" in event &&\n \"tools\" in event &&\n typeof event.system_prompt === \"object\" &&\n Array.isArray(event.tools);\n\n/**\n * Type guard function to check if an event is a conversation state update event\n */\nexport const isConversationStateUpdateEvent = (\n event: OpenHandsEvent,\n): event is ConversationStateUpdateEvent =>\n \"kind\" in event && event.kind === \"ConversationStateUpdateEvent\";\n\nexport const isFullStateConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventFullState => event.key === \"full_state\";\n\nexport const isAgentStatusConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventAgentStatus =>\n event.key === \"execution_status\";\n\nexport const isStatsConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventStats => event.key === \"stats\";\n\n/**\n * Type guard function to check if an event is a conversation error event\n */\nexport const isConversationErrorEvent = (\n event: OpenHandsEvent,\n): event is ConversationErrorEvent =>\n \"kind\" in event && event.kind === \"ConversationErrorEvent\";\n\n/**\n * Type guard function to check if an event is a server error event\n */\nexport const isServerErrorEvent = (\n event: OpenHandsEvent,\n): event is ServerErrorEvent =>\n \"kind\" in event && event.kind === \"ServerErrorEvent\";\n\n/**\n * Type guard function to check if an event is a displayable error event\n * (ConversationErrorEvent or ServerErrorEvent) - both should show as error banners\n */\nexport const isDisplayableErrorEvent = (event: OpenHandsEvent): boolean =>\n isConversationErrorEvent(event) || isServerErrorEvent(event);\n\n/**\n * Type guard function to check if an event is a hook execution event\n */\nexport const isHookExecutionEvent = (\n event: OpenHandsEvent,\n): event is HookExecutionEvent =>\n \"kind\" in event && event.kind === \"HookExecutionEvent\";\n\n/**\n * Type guard function to check if an event is an ACP tool call event\n */\nexport const isACPToolCallEvent = (\n event: OpenHandsEvent,\n): event is ACPToolCallEvent =>\n \"kind\" in event && event.kind === \"ACPToolCallEvent\";\n\n// =============================================================================\n// COMPATIBILITY TYPE GUARDS\n// =============================================================================\n\n/**\n * Type guard to check if an event is an agent-server OpenHandsEvent.\n * Uses isBaseEvent to validate the complete event structure.\n */\nexport function isAgentServerEvent(event: unknown): event is OpenHandsEvent {\n return isBaseEvent(event);\n}\n"],"mappings":"gDAkCA,SAAgB,EAAY,EAAoC,CAC9D,OAEE,OAAO,GAAU,YADjB,GAEA,OAAQ,GACR,cAAe,GACf,WAAY,GACZ,OAAO,EAAM,IAAO,UACpB,EAAM,GAAG,OAAS,GAClB,OAAO,EAAM,WAAc,UAC3B,EAAM,UAAU,OAAS,GACzB,OAAO,EAAM,QAAW,WACvB,EAAM,SAAW,SAChB,EAAM,SAAW,QACjB,EAAM,SAAW,eACjB,EAAM,SAAW,QAOvB,IAAa,EACX,GAEA,EAAM,SAAW,eACjB,cAAe,GACf,gBAAiB,GACjB,EAAM,cAAgB,MACtB,OAAO,EAAM,aAAgB,UAC7B,SAAU,EAAM,YAKL,EACX,GAEA,EAAM,SAAW,SACjB,cAAe,GACf,iBAAkB,GAClB,UAAW,GACX,OAAO,EAAM,WAAc,UAC3B,OAAO,EAAM,cAAiB,UAC9B,OAAO,EAAM,OAAU,SAKZ,EAAkB,GAC7B,gBAAiB,GACjB,OAAO,EAAM,aAAgB,UAC7B,EAAM,cAAgB,MACtB,SAAU,EAAM,aAChB,YAAa,EAAM,YAKR,EACX,GAEA,EAAe,EAAM,EAAI,EAAM,YAAY,OAAS,OAKzC,EAAiB,GAC5B,EAAM,SAAW,SACjB,WAAY,GACZ,EAAM,SAAW,MACjB,OAAO,EAAM,QAAW,UACxB,SAAU,EAAM,QAChB,cAAe,GACf,iBAAkB,GAClB,OAAO,EAAM,WAAc,UAC3B,OAAO,EAAM,cAAiB,SAKnB,EACX,GAEA,EAAc,EAAM,GACnB,EAAM,OAAO,OAAS,qBACrB,EAAM,OAAO,OAAS,kBAKb,EACX,GAEA,EAAmB,EAAM,GACxB,EAAM,YAAY,OAAS,0BAC1B,EAAM,YAAY,OAAS,uBAKlB,EACX,GAEA,EAAmB,EAAM,EACzB,EAAM,YAAY,OAAS,gCAKhB,EACX,GAEA,EAAmB,EAAM,EAAI,EAAM,YAAY,OAAS,qBAK7C,EACX,GAEA,EAAmB,EAAM,EACzB,EAAM,YAAY,OAAS,uBAKhB,EACX,GAEA,EAAc,EAAM,EAAI,EAAM,OAAO,OAAS,wBAUnC,EACX,GAEA,EAAc,EAAM,EAAI,EAAM,YAAc,YAKjC,EACX,GAEA,EAAM,SAAW,SACjB,kBAAmB,GACnB,UAAW,GACX,OAAO,EAAM,eAAkB,UAC/B,MAAM,QAAQ,EAAM,MAAM,CAKf,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,+BAEvB,EACX,GACmD,EAAM,MAAQ,aAEtD,EACX,GAEA,EAAM,MAAQ,mBAEH,EACX,GAC+C,EAAM,MAAQ,QAKlD,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,yBAKvB,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,mBAMvB,EAA2B,GACtC,EAAyB,EAAM,EAAI,EAAmB,EAAM,CAKjD,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,qBAKvB,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,mBAUpC,SAAgB,EAAmB,EAAyC,CAC1E,OAAO,EAAY,EAAM"}
1
+ {"version":3,"file":"type-guards.cjs","names":[],"sources":["../../../src/types/agent-server/type-guards.ts"],"sourcesContent":["import {\n OpenHandsEvent,\n ObservationEvent,\n BaseEvent,\n ExecuteBashAction,\n TerminalAction,\n ExecuteBashObservation,\n PlanningFileEditorObservation,\n TerminalObservation,\n BrowserObservation,\n BrowserNavigateAction,\n SwitchLLMObservation,\n CanvasUIAction,\n} from \"./core\";\nimport { AgentErrorEvent } from \"./core/events/observation-event\";\nimport { MessageEvent } from \"./core/events/message-event\";\nimport { ActionEvent } from \"./core/events/action-event\";\nimport {\n ConversationStateUpdateEvent,\n ConversationStateUpdateEventAgentStatus,\n ConversationStateUpdateEventFullState,\n ConversationStateUpdateEventStats,\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"./core/events/conversation-state-event\";\nimport { HookExecutionEvent } from \"./core/events/hook-execution-event\";\nimport { ACPToolCallEvent } from \"./core/events/acp-tool-call-event\";\nimport { StreamingDeltaEvent } from \"./core/events/streaming-delta-event\";\nimport { SystemPromptEvent } from \"./core/events/system-event\";\n\n/**\n * Type guard to check if an unknown value is a valid BaseEvent\n * @param value - The value to check\n * @returns true if the value is a valid BaseEvent\n */\nexport function isBaseEvent(value: unknown): value is BaseEvent {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"id\" in value &&\n \"timestamp\" in value &&\n \"source\" in value &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n typeof value.timestamp === \"string\" &&\n value.timestamp.length > 0 &&\n typeof value.source === \"string\" &&\n (value.source === \"agent\" ||\n value.source === \"user\" ||\n value.source === \"environment\" ||\n value.source === \"hook\")\n );\n}\n\n/**\n * Type guard function to check if an event is an observation event\n */\nexport const isObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent =>\n event.source === \"environment\" &&\n \"action_id\" in event &&\n \"observation\" in event &&\n event.observation !== null &&\n typeof event.observation === \"object\" &&\n \"kind\" in event.observation;\n\n/**\n * Type guard function to check if an event is an agent error event\n */\nexport const isAgentErrorEvent = (\n event: OpenHandsEvent,\n): event is AgentErrorEvent =>\n event.source === \"agent\" &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n \"error\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\" &&\n typeof event.error === \"string\";\n\n/**\n * Type guard function to check if an event is a message event (user or assistant)\n */\nexport const isMessageEvent = (event: OpenHandsEvent): event is MessageEvent =>\n \"llm_message\" in event &&\n typeof event.llm_message === \"object\" &&\n event.llm_message !== null &&\n \"role\" in event.llm_message &&\n \"content\" in event.llm_message;\n\n/**\n * Type guard function to check if an event is a user message event\n */\nexport const isUserMessageEvent = (\n event: OpenHandsEvent,\n): event is MessageEvent =>\n isMessageEvent(event) && event.llm_message.role === \"user\";\n\n/**\n * Type guard function to check if an event is an action event\n */\nexport const isActionEvent = (event: OpenHandsEvent): event is ActionEvent =>\n event.source === \"agent\" &&\n \"action\" in event &&\n event.action !== null &&\n typeof event.action === \"object\" &&\n \"kind\" in event.action &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\";\n\n/**\n * Type guard function to check if an action event is an ExecuteBashAction\n */\nexport const isExecuteBashActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<ExecuteBashAction | TerminalAction> =>\n isActionEvent(event) &&\n (event.action.kind === \"ExecuteBashAction\" ||\n event.action.kind === \"TerminalAction\");\n\n/**\n * Type guard function to check if an observation event contains terminal output\n */\nexport const isExecuteBashObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<ExecuteBashObservation | TerminalObservation> =>\n isObservationEvent(event) &&\n (event.observation.kind === \"ExecuteBashObservation\" ||\n event.observation.kind === \"TerminalObservation\");\n\n/**\n * Type guard function to check if an observation event is a PlanningFileEditorObservation\n */\nexport const isPlanningFileEditorObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<PlanningFileEditorObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"PlanningFileEditorObservation\";\n\n/**\n * Type guard function to check if an observation event is a BrowserObservation\n */\nexport const isBrowserObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<BrowserObservation> =>\n isObservationEvent(event) && event.observation.kind === \"BrowserObservation\";\n\n/**\n * Type guard function to check if an observation event is a SwitchLLMObservation\n */\nexport const isSwitchLLMObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<SwitchLLMObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"SwitchLLMObservation\";\n\n/**\n * Type guard function to check if an action event is a BrowserNavigateAction\n */\nexport const isBrowserNavigateActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<BrowserNavigateAction> =>\n isActionEvent(event) && event.action.kind === \"BrowserNavigateAction\";\n\n/**\n * Type guard for the canvas_ui custom tool's ActionEvent.\n *\n * The tool is injected via tool_module_qualnames (see canvas_ui_tool.py and\n * agent-server-adapter.ts). We discriminate on tool_name (which we control\n * via register_tool(\"canvas_ui\", ...)). The predicate narrows the event so\n * the call site can read `event.action.command` etc. without further casts.\n */\nexport const isCanvasUIActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<CanvasUIAction> =>\n isActionEvent(event) && event.tool_name === \"canvas_ui\";\n\n/**\n * Type guard function to check if an event is a system prompt event\n */\nexport const isSystemPromptEvent = (\n event: OpenHandsEvent,\n): event is SystemPromptEvent =>\n event.source === \"agent\" &&\n \"system_prompt\" in event &&\n \"tools\" in event &&\n typeof event.system_prompt === \"object\" &&\n Array.isArray(event.tools);\n\n/**\n * Type guard function to check if an event is a conversation state update event\n */\nexport const isConversationStateUpdateEvent = (\n event: OpenHandsEvent,\n): event is ConversationStateUpdateEvent =>\n \"kind\" in event && event.kind === \"ConversationStateUpdateEvent\";\n\nexport const isFullStateConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventFullState => event.key === \"full_state\";\n\nexport const isAgentStatusConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventAgentStatus =>\n event.key === \"execution_status\";\n\nexport const isStatsConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventStats => event.key === \"stats\";\n\n/**\n * Type guard function to check if an event is a conversation error event\n */\nexport const isConversationErrorEvent = (\n event: OpenHandsEvent,\n): event is ConversationErrorEvent =>\n \"kind\" in event && event.kind === \"ConversationErrorEvent\";\n\n/**\n * Type guard function to check if an event is a server error event\n */\nexport const isServerErrorEvent = (\n event: OpenHandsEvent,\n): event is ServerErrorEvent =>\n \"kind\" in event && event.kind === \"ServerErrorEvent\";\n\n/**\n * Type guard function to check if an event is a displayable error event\n * (ConversationErrorEvent or ServerErrorEvent) - both should show as error banners\n */\nexport const isDisplayableErrorEvent = (event: OpenHandsEvent): boolean =>\n isConversationErrorEvent(event) || isServerErrorEvent(event);\n\n/**\n * Type guard function to check if an event is a hook execution event\n */\nexport const isHookExecutionEvent = (\n event: OpenHandsEvent,\n): event is HookExecutionEvent =>\n \"kind\" in event && event.kind === \"HookExecutionEvent\";\n\n/**\n * Type guard function to check if an event is an ACP tool call event\n */\nexport const isACPToolCallEvent = (\n event: OpenHandsEvent,\n): event is ACPToolCallEvent =>\n \"kind\" in event && event.kind === \"ACPToolCallEvent\";\n\nexport const isStreamingDeltaEvent = (\n event: OpenHandsEvent,\n): event is StreamingDeltaEvent =>\n \"kind\" in event && event.kind === \"StreamingDeltaEvent\";\n\n// =============================================================================\n// COMPATIBILITY TYPE GUARDS\n// =============================================================================\n\n/**\n * Type guard to check if an event is an agent-server OpenHandsEvent.\n * Uses isBaseEvent to validate the complete event structure.\n */\nexport function isAgentServerEvent(event: unknown): event is OpenHandsEvent {\n return isBaseEvent(event);\n}\n"],"mappings":"gDAmCA,SAAgB,EAAY,EAAoC,CAC9D,OAEE,OAAO,GAAU,YADjB,GAEA,OAAQ,GACR,cAAe,GACf,WAAY,GACZ,OAAO,EAAM,IAAO,UACpB,EAAM,GAAG,OAAS,GAClB,OAAO,EAAM,WAAc,UAC3B,EAAM,UAAU,OAAS,GACzB,OAAO,EAAM,QAAW,WACvB,EAAM,SAAW,SAChB,EAAM,SAAW,QACjB,EAAM,SAAW,eACjB,EAAM,SAAW,QAOvB,IAAa,EACX,GAEA,EAAM,SAAW,eACjB,cAAe,GACf,gBAAiB,GACjB,EAAM,cAAgB,MACtB,OAAO,EAAM,aAAgB,UAC7B,SAAU,EAAM,YAKL,EACX,GAEA,EAAM,SAAW,SACjB,cAAe,GACf,iBAAkB,GAClB,UAAW,GACX,OAAO,EAAM,WAAc,UAC3B,OAAO,EAAM,cAAiB,UAC9B,OAAO,EAAM,OAAU,SAKZ,EAAkB,GAC7B,gBAAiB,GACjB,OAAO,EAAM,aAAgB,UAC7B,EAAM,cAAgB,MACtB,SAAU,EAAM,aAChB,YAAa,EAAM,YAKR,EACX,GAEA,EAAe,EAAM,EAAI,EAAM,YAAY,OAAS,OAKzC,EAAiB,GAC5B,EAAM,SAAW,SACjB,WAAY,GACZ,EAAM,SAAW,MACjB,OAAO,EAAM,QAAW,UACxB,SAAU,EAAM,QAChB,cAAe,GACf,iBAAkB,GAClB,OAAO,EAAM,WAAc,UAC3B,OAAO,EAAM,cAAiB,SAKnB,EACX,GAEA,EAAc,EAAM,GACnB,EAAM,OAAO,OAAS,qBACrB,EAAM,OAAO,OAAS,kBAKb,EACX,GAEA,EAAmB,EAAM,GACxB,EAAM,YAAY,OAAS,0BAC1B,EAAM,YAAY,OAAS,uBAKlB,EACX,GAEA,EAAmB,EAAM,EACzB,EAAM,YAAY,OAAS,gCAKhB,EACX,GAEA,EAAmB,EAAM,EAAI,EAAM,YAAY,OAAS,qBAK7C,EACX,GAEA,EAAmB,EAAM,EACzB,EAAM,YAAY,OAAS,uBAKhB,EACX,GAEA,EAAc,EAAM,EAAI,EAAM,OAAO,OAAS,wBAUnC,EACX,GAEA,EAAc,EAAM,EAAI,EAAM,YAAc,YAKjC,EACX,GAEA,EAAM,SAAW,SACjB,kBAAmB,GACnB,UAAW,GACX,OAAO,EAAM,eAAkB,UAC/B,MAAM,QAAQ,EAAM,MAAM,CAKf,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,+BAEvB,EACX,GACmD,EAAM,MAAQ,aAEtD,EACX,GAEA,EAAM,MAAQ,mBAEH,EACX,GAC+C,EAAM,MAAQ,QAKlD,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,yBAKvB,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,mBAMvB,EAA2B,GACtC,EAAyB,EAAM,EAAI,EAAmB,EAAM,CAKjD,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,qBAKvB,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,mBAEvB,EACX,GAEA,SAAU,GAAS,EAAM,OAAS,sBAUpC,SAAgB,EAAmB,EAAyC,CAC1E,OAAO,EAAY,EAAM"}
@@ -5,6 +5,7 @@ import { ActionEvent } from "./core/events/action-event";
5
5
  import { ConversationStateUpdateEvent, ConversationStateUpdateEventAgentStatus, ConversationStateUpdateEventFullState, ConversationStateUpdateEventStats, ConversationErrorEvent, ServerErrorEvent } from "./core/events/conversation-state-event";
6
6
  import { HookExecutionEvent } from "./core/events/hook-execution-event";
7
7
  import { ACPToolCallEvent } from "./core/events/acp-tool-call-event";
8
+ import { StreamingDeltaEvent } from "./core/events/streaming-delta-event";
8
9
  import { SystemPromptEvent } from "./core/events/system-event";
9
10
  /**
10
11
  * Type guard to check if an unknown value is a valid BaseEvent
@@ -97,6 +98,7 @@ export declare const isHookExecutionEvent: (event: OpenHandsEvent) => event is H
97
98
  * Type guard function to check if an event is an ACP tool call event
98
99
  */
99
100
  export declare const isACPToolCallEvent: (event: OpenHandsEvent) => event is ACPToolCallEvent;
101
+ export declare const isStreamingDeltaEvent: (event: OpenHandsEvent) => event is StreamingDeltaEvent;
100
102
  /**
101
103
  * Type guard to check if an event is an agent-server OpenHandsEvent.
102
104
  * Uses isBaseEvent to validate the complete event structure.
@@ -2,11 +2,11 @@
2
2
  function e(e) {
3
3
  return typeof e == "object" && !!e && "id" in e && "timestamp" in e && "source" in e && typeof e.id == "string" && e.id.length > 0 && typeof e.timestamp == "string" && e.timestamp.length > 0 && typeof e.source == "string" && (e.source === "agent" || e.source === "user" || e.source === "environment" || e.source === "hook");
4
4
  }
5
- var t = (e) => e.source === "environment" && "action_id" in e && "observation" in e && e.observation !== null && typeof e.observation == "object" && "kind" in e.observation, n = (e) => e.source === "agent" && "tool_name" in e && "tool_call_id" in e && "error" in e && typeof e.tool_name == "string" && typeof e.tool_call_id == "string" && typeof e.error == "string", r = (e) => "llm_message" in e && typeof e.llm_message == "object" && e.llm_message !== null && "role" in e.llm_message && "content" in e.llm_message, i = (e) => r(e) && e.llm_message.role === "user", a = (e) => e.source === "agent" && "action" in e && e.action !== null && typeof e.action == "object" && "kind" in e.action && "tool_name" in e && "tool_call_id" in e && typeof e.tool_name == "string" && typeof e.tool_call_id == "string", o = (e) => a(e) && (e.action.kind === "ExecuteBashAction" || e.action.kind === "TerminalAction"), s = (e) => t(e) && (e.observation.kind === "ExecuteBashObservation" || e.observation.kind === "TerminalObservation"), c = (e) => t(e) && e.observation.kind === "PlanningFileEditorObservation", l = (e) => t(e) && e.observation.kind === "BrowserObservation", u = (e) => t(e) && e.observation.kind === "SwitchLLMObservation", d = (e) => a(e) && e.action.kind === "BrowserNavigateAction", f = (e) => a(e) && e.tool_name === "canvas_ui", p = (e) => e.source === "agent" && "system_prompt" in e && "tools" in e && typeof e.system_prompt == "object" && Array.isArray(e.tools), m = (e) => "kind" in e && e.kind === "ConversationStateUpdateEvent", h = (e) => e.key === "full_state", g = (e) => e.key === "execution_status", _ = (e) => e.key === "stats", v = (e) => "kind" in e && e.kind === "ConversationErrorEvent", y = (e) => "kind" in e && e.kind === "ServerErrorEvent", b = (e) => v(e) || y(e), x = (e) => "kind" in e && e.kind === "HookExecutionEvent", S = (e) => "kind" in e && e.kind === "ACPToolCallEvent";
6
- function C(t) {
5
+ var t = (e) => e.source === "environment" && "action_id" in e && "observation" in e && e.observation !== null && typeof e.observation == "object" && "kind" in e.observation, n = (e) => e.source === "agent" && "tool_name" in e && "tool_call_id" in e && "error" in e && typeof e.tool_name == "string" && typeof e.tool_call_id == "string" && typeof e.error == "string", r = (e) => "llm_message" in e && typeof e.llm_message == "object" && e.llm_message !== null && "role" in e.llm_message && "content" in e.llm_message, i = (e) => r(e) && e.llm_message.role === "user", a = (e) => e.source === "agent" && "action" in e && e.action !== null && typeof e.action == "object" && "kind" in e.action && "tool_name" in e && "tool_call_id" in e && typeof e.tool_name == "string" && typeof e.tool_call_id == "string", o = (e) => a(e) && (e.action.kind === "ExecuteBashAction" || e.action.kind === "TerminalAction"), s = (e) => t(e) && (e.observation.kind === "ExecuteBashObservation" || e.observation.kind === "TerminalObservation"), c = (e) => t(e) && e.observation.kind === "PlanningFileEditorObservation", l = (e) => t(e) && e.observation.kind === "BrowserObservation", u = (e) => t(e) && e.observation.kind === "SwitchLLMObservation", d = (e) => a(e) && e.action.kind === "BrowserNavigateAction", f = (e) => a(e) && e.tool_name === "canvas_ui", p = (e) => e.source === "agent" && "system_prompt" in e && "tools" in e && typeof e.system_prompt == "object" && Array.isArray(e.tools), m = (e) => "kind" in e && e.kind === "ConversationStateUpdateEvent", h = (e) => e.key === "full_state", g = (e) => e.key === "execution_status", _ = (e) => e.key === "stats", v = (e) => "kind" in e && e.kind === "ConversationErrorEvent", y = (e) => "kind" in e && e.kind === "ServerErrorEvent", b = (e) => v(e) || y(e), x = (e) => "kind" in e && e.kind === "HookExecutionEvent", S = (e) => "kind" in e && e.kind === "ACPToolCallEvent", C = (e) => "kind" in e && e.kind === "StreamingDeltaEvent";
6
+ function w(t) {
7
7
  return e(t);
8
8
  }
9
9
  //#endregion
10
- export { S as isACPToolCallEvent, a as isActionEvent, n as isAgentErrorEvent, C as isAgentServerEvent, g as isAgentStatusConversationStateUpdateEvent, d as isBrowserNavigateActionEvent, l as isBrowserObservationEvent, f as isCanvasUIActionEvent, m as isConversationStateUpdateEvent, b as isDisplayableErrorEvent, o as isExecuteBashActionEvent, s as isExecuteBashObservationEvent, h as isFullStateConversationStateUpdateEvent, x as isHookExecutionEvent, r as isMessageEvent, t as isObservationEvent, c as isPlanningFileEditorObservationEvent, _ as isStatsConversationStateUpdateEvent, u as isSwitchLLMObservationEvent, p as isSystemPromptEvent, i as isUserMessageEvent };
10
+ export { S as isACPToolCallEvent, a as isActionEvent, n as isAgentErrorEvent, w as isAgentServerEvent, g as isAgentStatusConversationStateUpdateEvent, d as isBrowserNavigateActionEvent, l as isBrowserObservationEvent, f as isCanvasUIActionEvent, m as isConversationStateUpdateEvent, b as isDisplayableErrorEvent, o as isExecuteBashActionEvent, s as isExecuteBashObservationEvent, h as isFullStateConversationStateUpdateEvent, x as isHookExecutionEvent, r as isMessageEvent, t as isObservationEvent, c as isPlanningFileEditorObservationEvent, _ as isStatsConversationStateUpdateEvent, C as isStreamingDeltaEvent, u as isSwitchLLMObservationEvent, p as isSystemPromptEvent, i as isUserMessageEvent };
11
11
 
12
12
  //# sourceMappingURL=type-guards.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"type-guards.js","names":[],"sources":["../../../src/types/agent-server/type-guards.ts"],"sourcesContent":["import {\n OpenHandsEvent,\n ObservationEvent,\n BaseEvent,\n ExecuteBashAction,\n TerminalAction,\n ExecuteBashObservation,\n PlanningFileEditorObservation,\n TerminalObservation,\n BrowserObservation,\n BrowserNavigateAction,\n SwitchLLMObservation,\n CanvasUIAction,\n} from \"./core\";\nimport { AgentErrorEvent } from \"./core/events/observation-event\";\nimport { MessageEvent } from \"./core/events/message-event\";\nimport { ActionEvent } from \"./core/events/action-event\";\nimport {\n ConversationStateUpdateEvent,\n ConversationStateUpdateEventAgentStatus,\n ConversationStateUpdateEventFullState,\n ConversationStateUpdateEventStats,\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"./core/events/conversation-state-event\";\nimport { HookExecutionEvent } from \"./core/events/hook-execution-event\";\nimport { ACPToolCallEvent } from \"./core/events/acp-tool-call-event\";\nimport { SystemPromptEvent } from \"./core/events/system-event\";\n\n/**\n * Type guard to check if an unknown value is a valid BaseEvent\n * @param value - The value to check\n * @returns true if the value is a valid BaseEvent\n */\nexport function isBaseEvent(value: unknown): value is BaseEvent {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"id\" in value &&\n \"timestamp\" in value &&\n \"source\" in value &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n typeof value.timestamp === \"string\" &&\n value.timestamp.length > 0 &&\n typeof value.source === \"string\" &&\n (value.source === \"agent\" ||\n value.source === \"user\" ||\n value.source === \"environment\" ||\n value.source === \"hook\")\n );\n}\n\n/**\n * Type guard function to check if an event is an observation event\n */\nexport const isObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent =>\n event.source === \"environment\" &&\n \"action_id\" in event &&\n \"observation\" in event &&\n event.observation !== null &&\n typeof event.observation === \"object\" &&\n \"kind\" in event.observation;\n\n/**\n * Type guard function to check if an event is an agent error event\n */\nexport const isAgentErrorEvent = (\n event: OpenHandsEvent,\n): event is AgentErrorEvent =>\n event.source === \"agent\" &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n \"error\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\" &&\n typeof event.error === \"string\";\n\n/**\n * Type guard function to check if an event is a message event (user or assistant)\n */\nexport const isMessageEvent = (event: OpenHandsEvent): event is MessageEvent =>\n \"llm_message\" in event &&\n typeof event.llm_message === \"object\" &&\n event.llm_message !== null &&\n \"role\" in event.llm_message &&\n \"content\" in event.llm_message;\n\n/**\n * Type guard function to check if an event is a user message event\n */\nexport const isUserMessageEvent = (\n event: OpenHandsEvent,\n): event is MessageEvent =>\n isMessageEvent(event) && event.llm_message.role === \"user\";\n\n/**\n * Type guard function to check if an event is an action event\n */\nexport const isActionEvent = (event: OpenHandsEvent): event is ActionEvent =>\n event.source === \"agent\" &&\n \"action\" in event &&\n event.action !== null &&\n typeof event.action === \"object\" &&\n \"kind\" in event.action &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\";\n\n/**\n * Type guard function to check if an action event is an ExecuteBashAction\n */\nexport const isExecuteBashActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<ExecuteBashAction | TerminalAction> =>\n isActionEvent(event) &&\n (event.action.kind === \"ExecuteBashAction\" ||\n event.action.kind === \"TerminalAction\");\n\n/**\n * Type guard function to check if an observation event contains terminal output\n */\nexport const isExecuteBashObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<ExecuteBashObservation | TerminalObservation> =>\n isObservationEvent(event) &&\n (event.observation.kind === \"ExecuteBashObservation\" ||\n event.observation.kind === \"TerminalObservation\");\n\n/**\n * Type guard function to check if an observation event is a PlanningFileEditorObservation\n */\nexport const isPlanningFileEditorObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<PlanningFileEditorObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"PlanningFileEditorObservation\";\n\n/**\n * Type guard function to check if an observation event is a BrowserObservation\n */\nexport const isBrowserObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<BrowserObservation> =>\n isObservationEvent(event) && event.observation.kind === \"BrowserObservation\";\n\n/**\n * Type guard function to check if an observation event is a SwitchLLMObservation\n */\nexport const isSwitchLLMObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<SwitchLLMObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"SwitchLLMObservation\";\n\n/**\n * Type guard function to check if an action event is a BrowserNavigateAction\n */\nexport const isBrowserNavigateActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<BrowserNavigateAction> =>\n isActionEvent(event) && event.action.kind === \"BrowserNavigateAction\";\n\n/**\n * Type guard for the canvas_ui custom tool's ActionEvent.\n *\n * The tool is injected via tool_module_qualnames (see canvas_ui_tool.py and\n * agent-server-adapter.ts). We discriminate on tool_name (which we control\n * via register_tool(\"canvas_ui\", ...)). The predicate narrows the event so\n * the call site can read `event.action.command` etc. without further casts.\n */\nexport const isCanvasUIActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<CanvasUIAction> =>\n isActionEvent(event) && event.tool_name === \"canvas_ui\";\n\n/**\n * Type guard function to check if an event is a system prompt event\n */\nexport const isSystemPromptEvent = (\n event: OpenHandsEvent,\n): event is SystemPromptEvent =>\n event.source === \"agent\" &&\n \"system_prompt\" in event &&\n \"tools\" in event &&\n typeof event.system_prompt === \"object\" &&\n Array.isArray(event.tools);\n\n/**\n * Type guard function to check if an event is a conversation state update event\n */\nexport const isConversationStateUpdateEvent = (\n event: OpenHandsEvent,\n): event is ConversationStateUpdateEvent =>\n \"kind\" in event && event.kind === \"ConversationStateUpdateEvent\";\n\nexport const isFullStateConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventFullState => event.key === \"full_state\";\n\nexport const isAgentStatusConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventAgentStatus =>\n event.key === \"execution_status\";\n\nexport const isStatsConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventStats => event.key === \"stats\";\n\n/**\n * Type guard function to check if an event is a conversation error event\n */\nexport const isConversationErrorEvent = (\n event: OpenHandsEvent,\n): event is ConversationErrorEvent =>\n \"kind\" in event && event.kind === \"ConversationErrorEvent\";\n\n/**\n * Type guard function to check if an event is a server error event\n */\nexport const isServerErrorEvent = (\n event: OpenHandsEvent,\n): event is ServerErrorEvent =>\n \"kind\" in event && event.kind === \"ServerErrorEvent\";\n\n/**\n * Type guard function to check if an event is a displayable error event\n * (ConversationErrorEvent or ServerErrorEvent) - both should show as error banners\n */\nexport const isDisplayableErrorEvent = (event: OpenHandsEvent): boolean =>\n isConversationErrorEvent(event) || isServerErrorEvent(event);\n\n/**\n * Type guard function to check if an event is a hook execution event\n */\nexport const isHookExecutionEvent = (\n event: OpenHandsEvent,\n): event is HookExecutionEvent =>\n \"kind\" in event && event.kind === \"HookExecutionEvent\";\n\n/**\n * Type guard function to check if an event is an ACP tool call event\n */\nexport const isACPToolCallEvent = (\n event: OpenHandsEvent,\n): event is ACPToolCallEvent =>\n \"kind\" in event && event.kind === \"ACPToolCallEvent\";\n\n// =============================================================================\n// COMPATIBILITY TYPE GUARDS\n// =============================================================================\n\n/**\n * Type guard to check if an event is an agent-server OpenHandsEvent.\n * Uses isBaseEvent to validate the complete event structure.\n */\nexport function isAgentServerEvent(event: unknown): event is OpenHandsEvent {\n return isBaseEvent(event);\n}\n"],"mappings":";AAkCA,SAAgB,EAAY,GAAoC;AAC9D,QAEE,OAAO,KAAU,cADjB,KAEA,QAAQ,KACR,eAAe,KACf,YAAY,KACZ,OAAO,EAAM,MAAO,YACpB,EAAM,GAAG,SAAS,KAClB,OAAO,EAAM,aAAc,YAC3B,EAAM,UAAU,SAAS,KACzB,OAAO,EAAM,UAAW,aACvB,EAAM,WAAW,WAChB,EAAM,WAAW,UACjB,EAAM,WAAW,iBACjB,EAAM,WAAW;;AAOvB,IAAa,KACX,MAEA,EAAM,WAAW,iBACjB,eAAe,KACf,iBAAiB,KACjB,EAAM,gBAAgB,QACtB,OAAO,EAAM,eAAgB,YAC7B,UAAU,EAAM,aAKL,KACX,MAEA,EAAM,WAAW,WACjB,eAAe,KACf,kBAAkB,KAClB,WAAW,KACX,OAAO,EAAM,aAAc,YAC3B,OAAO,EAAM,gBAAiB,YAC9B,OAAO,EAAM,SAAU,UAKZ,KAAkB,MAC7B,iBAAiB,KACjB,OAAO,EAAM,eAAgB,YAC7B,EAAM,gBAAgB,QACtB,UAAU,EAAM,eAChB,aAAa,EAAM,aAKR,KACX,MAEA,EAAe,EAAM,IAAI,EAAM,YAAY,SAAS,QAKzC,KAAiB,MAC5B,EAAM,WAAW,WACjB,YAAY,KACZ,EAAM,WAAW,QACjB,OAAO,EAAM,UAAW,YACxB,UAAU,EAAM,UAChB,eAAe,KACf,kBAAkB,KAClB,OAAO,EAAM,aAAc,YAC3B,OAAO,EAAM,gBAAiB,UAKnB,KACX,MAEA,EAAc,EAAM,KACnB,EAAM,OAAO,SAAS,uBACrB,EAAM,OAAO,SAAS,mBAKb,KACX,MAEA,EAAmB,EAAM,KACxB,EAAM,YAAY,SAAS,4BAC1B,EAAM,YAAY,SAAS,wBAKlB,KACX,MAEA,EAAmB,EAAM,IACzB,EAAM,YAAY,SAAS,iCAKhB,KACX,MAEA,EAAmB,EAAM,IAAI,EAAM,YAAY,SAAS,sBAK7C,KACX,MAEA,EAAmB,EAAM,IACzB,EAAM,YAAY,SAAS,wBAKhB,KACX,MAEA,EAAc,EAAM,IAAI,EAAM,OAAO,SAAS,yBAUnC,KACX,MAEA,EAAc,EAAM,IAAI,EAAM,cAAc,aAKjC,KACX,MAEA,EAAM,WAAW,WACjB,mBAAmB,KACnB,WAAW,KACX,OAAO,EAAM,iBAAkB,YAC/B,MAAM,QAAQ,EAAM,MAAM,EAKf,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,gCAEvB,KACX,MACmD,EAAM,QAAQ,cAEtD,KACX,MAEA,EAAM,QAAQ,oBAEH,KACX,MAC+C,EAAM,QAAQ,SAKlD,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,0BAKvB,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,oBAMvB,KAA2B,MACtC,EAAyB,EAAM,IAAI,EAAmB,EAAM,EAKjD,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,sBAKvB,KACX,MAEA,UAAU,KAAS,EAAM,SAAS;AAUpC,SAAgB,EAAmB,GAAyC;AAC1E,QAAO,EAAY,EAAM"}
1
+ {"version":3,"file":"type-guards.js","names":[],"sources":["../../../src/types/agent-server/type-guards.ts"],"sourcesContent":["import {\n OpenHandsEvent,\n ObservationEvent,\n BaseEvent,\n ExecuteBashAction,\n TerminalAction,\n ExecuteBashObservation,\n PlanningFileEditorObservation,\n TerminalObservation,\n BrowserObservation,\n BrowserNavigateAction,\n SwitchLLMObservation,\n CanvasUIAction,\n} from \"./core\";\nimport { AgentErrorEvent } from \"./core/events/observation-event\";\nimport { MessageEvent } from \"./core/events/message-event\";\nimport { ActionEvent } from \"./core/events/action-event\";\nimport {\n ConversationStateUpdateEvent,\n ConversationStateUpdateEventAgentStatus,\n ConversationStateUpdateEventFullState,\n ConversationStateUpdateEventStats,\n ConversationErrorEvent,\n ServerErrorEvent,\n} from \"./core/events/conversation-state-event\";\nimport { HookExecutionEvent } from \"./core/events/hook-execution-event\";\nimport { ACPToolCallEvent } from \"./core/events/acp-tool-call-event\";\nimport { StreamingDeltaEvent } from \"./core/events/streaming-delta-event\";\nimport { SystemPromptEvent } from \"./core/events/system-event\";\n\n/**\n * Type guard to check if an unknown value is a valid BaseEvent\n * @param value - The value to check\n * @returns true if the value is a valid BaseEvent\n */\nexport function isBaseEvent(value: unknown): value is BaseEvent {\n return (\n value !== null &&\n typeof value === \"object\" &&\n \"id\" in value &&\n \"timestamp\" in value &&\n \"source\" in value &&\n typeof value.id === \"string\" &&\n value.id.length > 0 &&\n typeof value.timestamp === \"string\" &&\n value.timestamp.length > 0 &&\n typeof value.source === \"string\" &&\n (value.source === \"agent\" ||\n value.source === \"user\" ||\n value.source === \"environment\" ||\n value.source === \"hook\")\n );\n}\n\n/**\n * Type guard function to check if an event is an observation event\n */\nexport const isObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent =>\n event.source === \"environment\" &&\n \"action_id\" in event &&\n \"observation\" in event &&\n event.observation !== null &&\n typeof event.observation === \"object\" &&\n \"kind\" in event.observation;\n\n/**\n * Type guard function to check if an event is an agent error event\n */\nexport const isAgentErrorEvent = (\n event: OpenHandsEvent,\n): event is AgentErrorEvent =>\n event.source === \"agent\" &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n \"error\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\" &&\n typeof event.error === \"string\";\n\n/**\n * Type guard function to check if an event is a message event (user or assistant)\n */\nexport const isMessageEvent = (event: OpenHandsEvent): event is MessageEvent =>\n \"llm_message\" in event &&\n typeof event.llm_message === \"object\" &&\n event.llm_message !== null &&\n \"role\" in event.llm_message &&\n \"content\" in event.llm_message;\n\n/**\n * Type guard function to check if an event is a user message event\n */\nexport const isUserMessageEvent = (\n event: OpenHandsEvent,\n): event is MessageEvent =>\n isMessageEvent(event) && event.llm_message.role === \"user\";\n\n/**\n * Type guard function to check if an event is an action event\n */\nexport const isActionEvent = (event: OpenHandsEvent): event is ActionEvent =>\n event.source === \"agent\" &&\n \"action\" in event &&\n event.action !== null &&\n typeof event.action === \"object\" &&\n \"kind\" in event.action &&\n \"tool_name\" in event &&\n \"tool_call_id\" in event &&\n typeof event.tool_name === \"string\" &&\n typeof event.tool_call_id === \"string\";\n\n/**\n * Type guard function to check if an action event is an ExecuteBashAction\n */\nexport const isExecuteBashActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<ExecuteBashAction | TerminalAction> =>\n isActionEvent(event) &&\n (event.action.kind === \"ExecuteBashAction\" ||\n event.action.kind === \"TerminalAction\");\n\n/**\n * Type guard function to check if an observation event contains terminal output\n */\nexport const isExecuteBashObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<ExecuteBashObservation | TerminalObservation> =>\n isObservationEvent(event) &&\n (event.observation.kind === \"ExecuteBashObservation\" ||\n event.observation.kind === \"TerminalObservation\");\n\n/**\n * Type guard function to check if an observation event is a PlanningFileEditorObservation\n */\nexport const isPlanningFileEditorObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<PlanningFileEditorObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"PlanningFileEditorObservation\";\n\n/**\n * Type guard function to check if an observation event is a BrowserObservation\n */\nexport const isBrowserObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<BrowserObservation> =>\n isObservationEvent(event) && event.observation.kind === \"BrowserObservation\";\n\n/**\n * Type guard function to check if an observation event is a SwitchLLMObservation\n */\nexport const isSwitchLLMObservationEvent = (\n event: OpenHandsEvent,\n): event is ObservationEvent<SwitchLLMObservation> =>\n isObservationEvent(event) &&\n event.observation.kind === \"SwitchLLMObservation\";\n\n/**\n * Type guard function to check if an action event is a BrowserNavigateAction\n */\nexport const isBrowserNavigateActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<BrowserNavigateAction> =>\n isActionEvent(event) && event.action.kind === \"BrowserNavigateAction\";\n\n/**\n * Type guard for the canvas_ui custom tool's ActionEvent.\n *\n * The tool is injected via tool_module_qualnames (see canvas_ui_tool.py and\n * agent-server-adapter.ts). We discriminate on tool_name (which we control\n * via register_tool(\"canvas_ui\", ...)). The predicate narrows the event so\n * the call site can read `event.action.command` etc. without further casts.\n */\nexport const isCanvasUIActionEvent = (\n event: OpenHandsEvent,\n): event is ActionEvent<CanvasUIAction> =>\n isActionEvent(event) && event.tool_name === \"canvas_ui\";\n\n/**\n * Type guard function to check if an event is a system prompt event\n */\nexport const isSystemPromptEvent = (\n event: OpenHandsEvent,\n): event is SystemPromptEvent =>\n event.source === \"agent\" &&\n \"system_prompt\" in event &&\n \"tools\" in event &&\n typeof event.system_prompt === \"object\" &&\n Array.isArray(event.tools);\n\n/**\n * Type guard function to check if an event is a conversation state update event\n */\nexport const isConversationStateUpdateEvent = (\n event: OpenHandsEvent,\n): event is ConversationStateUpdateEvent =>\n \"kind\" in event && event.kind === \"ConversationStateUpdateEvent\";\n\nexport const isFullStateConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventFullState => event.key === \"full_state\";\n\nexport const isAgentStatusConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventAgentStatus =>\n event.key === \"execution_status\";\n\nexport const isStatsConversationStateUpdateEvent = (\n event: ConversationStateUpdateEvent,\n): event is ConversationStateUpdateEventStats => event.key === \"stats\";\n\n/**\n * Type guard function to check if an event is a conversation error event\n */\nexport const isConversationErrorEvent = (\n event: OpenHandsEvent,\n): event is ConversationErrorEvent =>\n \"kind\" in event && event.kind === \"ConversationErrorEvent\";\n\n/**\n * Type guard function to check if an event is a server error event\n */\nexport const isServerErrorEvent = (\n event: OpenHandsEvent,\n): event is ServerErrorEvent =>\n \"kind\" in event && event.kind === \"ServerErrorEvent\";\n\n/**\n * Type guard function to check if an event is a displayable error event\n * (ConversationErrorEvent or ServerErrorEvent) - both should show as error banners\n */\nexport const isDisplayableErrorEvent = (event: OpenHandsEvent): boolean =>\n isConversationErrorEvent(event) || isServerErrorEvent(event);\n\n/**\n * Type guard function to check if an event is a hook execution event\n */\nexport const isHookExecutionEvent = (\n event: OpenHandsEvent,\n): event is HookExecutionEvent =>\n \"kind\" in event && event.kind === \"HookExecutionEvent\";\n\n/**\n * Type guard function to check if an event is an ACP tool call event\n */\nexport const isACPToolCallEvent = (\n event: OpenHandsEvent,\n): event is ACPToolCallEvent =>\n \"kind\" in event && event.kind === \"ACPToolCallEvent\";\n\nexport const isStreamingDeltaEvent = (\n event: OpenHandsEvent,\n): event is StreamingDeltaEvent =>\n \"kind\" in event && event.kind === \"StreamingDeltaEvent\";\n\n// =============================================================================\n// COMPATIBILITY TYPE GUARDS\n// =============================================================================\n\n/**\n * Type guard to check if an event is an agent-server OpenHandsEvent.\n * Uses isBaseEvent to validate the complete event structure.\n */\nexport function isAgentServerEvent(event: unknown): event is OpenHandsEvent {\n return isBaseEvent(event);\n}\n"],"mappings":";AAmCA,SAAgB,EAAY,GAAoC;AAC9D,QAEE,OAAO,KAAU,cADjB,KAEA,QAAQ,KACR,eAAe,KACf,YAAY,KACZ,OAAO,EAAM,MAAO,YACpB,EAAM,GAAG,SAAS,KAClB,OAAO,EAAM,aAAc,YAC3B,EAAM,UAAU,SAAS,KACzB,OAAO,EAAM,UAAW,aACvB,EAAM,WAAW,WAChB,EAAM,WAAW,UACjB,EAAM,WAAW,iBACjB,EAAM,WAAW;;AAOvB,IAAa,KACX,MAEA,EAAM,WAAW,iBACjB,eAAe,KACf,iBAAiB,KACjB,EAAM,gBAAgB,QACtB,OAAO,EAAM,eAAgB,YAC7B,UAAU,EAAM,aAKL,KACX,MAEA,EAAM,WAAW,WACjB,eAAe,KACf,kBAAkB,KAClB,WAAW,KACX,OAAO,EAAM,aAAc,YAC3B,OAAO,EAAM,gBAAiB,YAC9B,OAAO,EAAM,SAAU,UAKZ,KAAkB,MAC7B,iBAAiB,KACjB,OAAO,EAAM,eAAgB,YAC7B,EAAM,gBAAgB,QACtB,UAAU,EAAM,eAChB,aAAa,EAAM,aAKR,KACX,MAEA,EAAe,EAAM,IAAI,EAAM,YAAY,SAAS,QAKzC,KAAiB,MAC5B,EAAM,WAAW,WACjB,YAAY,KACZ,EAAM,WAAW,QACjB,OAAO,EAAM,UAAW,YACxB,UAAU,EAAM,UAChB,eAAe,KACf,kBAAkB,KAClB,OAAO,EAAM,aAAc,YAC3B,OAAO,EAAM,gBAAiB,UAKnB,KACX,MAEA,EAAc,EAAM,KACnB,EAAM,OAAO,SAAS,uBACrB,EAAM,OAAO,SAAS,mBAKb,KACX,MAEA,EAAmB,EAAM,KACxB,EAAM,YAAY,SAAS,4BAC1B,EAAM,YAAY,SAAS,wBAKlB,KACX,MAEA,EAAmB,EAAM,IACzB,EAAM,YAAY,SAAS,iCAKhB,KACX,MAEA,EAAmB,EAAM,IAAI,EAAM,YAAY,SAAS,sBAK7C,KACX,MAEA,EAAmB,EAAM,IACzB,EAAM,YAAY,SAAS,wBAKhB,KACX,MAEA,EAAc,EAAM,IAAI,EAAM,OAAO,SAAS,yBAUnC,KACX,MAEA,EAAc,EAAM,IAAI,EAAM,cAAc,aAKjC,KACX,MAEA,EAAM,WAAW,WACjB,mBAAmB,KACnB,WAAW,KACX,OAAO,EAAM,iBAAkB,YAC/B,MAAM,QAAQ,EAAM,MAAM,EAKf,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,gCAEvB,KACX,MACmD,EAAM,QAAQ,cAEtD,KACX,MAEA,EAAM,QAAQ,oBAEH,KACX,MAC+C,EAAM,QAAQ,SAKlD,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,0BAKvB,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,oBAMvB,KAA2B,MACtC,EAAyB,EAAM,IAAI,EAAmB,EAAM,EAKjD,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,sBAKvB,KACX,MAEA,UAAU,KAAS,EAAM,SAAS,oBAEvB,KACX,MAEA,UAAU,KAAS,EAAM,SAAS;AAUpC,SAAgB,EAAmB,GAAyC;AAC1E,QAAO,EAAY,EAAM"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../types/agent-server/type-guards.cjs`);var t=(t,n)=>{let r=[...n];if(e.isACPToolCallEvent(t)){let n=r.findIndex(n=>e.isACPToolCallEvent(n)&&n.tool_call_id===t.tool_call_id);return n===-1?r.push(t):r[n]=t,r}if(e.isObservationEvent(t)){if(t.observation.kind===`ThinkObservation`||t.observation.kind===`FinishObservation`)return r;let e=r.findIndex(e=>e.id===t.action_id);e===-1?r.push(t):r[e]=t}else r.push(t);return r};exports.handleEventForUI=t;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);const e=require(`../types/agent-server/type-guards.cjs`);var t=(e,t)=>({...t,content:`${t.content??``}${e.content??``}`||null,reasoning_content:`${t.reasoning_content??``}${e.reasoning_content??``}`||null}),n=(e,t)=>({...e,content:`${e.content??``}${t}`||null}),r=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},i=e=>e.llm_message.content.filter(e=>e.type===`text`).map(e=>e.text).join(``),a=t=>e.isActionEvent(t)&&t.action.kind===`FinishAction`?t.action.message:e.isMessageEvent(t)&&t.source===`agent`?i(t):null,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,i)=>{let s=r(i),c=i.map((e,t)=>({uiEvent:e,index:t})).filter(({uiEvent:t,index:n})=>n>s&&e.isStreamingDeltaEvent(t)).map(({index:e})=>e);if(c.length===0)return null;let l=a(t),u=c.map(e=>({event:i[e],index:e})).filter(t=>e.isStreamingDeltaEvent(t.event)&&(t.event.content?.length??0)>0),d=u.map(({event:e})=>e.content??``);if(!l||d.length===0)return null;let f=[...i],p=d.join(``),m=``;if(l.startsWith(p))m=l.slice(p.length);else{let e=o(l,d);if(!e.matched)return null;m=l.slice(e.lastMatchEnd)}let h=u.at(-1)?.index,g=h===void 0?void 0:f[h];return m&&h!==void 0&&g&&e.isStreamingDeltaEvent(g)&&(f[h]=n(g,m)),f},c=(n,r)=>{let i=[...r];if(e.isStreamingDeltaEvent(n)){if(n.content===null&&n.reasoning_content===null)return i;let r=i.length-1,a=i[r];return a&&e.isStreamingDeltaEvent(a)?(i[r]=t(n,a),i):(i.push(n),i)}if(e.isActionEvent(n)&&n.action.kind===`FinishAction`||e.isMessageEvent(n)&&n.source===`agent`){let e=s(n,i);if(e)return e}if(e.isACPToolCallEvent(n)){let t=i.findIndex(t=>e.isACPToolCallEvent(t)&&t.tool_call_id===n.tool_call_id);return t===-1?i.push(n):i[t]=n,i}if(e.isObservationEvent(n)){if(n.observation.kind===`ThinkObservation`||n.observation.kind===`FinishObservation`)return i;let e=i.findIndex(e=>e.id===n.action_id);e===-1?i.push(n):i[e]=n}else i.push(n);return i};exports.handleEventForUI=c;
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 { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isObservationEvent,\n} from \"#/types/agent-server/type-guards\";\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 (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":"sGAkBA,IAAa,GACX,EACA,IACqB,CACrB,IAAM,EAAc,CAAC,GAAG,EAAS,CAEjC,GAAI,EAAA,mBAAmB,EAAM,CAAE,CAC7B,IAAM,EAAgB,EAAY,UAC/B,GACC,EAAA,mBAAmB,EAAQ,EAC3B,EAAQ,eAAiB,EAAM,aAClC,CAMD,OALI,IAAkB,GAGpB,EAAY,KAAK,EAAM,CAFvB,EAAY,GAAiB,EAIxB,EAGT,GAAI,EAAA,mBAAmB,EAAM,CAAE,CAU7B,GAPI,EAAM,YAAY,OAAS,oBAO3B,EAAM,YAAY,OAAS,oBAC7B,OAAO,EAIT,IAAM,EAAc,EAAY,UAC7B,GAAY,EAAQ,KAAO,EAAM,UACnC,CACG,IAAgB,GAIlB,EAAY,KAAK,EAAM,CAHvB,EAAY,GAAe,OAO7B,EAAY,KAAK,EAAM,CAGzB,OAAO"}
1
+ {"version":3,"file":"handle-event-for-ui.cjs","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import { MessageEvent, OpenHandsEvent } 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\";\n\nconst 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 MessageEvent against the already-rendered streaming delta.\nconst getAgentMessageText = (event: MessageEvent): string =>\n event.llm_message.content\n .filter((content) => content.type === \"text\")\n .map((content) => content.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 getAgentMessageText(event);\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\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n const currentTurnStreamingDeltaIndexes = uiEvents\n .map((uiEvent, index) => ({ uiEvent, index }))\n .filter(\n ({ uiEvent, index }) =>\n index > lastUserMessageIndex && isStreamingDeltaEvent(uiEvent),\n )\n .map(({ index }) => index);\n\n if (currentTurnStreamingDeltaIndexes.length === 0) {\n return null;\n }\n\n const finalText = getFinalAgentText(finalEvent);\n // Only the regular `content` field participates in reconciliation.\n // Reasoning-only deltas (those that carry only `reasoning_content`) produce\n // an empty streamingSegments list, causing the function to return null so\n // the finalEvent is appended normally. This is intentional: reasoning\n // content renders in its own collapsed bubble and never overlaps with the\n // assistant's regular message text in `FinishAction.message`.\n const contentStreamingDeltas = currentTurnStreamingDeltaIndexes\n .map((index) => ({ event: uiEvents[index], index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0,\n );\n const streamingSegments = contentStreamingDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n if (!finalText || streamingSegments.length === 0) {\n return null;\n }\n\n const nextUiEvents = [...uiEvents];\n const streamedText = streamingSegments.join(\"\");\n let unstreamedSuffix = \"\";\n\n if (finalText.startsWith(streamedText)) {\n unstreamedSuffix = finalText.slice(streamedText.length);\n } else {\n const match = findTextSegmentsInOrder(finalText, streamingSegments);\n if (!match.matched) {\n return null;\n }\n unstreamedSuffix = finalText.slice(match.lastMatchEnd);\n }\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 * 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 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,19 +1,75 @@
1
- import { isACPToolCallEvent as e, isObservationEvent as t } from "../types/agent-server/type-guards.js";
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
2
  //#region src/utils/handle-event-for-ui.ts
3
- var n = (n, r) => {
4
- let i = [...r];
5
- if (e(n)) {
6
- let t = i.findIndex((t) => e(t) && t.tool_call_id === n.tool_call_id);
7
- return t === -1 ? i.push(n) : i[t] = n, i;
3
+ var a = (e, t) => ({
4
+ ...t,
5
+ content: `${t.content ?? ""}${e.content ?? ""}` || null,
6
+ reasoning_content: `${t.reasoning_content ?? ""}${e.reasoning_content ?? ""}` || null
7
+ }), o = (e, t) => ({
8
+ ...e,
9
+ content: `${e.content ?? ""}${t}` || null
10
+ }), s = (e) => {
11
+ for (let t = e.length - 1; t >= 0; --t) {
12
+ let r = e[t];
13
+ if (n(r) && r.source === "user") return t;
8
14
  }
9
- if (t(n)) {
10
- if (n.observation.kind === "ThinkObservation" || n.observation.kind === "FinishObservation") return i;
11
- let e = i.findIndex((e) => e.id === n.action_id);
12
- e === -1 ? i.push(n) : i[e] = n;
13
- } else i.push(n);
14
- return i;
15
+ return -1;
16
+ }, c = (e) => e.llm_message.content.filter((e) => e.type === "text").map((e) => e.text).join(""), l = (e) => t(e) && e.action.kind === "FinishAction" ? e.action.message : n(e) && e.source === "agent" ? c(e) : null, u = (e, t) => {
17
+ let n = 0, r = 0;
18
+ for (let i of t) {
19
+ let t = e.indexOf(i, n);
20
+ if (t === -1) return {
21
+ matched: !1,
22
+ lastMatchEnd: r
23
+ };
24
+ r = t + i.length, n = r;
25
+ }
26
+ return {
27
+ matched: !0,
28
+ lastMatchEnd: r
29
+ };
30
+ }, d = (e, t) => {
31
+ let n = s(t), r = t.map((e, t) => ({
32
+ uiEvent: e,
33
+ index: t
34
+ })).filter(({ uiEvent: e, index: t }) => t > n && i(e)).map(({ index: e }) => e);
35
+ if (r.length === 0) return null;
36
+ let a = l(e), c = r.map((e) => ({
37
+ event: t[e],
38
+ index: e
39
+ })).filter((e) => i(e.event) && (e.event.content?.length ?? 0) > 0), d = c.map(({ event: e }) => e.content ?? "");
40
+ if (!a || d.length === 0) return null;
41
+ let f = [...t], p = d.join(""), m = "";
42
+ if (a.startsWith(p)) m = a.slice(p.length);
43
+ else {
44
+ let e = u(a, d);
45
+ if (!e.matched) return null;
46
+ m = a.slice(e.lastMatchEnd);
47
+ }
48
+ let h = c.at(-1)?.index, g = h === void 0 ? void 0 : f[h];
49
+ return m && h !== void 0 && g && i(g) && (f[h] = o(g, m)), f;
50
+ }, f = (o, s) => {
51
+ let c = [...s];
52
+ if (i(o)) {
53
+ if (o.content === null && o.reasoning_content === null) return c;
54
+ let e = c.length - 1, t = c[e];
55
+ return t && i(t) ? (c[e] = a(o, t), c) : (c.push(o), c);
56
+ }
57
+ if (t(o) && o.action.kind === "FinishAction" || n(o) && o.source === "agent") {
58
+ let e = d(o, c);
59
+ if (e) return e;
60
+ }
61
+ if (e(o)) {
62
+ let t = c.findIndex((t) => e(t) && t.tool_call_id === o.tool_call_id);
63
+ return t === -1 ? c.push(o) : c[t] = o, c;
64
+ }
65
+ if (r(o)) {
66
+ if (o.observation.kind === "ThinkObservation" || o.observation.kind === "FinishObservation") return c;
67
+ let e = c.findIndex((e) => e.id === o.action_id);
68
+ e === -1 ? c.push(o) : c[e] = o;
69
+ } else c.push(o);
70
+ return c;
15
71
  };
16
72
  //#endregion
17
- export { n as handleEventForUI };
73
+ export { f as handleEventForUI };
18
74
 
19
75
  //# 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 { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isACPToolCallEvent,\n isObservationEvent,\n} from \"#/types/agent-server/type-guards\";\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 (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":";;AAkBA,IAAa,KACX,GACA,MACqB;CACrB,IAAM,IAAc,CAAC,GAAG,EAAS;AAEjC,KAAI,EAAmB,EAAM,EAAE;EAC7B,IAAM,IAAgB,EAAY,WAC/B,MACC,EAAmB,EAAQ,IAC3B,EAAQ,iBAAiB,EAAM,aAClC;AAMD,SALI,MAAkB,KAGpB,EAAY,KAAK,EAAM,GAFvB,EAAY,KAAiB,GAIxB;;AAGT,KAAI,EAAmB,EAAM,EAAE;AAU7B,MAPI,EAAM,YAAY,SAAS,sBAO3B,EAAM,YAAY,SAAS,oBAC7B,QAAO;EAIT,IAAM,IAAc,EAAY,WAC7B,MAAY,EAAQ,OAAO,EAAM,UACnC;AACD,EAAI,MAAgB,KAIlB,EAAY,KAAK,EAAM,GAHvB,EAAY,KAAe;OAO7B,GAAY,KAAK,EAAM;AAGzB,QAAO"}
1
+ {"version":3,"file":"handle-event-for-ui.js","names":[],"sources":["../../src/utils/handle-event-for-ui.ts"],"sourcesContent":["import { MessageEvent, OpenHandsEvent } 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\";\n\nconst 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 MessageEvent against the already-rendered streaming delta.\nconst getAgentMessageText = (event: MessageEvent): string =>\n event.llm_message.content\n .filter((content) => content.type === \"text\")\n .map((content) => content.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 getAgentMessageText(event);\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\nconst finalizeStreamingDeltasInPlace = (\n finalEvent: OpenHandsEvent,\n uiEvents: OpenHandsEvent[],\n): OpenHandsEvent[] | null => {\n const lastUserMessageIndex = findLastUserMessageIndex(uiEvents);\n const currentTurnStreamingDeltaIndexes = uiEvents\n .map((uiEvent, index) => ({ uiEvent, index }))\n .filter(\n ({ uiEvent, index }) =>\n index > lastUserMessageIndex && isStreamingDeltaEvent(uiEvent),\n )\n .map(({ index }) => index);\n\n if (currentTurnStreamingDeltaIndexes.length === 0) {\n return null;\n }\n\n const finalText = getFinalAgentText(finalEvent);\n // Only the regular `content` field participates in reconciliation.\n // Reasoning-only deltas (those that carry only `reasoning_content`) produce\n // an empty streamingSegments list, causing the function to return null so\n // the finalEvent is appended normally. This is intentional: reasoning\n // content renders in its own collapsed bubble and never overlaps with the\n // assistant's regular message text in `FinishAction.message`.\n const contentStreamingDeltas = currentTurnStreamingDeltaIndexes\n .map((index) => ({ event: uiEvents[index], index }))\n .filter(\n (item): item is { event: StreamingDeltaEvent; index: number } =>\n isStreamingDeltaEvent(item.event) &&\n (item.event.content?.length ?? 0) > 0,\n );\n const streamingSegments = contentStreamingDeltas.map(\n ({ event }) => event.content ?? \"\",\n );\n\n if (!finalText || streamingSegments.length === 0) {\n return null;\n }\n\n const nextUiEvents = [...uiEvents];\n const streamedText = streamingSegments.join(\"\");\n let unstreamedSuffix = \"\";\n\n if (finalText.startsWith(streamedText)) {\n unstreamedSuffix = finalText.slice(streamedText.length);\n } else {\n const match = findTextSegmentsInOrder(finalText, streamingSegments);\n if (!match.matched) {\n return null;\n }\n unstreamedSuffix = finalText.slice(match.lastMatchEnd);\n }\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 * 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 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`);var e={sse_servers:[],stdio_servers:[],shttp_servers:[]};function t(t){if(!t||typeof t!=`object`)return{...e};let n=t;if(!(`mcpServers`in n)||!n.mcpServers||typeof n.mcpServers!=`object`)return{...e};let r=[],i=[],a=[],o=n.mcpServers;for(let[e,t]of Object.entries(o)){if(!t||typeof t!=`object`)continue;let n=t.url;if(n){let e=t.transport,i=t.auth,o=typeof i==`string`&&i!==`oauth`?i:void 0;if(e===`sse`){let e={url:n};o&&(e.api_key=o),r.push(e)}else{let e={url:n};o&&(e.api_key=o),t.timeout!=null&&(e.timeout=t.timeout),a.push(e)}}else{let n={name:e,command:t.command};t.args&&(n.args=t.args),t.env&&(n.env=t.env),i.push(n)}}return{sse_servers:r,stdio_servers:i,shttp_servers:a}}function n(e){let t={},n=e=>{if(!(e in t))return e;let n=1;for(;`${e}_${n}`in t;)n+=1;return`${e}_${n}`};for(let r of e.sse_servers){let e={};typeof r==`string`?e.url=r:(e.url=r.url,r.api_key&&(e.auth=r.api_key)),e.transport=`sse`,t[n(`sse`)]=e}for(let r of e.shttp_servers){let e={};typeof r==`string`?e.url=r:(e.url=r.url,r.api_key&&(e.auth=r.api_key),r.timeout!=null&&(e.timeout=r.timeout)),t[n(`shttp`)]=e}for(let r of e.stdio_servers){let e={command:r.command};r.args&&(e.args=r.args),r.env&&(e.env=r.env),t[n(r.name||`stdio`)]=e}return Object.keys(t).length>0?{mcpServers:t}:null}exports.parseMcpConfig=t,exports.toSdkMcpConfig=n;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);var e={sse_servers:[],stdio_servers:[],shttp_servers:[]},t=`https://mcp.linear.app/sse`,n=`https://mcp.linear.app/mcp`;function r(e,n){return n===`sse`?e.split(`?`)[0].replace(/\/+$/,``)===t:!1}function i(t){if(!t||typeof t!=`object`)return{...e};let i=t;if(!(`mcpServers`in i)||!i.mcpServers||typeof i.mcpServers!=`object`)return{...e};let a=[],o=[],s=[],c=[],l=i.mcpServers;for(let[e,t]of Object.entries(l)){if(!t||typeof t!=`object`)continue;let i=t.url;if(i){let e=t.transport,o=t.auth,l=typeof o==`string`&&o!==`oauth`?o:void 0;if(r(i,e)){let e={url:n};l&&(e.api_key=l),c.push(e)}else if(e===`sse`){let e={url:i};l&&(e.api_key=l),a.push(e)}else{let e={url:i};l&&(e.api_key=l),t.timeout!=null&&(e.timeout=t.timeout),s.push(e)}}else{let n={name:e,command:t.command};t.args&&(n.args=t.args),t.env&&(n.env=t.env),o.push(n)}}let u=e=>e.replace(/\/+$/,``);for(let e of c)s.some(t=>u(typeof t==`string`?t:t.url)===u(e.url))||s.push(e);return{sse_servers:a,stdio_servers:o,shttp_servers:s}}function a(e){let t={},n=e=>{if(!(e in t))return e;let n=1;for(;`${e}_${n}`in t;)n+=1;return`${e}_${n}`};for(let r of e.sse_servers){let e={};typeof r==`string`?e.url=r:(e.url=r.url,r.api_key&&(e.auth=r.api_key)),e.transport=`sse`,t[n(`sse`)]=e}for(let r of e.shttp_servers){let e={};typeof r==`string`?e.url=r:(e.url=r.url,r.api_key&&(e.auth=r.api_key),r.timeout!=null&&(e.timeout=r.timeout)),t[n(`shttp`)]=e}for(let r of e.stdio_servers){let e={command:r.command};r.args&&(e.args=r.args),r.env&&(e.env=r.env),t[n(r.name||`stdio`)]=e}return Object.keys(t).length>0?{mcpServers:t}:null}exports.parseMcpConfig=i,exports.toSdkMcpConfig=a;
2
2
  //# sourceMappingURL=mcp-config.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-config.cjs","names":[],"sources":["../../src/utils/mcp-config.ts"],"sourcesContent":["import {\n MCPConfig,\n MCPSSEServer,\n MCPSHTTPServer,\n MCPStdioServer,\n SettingsValue,\n} from \"#/types/settings\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\ntype SdkMcpServerConfig = Record<string, SettingsValue>;\ntype SdkMcpConfig = { mcpServers: Record<string, SdkMcpServerConfig> };\n\n/**\n * Parse an SDK mcp_config value ({ mcpServers: { ... } }) and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n if (!value || typeof value !== \"object\") {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const obj = value as Record<string, unknown>;\n\n if (\n !(\"mcpServers\" in obj) ||\n !obj.mcpServers ||\n typeof obj.mcpServers !== \"object\"\n ) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n const mcpServers = obj.mcpServers as Record<string, Record<string, unknown>>;\n\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n if (!serverConfig || typeof serverConfig !== \"object\") continue;\n\n const url = serverConfig.url as string | undefined;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = serverConfig.auth as string | undefined;\n const apiKey =\n typeof auth === \"string\" && auth !== \"oauth\" ? auth : undefined;\n\n if (transport === \"sse\") {\n const server: MCPSSEServer = { url };\n if (apiKey) server.api_key = apiKey;\n sseServers.push(server);\n } else {\n const server: MCPSHTTPServer = { url };\n if (apiKey) server.api_key = apiKey;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n shttpServers.push(server);\n }\n } else {\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command as string,\n };\n if (serverConfig.args) {\n stdioServer.args = serverConfig.args as string[];\n }\n if (serverConfig.env) {\n stdioServer.env = serverConfig.env as Record<string, string>;\n }\n stdioServers.push(stdioServer);\n }\n }\n\n return {\n sse_servers: sseServers,\n stdio_servers: stdioServers,\n shttp_servers: shttpServers,\n };\n}\n\n/**\n * Convert the frontend MCPConfig format back to the SDK { mcpServers: { ... } }\n * shape expected by agent_settings.mcp_config on the backend.\n *\n * Names are only suffixed (``_1``, ``_2``, …) when an earlier entry has\n * already claimed the bare base name. We intentionally do NOT use a single\n * monotonic counter across server types: that would, for example, rename a\n * stdio server \"myname\" to \"myname_1\" the moment any sse/shttp entry is\n * persisted ahead of it, and shift the suffix on every save as the count\n * of other server types changes. With per-base collision suffixing,\n * unrelated entries keep their human-meaningful names stable across edits.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpServers: Record<string, SdkMcpServerConfig> = {};\n\n const reserve = (base: string): string => {\n if (!(base in mcpServers)) return base;\n let i = 1;\n while (`${base}_${i}` in mcpServers) i += 1;\n return `${base}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n }\n server.transport = \"sse\";\n mcpServers[reserve(\"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpServers[reserve(\"shttp\")] = server;\n }\n\n for (const entry of config.stdio_servers) {\n const server: SdkMcpServerConfig = {\n command: entry.command,\n };\n if (entry.args) server.args = entry.args;\n if (entry.env) server.env = entry.env;\n mcpServers[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpServers).length > 0 ? { mcpServers } : null;\n}\n"],"mappings":"6CAQA,IAAM,EAA8B,CAClC,YAAa,EAAE,CACf,cAAe,EAAE,CACjB,cAAe,EAAE,CAClB,CASD,SAAgB,EAAe,EAA2B,CACxD,GAAI,CAAC,GAAS,OAAO,GAAU,SAC7B,MAAO,CAAE,GAAG,EAAkB,CAGhC,IAAM,EAAM,EAEZ,GACE,EAAE,eAAgB,IAClB,CAAC,EAAI,YACL,OAAO,EAAI,YAAe,SAE1B,MAAO,CAAE,GAAG,EAAkB,CAGhC,IAAM,EAAwC,EAAE,CAC1C,EAAiC,EAAE,CACnC,EAA4C,EAAE,CAE9C,EAAa,EAAI,WAEvB,IAAK,GAAM,CAAC,EAAY,KAAiB,OAAO,QAAQ,EAAW,CAAE,CACnE,GAAI,CAAC,GAAgB,OAAO,GAAiB,SAAU,SAEvD,IAAM,EAAM,EAAa,IAEzB,GAAI,EAAK,CACP,IAAM,EAAY,EAAa,UACzB,EAAO,EAAa,KACpB,EACJ,OAAO,GAAS,UAAY,IAAS,QAAU,EAAO,IAAA,GAExD,GAAI,IAAc,MAAO,CACvB,IAAM,EAAuB,CAAE,MAAK,CAChC,IAAQ,EAAO,QAAU,GAC7B,EAAW,KAAK,EAAO,KAClB,CACL,IAAM,EAAyB,CAAE,MAAK,CAClC,IAAQ,EAAO,QAAU,GACzB,EAAa,SAAW,OAC1B,EAAO,QAAU,EAAa,SAEhC,EAAa,KAAK,EAAO,MAEtB,CACL,IAAM,EAA8B,CAClC,KAAM,EACN,QAAS,EAAa,QACvB,CACG,EAAa,OACf,EAAY,KAAO,EAAa,MAE9B,EAAa,MACf,EAAY,IAAM,EAAa,KAEjC,EAAa,KAAK,EAAY,EAIlC,MAAO,CACL,YAAa,EACb,cAAe,EACf,cAAe,EAChB,CAeH,SAAgB,EAAe,EAAwC,CACrE,IAAM,EAAiD,EAAE,CAEnD,EAAW,GAAyB,CACxC,GAAI,EAAE,KAAQ,GAAa,OAAO,EAClC,IAAI,EAAI,EACR,KAAO,GAAG,EAAK,GAAG,MAAO,GAAY,GAAK,EAC1C,MAAO,GAAG,EAAK,GAAG,KAGpB,IAAK,IAAM,KAAS,EAAO,YAAa,CACtC,IAAM,EAA6B,EAAE,CACjC,OAAO,GAAU,SACnB,EAAO,IAAM,GAEb,EAAO,IAAM,EAAM,IACf,EAAM,UAAS,EAAO,KAAO,EAAM,UAEzC,EAAO,UAAY,MACnB,EAAW,EAAQ,MAAM,EAAI,EAG/B,IAAK,IAAM,KAAS,EAAO,cAAe,CACxC,IAAM,EAA6B,EAAE,CACjC,OAAO,GAAU,SACnB,EAAO,IAAM,GAEb,EAAO,IAAM,EAAM,IACf,EAAM,UAAS,EAAO,KAAO,EAAM,SACnC,EAAM,SAAW,OAAM,EAAO,QAAU,EAAM,UAEpD,EAAW,EAAQ,QAAQ,EAAI,EAGjC,IAAK,IAAM,KAAS,EAAO,cAAe,CACxC,IAAM,EAA6B,CACjC,QAAS,EAAM,QAChB,CACG,EAAM,OAAM,EAAO,KAAO,EAAM,MAChC,EAAM,MAAK,EAAO,IAAM,EAAM,KAClC,EAAW,EAAQ,EAAM,MAAQ,QAAQ,EAAI,EAG/C,OAAO,OAAO,KAAK,EAAW,CAAC,OAAS,EAAI,CAAE,aAAY,CAAG"}
1
+ {"version":3,"file":"mcp-config.cjs","names":[],"sources":["../../src/utils/mcp-config.ts"],"sourcesContent":["import {\n MCPConfig,\n MCPSSEServer,\n MCPSHTTPServer,\n MCPStdioServer,\n SettingsValue,\n} from \"#/types/settings\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nconst LINEAR_DEPRECATED_SSE_URL = \"https://mcp.linear.app/sse\";\nconst LINEAR_SHTTP_URL = \"https://mcp.linear.app/mcp\";\n\n/**\n * Linear removed its MCP SSE transport (the /sse endpoint rejects every\n * call since 2026-04-08). Detect persisted configs that still point at\n * the dead endpoint so they can be migrated to streamable HTTP at the\n * /mcp replacement. Matches only the exact deprecated URL (tolerating a\n * trailing slash or query string) — nothing else is rewritten.\n */\nfunction isDeprecatedLinearSse(\n url: string,\n transport: string | undefined,\n): boolean {\n if (transport !== \"sse\") return false;\n const normalized = url.split(\"?\")[0].replace(/\\/+$/, \"\");\n return normalized === LINEAR_DEPRECATED_SSE_URL;\n}\n\ntype SdkMcpServerConfig = Record<string, SettingsValue>;\ntype SdkMcpConfig = { mcpServers: Record<string, SdkMcpServerConfig> };\n\n/**\n * Parse an SDK mcp_config value ({ mcpServers: { ... } }) and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n if (!value || typeof value !== \"object\") {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const obj = value as Record<string, unknown>;\n\n if (\n !(\"mcpServers\" in obj) ||\n !obj.mcpServers ||\n typeof obj.mcpServers !== \"object\"\n ) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n // Legacy Linear SSE entries rewritten to the /mcp endpoint. Collected\n // separately and merged after the loop so an existing hand-added /mcp\n // entry (with its own api_key/timeout) wins over the migrated one.\n const migratedShttpServers: MCPSHTTPServer[] = [];\n\n const mcpServers = obj.mcpServers as Record<string, Record<string, unknown>>;\n\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n if (!serverConfig || typeof serverConfig !== \"object\") continue;\n\n const url = serverConfig.url as string | undefined;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = serverConfig.auth as string | undefined;\n const apiKey =\n typeof auth === \"string\" && auth !== \"oauth\" ? auth : undefined;\n\n if (isDeprecatedLinearSse(url, transport)) {\n const server: MCPSHTTPServer = { url: LINEAR_SHTTP_URL };\n if (apiKey) server.api_key = apiKey;\n migratedShttpServers.push(server);\n } else if (transport === \"sse\") {\n const server: MCPSSEServer = { url };\n if (apiKey) server.api_key = apiKey;\n sseServers.push(server);\n } else {\n const server: MCPSHTTPServer = { url };\n if (apiKey) server.api_key = apiKey;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n shttpServers.push(server);\n }\n } else {\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command as string,\n };\n if (serverConfig.args) {\n stdioServer.args = serverConfig.args as string[];\n }\n if (serverConfig.env) {\n stdioServer.env = serverConfig.env as Record<string, string>;\n }\n stdioServers.push(stdioServer);\n }\n }\n\n const normalizeUrl = (u: string) => u.replace(/\\/+$/, \"\");\n for (const migrated of migratedShttpServers) {\n const alreadyPresent = shttpServers.some((entry) => {\n const entryUrl = typeof entry === \"string\" ? entry : entry.url;\n return normalizeUrl(entryUrl) === normalizeUrl(migrated.url);\n });\n if (!alreadyPresent) shttpServers.push(migrated);\n }\n\n return {\n sse_servers: sseServers,\n stdio_servers: stdioServers,\n shttp_servers: shttpServers,\n };\n}\n\n/**\n * Convert the frontend MCPConfig format back to the SDK { mcpServers: { ... } }\n * shape expected by agent_settings.mcp_config on the backend.\n *\n * Names are only suffixed (``_1``, ``_2``, …) when an earlier entry has\n * already claimed the bare base name. We intentionally do NOT use a single\n * monotonic counter across server types: that would, for example, rename a\n * stdio server \"myname\" to \"myname_1\" the moment any sse/shttp entry is\n * persisted ahead of it, and shift the suffix on every save as the count\n * of other server types changes. With per-base collision suffixing,\n * unrelated entries keep their human-meaningful names stable across edits.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpServers: Record<string, SdkMcpServerConfig> = {};\n\n const reserve = (base: string): string => {\n if (!(base in mcpServers)) return base;\n let i = 1;\n while (`${base}_${i}` in mcpServers) i += 1;\n return `${base}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n }\n server.transport = \"sse\";\n mcpServers[reserve(\"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpServers[reserve(\"shttp\")] = server;\n }\n\n for (const entry of config.stdio_servers) {\n const server: SdkMcpServerConfig = {\n command: entry.command,\n };\n if (entry.args) server.args = entry.args;\n if (entry.env) server.env = entry.env;\n mcpServers[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpServers).length > 0 ? { mcpServers } : null;\n}\n"],"mappings":"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"}
@@ -3,39 +3,47 @@ var e = {
3
3
  sse_servers: [],
4
4
  stdio_servers: [],
5
5
  shttp_servers: []
6
- };
7
- function t(t) {
6
+ }, t = "https://mcp.linear.app/sse", n = "https://mcp.linear.app/mcp";
7
+ function r(e, n) {
8
+ return n === "sse" ? e.split("?")[0].replace(/\/+$/, "") === t : !1;
9
+ }
10
+ function i(t) {
8
11
  if (!t || typeof t != "object") return { ...e };
9
- let n = t;
10
- if (!("mcpServers" in n) || !n.mcpServers || typeof n.mcpServers != "object") return { ...e };
11
- let r = [], i = [], a = [], o = n.mcpServers;
12
- for (let [e, t] of Object.entries(o)) {
12
+ let i = t;
13
+ if (!("mcpServers" in i) || !i.mcpServers || typeof i.mcpServers != "object") return { ...e };
14
+ let a = [], o = [], s = [], c = [], l = i.mcpServers;
15
+ for (let [e, t] of Object.entries(l)) {
13
16
  if (!t || typeof t != "object") continue;
14
- let n = t.url;
15
- if (n) {
16
- let e = t.transport, i = t.auth, o = typeof i == "string" && i !== "oauth" ? i : void 0;
17
- if (e === "sse") {
17
+ let i = t.url;
18
+ if (i) {
19
+ let e = t.transport, o = t.auth, l = typeof o == "string" && o !== "oauth" ? o : void 0;
20
+ if (r(i, e)) {
18
21
  let e = { url: n };
19
- o && (e.api_key = o), r.push(e);
22
+ l && (e.api_key = l), c.push(e);
23
+ } else if (e === "sse") {
24
+ let e = { url: i };
25
+ l && (e.api_key = l), a.push(e);
20
26
  } else {
21
- let e = { url: n };
22
- o && (e.api_key = o), t.timeout != null && (e.timeout = t.timeout), a.push(e);
27
+ let e = { url: i };
28
+ l && (e.api_key = l), t.timeout != null && (e.timeout = t.timeout), s.push(e);
23
29
  }
24
30
  } else {
25
31
  let n = {
26
32
  name: e,
27
33
  command: t.command
28
34
  };
29
- t.args && (n.args = t.args), t.env && (n.env = t.env), i.push(n);
35
+ t.args && (n.args = t.args), t.env && (n.env = t.env), o.push(n);
30
36
  }
31
37
  }
38
+ let u = (e) => e.replace(/\/+$/, "");
39
+ for (let e of c) s.some((t) => u(typeof t == "string" ? t : t.url) === u(e.url)) || s.push(e);
32
40
  return {
33
- sse_servers: r,
34
- stdio_servers: i,
35
- shttp_servers: a
41
+ sse_servers: a,
42
+ stdio_servers: o,
43
+ shttp_servers: s
36
44
  };
37
45
  }
38
- function n(e) {
46
+ function a(e) {
39
47
  let t = {}, n = (e) => {
40
48
  if (!(e in t)) return e;
41
49
  let n = 1;
@@ -57,6 +65,6 @@ function n(e) {
57
65
  return Object.keys(t).length > 0 ? { mcpServers: t } : null;
58
66
  }
59
67
  //#endregion
60
- export { t as parseMcpConfig, n as toSdkMcpConfig };
68
+ export { i as parseMcpConfig, a as toSdkMcpConfig };
61
69
 
62
70
  //# sourceMappingURL=mcp-config.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-config.js","names":[],"sources":["../../src/utils/mcp-config.ts"],"sourcesContent":["import {\n MCPConfig,\n MCPSSEServer,\n MCPSHTTPServer,\n MCPStdioServer,\n SettingsValue,\n} from \"#/types/settings\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\ntype SdkMcpServerConfig = Record<string, SettingsValue>;\ntype SdkMcpConfig = { mcpServers: Record<string, SdkMcpServerConfig> };\n\n/**\n * Parse an SDK mcp_config value ({ mcpServers: { ... } }) and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n if (!value || typeof value !== \"object\") {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const obj = value as Record<string, unknown>;\n\n if (\n !(\"mcpServers\" in obj) ||\n !obj.mcpServers ||\n typeof obj.mcpServers !== \"object\"\n ) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n const mcpServers = obj.mcpServers as Record<string, Record<string, unknown>>;\n\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n if (!serverConfig || typeof serverConfig !== \"object\") continue;\n\n const url = serverConfig.url as string | undefined;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = serverConfig.auth as string | undefined;\n const apiKey =\n typeof auth === \"string\" && auth !== \"oauth\" ? auth : undefined;\n\n if (transport === \"sse\") {\n const server: MCPSSEServer = { url };\n if (apiKey) server.api_key = apiKey;\n sseServers.push(server);\n } else {\n const server: MCPSHTTPServer = { url };\n if (apiKey) server.api_key = apiKey;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n shttpServers.push(server);\n }\n } else {\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command as string,\n };\n if (serverConfig.args) {\n stdioServer.args = serverConfig.args as string[];\n }\n if (serverConfig.env) {\n stdioServer.env = serverConfig.env as Record<string, string>;\n }\n stdioServers.push(stdioServer);\n }\n }\n\n return {\n sse_servers: sseServers,\n stdio_servers: stdioServers,\n shttp_servers: shttpServers,\n };\n}\n\n/**\n * Convert the frontend MCPConfig format back to the SDK { mcpServers: { ... } }\n * shape expected by agent_settings.mcp_config on the backend.\n *\n * Names are only suffixed (``_1``, ``_2``, …) when an earlier entry has\n * already claimed the bare base name. We intentionally do NOT use a single\n * monotonic counter across server types: that would, for example, rename a\n * stdio server \"myname\" to \"myname_1\" the moment any sse/shttp entry is\n * persisted ahead of it, and shift the suffix on every save as the count\n * of other server types changes. With per-base collision suffixing,\n * unrelated entries keep their human-meaningful names stable across edits.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpServers: Record<string, SdkMcpServerConfig> = {};\n\n const reserve = (base: string): string => {\n if (!(base in mcpServers)) return base;\n let i = 1;\n while (`${base}_${i}` in mcpServers) i += 1;\n return `${base}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n }\n server.transport = \"sse\";\n mcpServers[reserve(\"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpServers[reserve(\"shttp\")] = server;\n }\n\n for (const entry of config.stdio_servers) {\n const server: SdkMcpServerConfig = {\n command: entry.command,\n };\n if (entry.args) server.args = entry.args;\n if (entry.env) server.env = entry.env;\n mcpServers[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpServers).length > 0 ? { mcpServers } : null;\n}\n"],"mappings":";AAQA,IAAM,IAA8B;CAClC,aAAa,EAAE;CACf,eAAe,EAAE;CACjB,eAAe,EAAE;CAClB;AASD,SAAgB,EAAe,GAA2B;AACxD,KAAI,CAAC,KAAS,OAAO,KAAU,SAC7B,QAAO,EAAE,GAAG,GAAkB;CAGhC,IAAM,IAAM;AAEZ,KACE,EAAE,gBAAgB,MAClB,CAAC,EAAI,cACL,OAAO,EAAI,cAAe,SAE1B,QAAO,EAAE,GAAG,GAAkB;CAGhC,IAAM,IAAwC,EAAE,EAC1C,IAAiC,EAAE,EACnC,IAA4C,EAAE,EAE9C,IAAa,EAAI;AAEvB,MAAK,IAAM,CAAC,GAAY,MAAiB,OAAO,QAAQ,EAAW,EAAE;AACnE,MAAI,CAAC,KAAgB,OAAO,KAAiB,SAAU;EAEvD,IAAM,IAAM,EAAa;AAEzB,MAAI,GAAK;GACP,IAAM,IAAY,EAAa,WACzB,IAAO,EAAa,MACpB,IACJ,OAAO,KAAS,YAAY,MAAS,UAAU,IAAO,KAAA;AAExD,OAAI,MAAc,OAAO;IACvB,IAAM,IAAuB,EAAE,QAAK;AAEpC,IADI,MAAQ,EAAO,UAAU,IAC7B,EAAW,KAAK,EAAO;UAClB;IACL,IAAM,IAAyB,EAAE,QAAK;AAKtC,IAJI,MAAQ,EAAO,UAAU,IACzB,EAAa,WAAW,SAC1B,EAAO,UAAU,EAAa,UAEhC,EAAa,KAAK,EAAO;;SAEtB;GACL,IAAM,IAA8B;IAClC,MAAM;IACN,SAAS,EAAa;IACvB;AAOD,GANI,EAAa,SACf,EAAY,OAAO,EAAa,OAE9B,EAAa,QACf,EAAY,MAAM,EAAa,MAEjC,EAAa,KAAK,EAAY;;;AAIlC,QAAO;EACL,aAAa;EACb,eAAe;EACf,eAAe;EAChB;;AAeH,SAAgB,EAAe,GAAwC;CACrE,IAAM,IAAiD,EAAE,EAEnD,KAAW,MAAyB;AACxC,MAAI,EAAE,KAAQ,GAAa,QAAO;EAClC,IAAI,IAAI;AACR,SAAO,GAAG,EAAK,GAAG,OAAO,GAAY,MAAK;AAC1C,SAAO,GAAG,EAAK,GAAG;;AAGpB,MAAK,IAAM,KAAS,EAAO,aAAa;EACtC,IAAM,IAA6B,EAAE;AAQrC,EAPI,OAAO,KAAU,WACnB,EAAO,MAAM,KAEb,EAAO,MAAM,EAAM,KACf,EAAM,YAAS,EAAO,OAAO,EAAM,WAEzC,EAAO,YAAY,OACnB,EAAW,EAAQ,MAAM,IAAI;;AAG/B,MAAK,IAAM,KAAS,EAAO,eAAe;EACxC,IAAM,IAA6B,EAAE;AAQrC,EAPI,OAAO,KAAU,WACnB,EAAO,MAAM,KAEb,EAAO,MAAM,EAAM,KACf,EAAM,YAAS,EAAO,OAAO,EAAM,UACnC,EAAM,WAAW,SAAM,EAAO,UAAU,EAAM,WAEpD,EAAW,EAAQ,QAAQ,IAAI;;AAGjC,MAAK,IAAM,KAAS,EAAO,eAAe;EACxC,IAAM,IAA6B,EACjC,SAAS,EAAM,SAChB;AAGD,EAFI,EAAM,SAAM,EAAO,OAAO,EAAM,OAChC,EAAM,QAAK,EAAO,MAAM,EAAM,MAClC,EAAW,EAAQ,EAAM,QAAQ,QAAQ,IAAI;;AAG/C,QAAO,OAAO,KAAK,EAAW,CAAC,SAAS,IAAI,EAAE,eAAY,GAAG"}
1
+ {"version":3,"file":"mcp-config.js","names":[],"sources":["../../src/utils/mcp-config.ts"],"sourcesContent":["import {\n MCPConfig,\n MCPSSEServer,\n MCPSHTTPServer,\n MCPStdioServer,\n SettingsValue,\n} from \"#/types/settings\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nconst LINEAR_DEPRECATED_SSE_URL = \"https://mcp.linear.app/sse\";\nconst LINEAR_SHTTP_URL = \"https://mcp.linear.app/mcp\";\n\n/**\n * Linear removed its MCP SSE transport (the /sse endpoint rejects every\n * call since 2026-04-08). Detect persisted configs that still point at\n * the dead endpoint so they can be migrated to streamable HTTP at the\n * /mcp replacement. Matches only the exact deprecated URL (tolerating a\n * trailing slash or query string) — nothing else is rewritten.\n */\nfunction isDeprecatedLinearSse(\n url: string,\n transport: string | undefined,\n): boolean {\n if (transport !== \"sse\") return false;\n const normalized = url.split(\"?\")[0].replace(/\\/+$/, \"\");\n return normalized === LINEAR_DEPRECATED_SSE_URL;\n}\n\ntype SdkMcpServerConfig = Record<string, SettingsValue>;\ntype SdkMcpConfig = { mcpServers: Record<string, SdkMcpServerConfig> };\n\n/**\n * Parse an SDK mcp_config value ({ mcpServers: { ... } }) and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n if (!value || typeof value !== \"object\") {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const obj = value as Record<string, unknown>;\n\n if (\n !(\"mcpServers\" in obj) ||\n !obj.mcpServers ||\n typeof obj.mcpServers !== \"object\"\n ) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n // Legacy Linear SSE entries rewritten to the /mcp endpoint. Collected\n // separately and merged after the loop so an existing hand-added /mcp\n // entry (with its own api_key/timeout) wins over the migrated one.\n const migratedShttpServers: MCPSHTTPServer[] = [];\n\n const mcpServers = obj.mcpServers as Record<string, Record<string, unknown>>;\n\n for (const [serverName, serverConfig] of Object.entries(mcpServers)) {\n if (!serverConfig || typeof serverConfig !== \"object\") continue;\n\n const url = serverConfig.url as string | undefined;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = serverConfig.auth as string | undefined;\n const apiKey =\n typeof auth === \"string\" && auth !== \"oauth\" ? auth : undefined;\n\n if (isDeprecatedLinearSse(url, transport)) {\n const server: MCPSHTTPServer = { url: LINEAR_SHTTP_URL };\n if (apiKey) server.api_key = apiKey;\n migratedShttpServers.push(server);\n } else if (transport === \"sse\") {\n const server: MCPSSEServer = { url };\n if (apiKey) server.api_key = apiKey;\n sseServers.push(server);\n } else {\n const server: MCPSHTTPServer = { url };\n if (apiKey) server.api_key = apiKey;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n shttpServers.push(server);\n }\n } else {\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command as string,\n };\n if (serverConfig.args) {\n stdioServer.args = serverConfig.args as string[];\n }\n if (serverConfig.env) {\n stdioServer.env = serverConfig.env as Record<string, string>;\n }\n stdioServers.push(stdioServer);\n }\n }\n\n const normalizeUrl = (u: string) => u.replace(/\\/+$/, \"\");\n for (const migrated of migratedShttpServers) {\n const alreadyPresent = shttpServers.some((entry) => {\n const entryUrl = typeof entry === \"string\" ? entry : entry.url;\n return normalizeUrl(entryUrl) === normalizeUrl(migrated.url);\n });\n if (!alreadyPresent) shttpServers.push(migrated);\n }\n\n return {\n sse_servers: sseServers,\n stdio_servers: stdioServers,\n shttp_servers: shttpServers,\n };\n}\n\n/**\n * Convert the frontend MCPConfig format back to the SDK { mcpServers: { ... } }\n * shape expected by agent_settings.mcp_config on the backend.\n *\n * Names are only suffixed (``_1``, ``_2``, …) when an earlier entry has\n * already claimed the bare base name. We intentionally do NOT use a single\n * monotonic counter across server types: that would, for example, rename a\n * stdio server \"myname\" to \"myname_1\" the moment any sse/shttp entry is\n * persisted ahead of it, and shift the suffix on every save as the count\n * of other server types changes. With per-base collision suffixing,\n * unrelated entries keep their human-meaningful names stable across edits.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpServers: Record<string, SdkMcpServerConfig> = {};\n\n const reserve = (base: string): string => {\n if (!(base in mcpServers)) return base;\n let i = 1;\n while (`${base}_${i}` in mcpServers) i += 1;\n return `${base}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n }\n server.transport = \"sse\";\n mcpServers[reserve(\"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = {};\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n server.url = entry.url;\n if (entry.api_key) server.auth = entry.api_key;\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpServers[reserve(\"shttp\")] = server;\n }\n\n for (const entry of config.stdio_servers) {\n const server: SdkMcpServerConfig = {\n command: entry.command,\n };\n if (entry.args) server.args = entry.args;\n if (entry.env) server.env = entry.env;\n mcpServers[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpServers).length > 0 ? { mcpServers } : null;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require(`../_virtual/_rolldown/runtime.cjs`);function e(e){return e.connectionOptions.filter(e=>e.provider===`mcp`&&!!e.transport)}function t(t){let n=e(t);return n.find(e=>e.id===t.defaultConnectionOptionId)??n[0]}function n(e){return e.auth.strategy!==`oauth2`}function r(t){let r=e(t),i=r.find(e=>e.id===t.defaultConnectionOptionId);return i&&n(i)?i:r.find(n)}function i(e){return t(e)?.transport}function a(e){return e.filter(e=>!!t(e))}var o=e=>{try{return new URL(e)}catch{return null}};function s(e,t){let n=typeof e==`string`?e:``,r=typeof t==`string`?t:``;if(!n||!r)return!1;let i=o(n),a=o(r);return!i||!a?n.replace(/\/+$/,``)===r.replace(/\/+$/,``):i.protocol===a.protocol&&i.host===a.host&&i.pathname.replace(/\/+$/,``)===a.pathname.replace(/\/+$/,``)}function c(e,t){if(e.kind===`shttp`){let n=e.url;return t.type===`shttp`&&!!t.url&&s(t.url,n)}if(e.kind===`sse`){let n=e.url;return t.type===`sse`&&!!t.url&&s(t.url,n)}return t.type===`stdio`&&t.name===e.serverName}function l(e,t){return!e.runtimeAvailability||e.runtimeAvailability===`all`?!0:e.runtimeAvailability===t}function u(e){return e.trim().toLowerCase()}function d(e){return e.map((e,t)=>({entry:e,index:t})).sort((e,t)=>(t.entry.popularityRank??0)-(e.entry.popularityRank??0)||e.index-t.index).map(({entry:e})=>e)}function f(e,t){let n=u(t);return n?[e.name,e.description,e.id,...e.keywords??[]].join(` `).toLowerCase().includes(n):!0}function p(e,t,n){let r=u(n);return r?[e.type,`name`in e?e.name:void 0,`command`in e?e.command:void 0,`args`in e?e.args?.join(` `):void 0,`url`in e?e.url:void 0,t?.name,t?.description,t?.id,...t?.keywords??[]].filter(Boolean).join(` `).toLowerCase().includes(r):!0}function m(t,n){return n.find(n=>e(n).some(e=>c(e.transport,t)))}exports.findCatalogEntryForServer=m,exports.getDefaultMcpTransport=i,exports.getInstallableMcpConnectionOption=r,exports.getMarketplaceEntriesByPopularity=d,exports.getMcpMarketplaceCatalog=a,exports.installedServerMatchesQuery=p,exports.isMarketplaceEntryAvailable=l,exports.marketplaceEntryMatchesQuery=f;
1
+ require(`../_virtual/_rolldown/runtime.cjs`);function e(e){return e.connectionOptions.filter(e=>e.provider===`mcp`&&!!e.transport)}function t(t){let n=e(t);return n.find(e=>e.id===t.defaultConnectionOptionId)??n[0]}function n(e){return e.auth.strategy!==`oauth2`}function r(t){let r=e(t),i=r.find(e=>e.id===t.defaultConnectionOptionId);return i&&n(i)?i:r.find(n)}function i(e){return t(e)?.transport}var a=`https://mcp.linear.app/sse`,o=`https://mcp.linear.app/mcp`,s=`https://linear.app/docs/mcp`;function c(e){return e.id===`linear`?{...e,docsUrl:s,installHint:`Authenticate with a Linear API key (Linear → Settings → Security & access) — sent as a Bearer token. Optional when the endpoint accepts your OAuth session.`,connectionOptions:e.connectionOptions.map(e=>e.transport?.kind===`sse`&&d(e.transport.url,a)?{...e,auth:{...e.auth,strategy:`bearer`},transport:{kind:`shttp`,url:o,apiKeyOptional:e.transport.apiKeyOptional}}:e)}:e}function l(e){return e.map(c).filter(e=>!!t(e))}var u=e=>{try{return new URL(e)}catch{return null}};function d(e,t){let n=typeof e==`string`?e:``,r=typeof t==`string`?t:``;if(!n||!r)return!1;let i=u(n),a=u(r);return!i||!a?n.replace(/\/+$/,``)===r.replace(/\/+$/,``):i.protocol===a.protocol&&i.host===a.host&&i.pathname.replace(/\/+$/,``)===a.pathname.replace(/\/+$/,``)}function f(e,t){if(e.kind===`shttp`){let n=e.url;return t.type===`shttp`&&!!t.url&&d(t.url,n)}if(e.kind===`sse`){let n=e.url;return t.type===`sse`&&!!t.url&&d(t.url,n)}return t.type===`stdio`&&t.name===e.serverName}function p(e,t){return!e.runtimeAvailability||e.runtimeAvailability===`all`?!0:e.runtimeAvailability===t}function m(e){return e.trim().toLowerCase()}function h(e){return e.map((e,t)=>({entry:e,index:t})).sort((e,t)=>(t.entry.popularityRank??0)-(e.entry.popularityRank??0)||e.index-t.index).map(({entry:e})=>e)}function g(e,t){let n=m(t);return n?[e.name,e.description,e.id,...e.keywords??[]].join(` `).toLowerCase().includes(n):!0}function _(e,t,n){let r=m(n);return r?[e.type,`name`in e?e.name:void 0,`command`in e?e.command:void 0,`args`in e?e.args?.join(` `):void 0,`url`in e?e.url:void 0,t?.name,t?.description,t?.id,...t?.keywords??[]].filter(Boolean).join(` `).toLowerCase().includes(r):!0}function v(t,n){return n.find(n=>e(n).some(e=>f(e.transport,t)))}exports.findCatalogEntryForServer=v,exports.getDefaultMcpTransport=i,exports.getInstallableMcpConnectionOption=r,exports.getMarketplaceEntriesByPopularity=h,exports.getMcpMarketplaceCatalog=l,exports.installedServerMatchesQuery=_,exports.isMarketplaceEntryAvailable=p,exports.marketplaceEntryMatchesQuery=g;
2
2
  //# sourceMappingURL=mcp-marketplace-utils.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-marketplace-utils.cjs","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationConnectionOption,\n IntegrationTransport,\n} from \"@openhands/extensions/integrations\";\n\nexport type { MarketplaceEntry };\n\nexport type McpMarketplaceConnectionOption = IntegrationConnectionOption & {\n provider: \"mcp\";\n transport: IntegrationTransport;\n};\n\nexport function getMcpConnectionOptions(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption[] {\n return entry.connectionOptions.filter(\n (option): option is McpMarketplaceConnectionOption =>\n option.provider === \"mcp\" && !!option.transport,\n );\n}\n\nexport function getDefaultMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n return (\n options.find((option) => option.id === entry.defaultConnectionOptionId) ??\n options[0]\n );\n}\n\nfunction isLocallyInstallableMcpOption(\n option: McpMarketplaceConnectionOption,\n): boolean {\n // The local install modal writes static MCP server config. OAuth options\n // describe hosted redirect flows, so prefer an API/stdio fallback when one\n // exists and leave OAuth as the default connection for hosted integrations.\n return option.auth.strategy !== \"oauth2\";\n}\n\nexport function getInstallableMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n const defaultOption = options.find(\n (option) => option.id === entry.defaultConnectionOptionId,\n );\n if (defaultOption && isLocallyInstallableMcpOption(defaultOption)) {\n return defaultOption;\n }\n return options.find(isLocallyInstallableMcpOption);\n}\n\nexport function getDefaultMcpTransport(\n entry: MarketplaceEntry,\n): IntegrationTransport | undefined {\n return getDefaultMcpConnectionOption(entry)?.transport;\n}\n\nexport function getMcpMarketplaceCatalog(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog.filter((entry) => !!getDefaultMcpConnectionOption(entry));\n}\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n transport: IntegrationTransport,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n return (\n servers.find((server) => transportMatchesServer(transport, server)) ?? null\n );\n}\n\nexport function findInstalledEntryMatch(\n entry: MarketplaceEntry,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n for (const option of getMcpConnectionOptions(entry)) {\n const match = findInstalledMatch(option.transport, servers);\n if (match) return match;\n }\n return null;\n}\n\nfunction transportMatchesServer(\n transport: IntegrationTransport,\n server: MCPServerConfig,\n): boolean {\n if (transport.kind === \"shttp\") {\n const tplUrl = transport.url;\n return (\n server.type === \"shttp\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n if (transport.kind === \"sse\") {\n const tplUrl = transport.url;\n return (\n server.type === \"sse\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n // stdio: match on the registered server name.\n return server.type === \"stdio\" && server.name === transport.serverName;\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check every MCP option rather than only the default. Some unified\n // integration entries default to OAuth-hosted MCP while still exposing\n // an API/stdio option; existing installed servers should match either.\n return getMcpConnectionOptions(entry).some((option) =>\n transportMatchesServer(option.transport, server),\n );\n });\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"mcp-marketplace-utils.cjs","names":[],"sources":["../../src/utils/mcp-marketplace-utils.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport type {\n IntegrationCatalogEntry as MarketplaceEntry,\n IntegrationConnectionOption,\n IntegrationTransport,\n} from \"@openhands/extensions/integrations\";\n\nexport type { MarketplaceEntry };\n\nexport type McpMarketplaceConnectionOption = IntegrationConnectionOption & {\n provider: \"mcp\";\n transport: IntegrationTransport;\n};\n\nexport function getMcpConnectionOptions(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption[] {\n return entry.connectionOptions.filter(\n (option): option is McpMarketplaceConnectionOption =>\n option.provider === \"mcp\" && !!option.transport,\n );\n}\n\nexport function getDefaultMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n return (\n options.find((option) => option.id === entry.defaultConnectionOptionId) ??\n options[0]\n );\n}\n\nfunction isLocallyInstallableMcpOption(\n option: McpMarketplaceConnectionOption,\n): boolean {\n // The local install modal writes static MCP server config. OAuth options\n // describe hosted redirect flows, so prefer an API/stdio fallback when one\n // exists and leave OAuth as the default connection for hosted integrations.\n return option.auth.strategy !== \"oauth2\";\n}\n\nexport function getInstallableMcpConnectionOption(\n entry: MarketplaceEntry,\n): McpMarketplaceConnectionOption | undefined {\n const options = getMcpConnectionOptions(entry);\n const defaultOption = options.find(\n (option) => option.id === entry.defaultConnectionOptionId,\n );\n if (defaultOption && isLocallyInstallableMcpOption(defaultOption)) {\n return defaultOption;\n }\n return options.find(isLocallyInstallableMcpOption);\n}\n\nexport function getDefaultMcpTransport(\n entry: MarketplaceEntry,\n): IntegrationTransport | undefined {\n return getDefaultMcpConnectionOption(entry)?.transport;\n}\n\nconst LINEAR_DEPRECATED_SSE_URL = \"https://mcp.linear.app/sse\";\nconst LINEAR_SHTTP_URL = \"https://mcp.linear.app/mcp\";\nconst LINEAR_DOCS_URL = \"https://linear.app/docs/mcp\";\n\n/**\n * Upstream @openhands/extensions still ships Linear's deprecated SSE\n * transport (removed upstream on 2026-04-08; the /sse endpoint now\n * rejects every call). Rewrite the entry to streamable HTTP at the\n * /mcp replacement endpoint until the pinned dependency catches up.\n *\n * The /mcp endpoint authenticates via OAuth 2.1 or a Linear API key\n * sent as \"Authorization: Bearer <token>\". This client has no\n * interactive OAuth flow for MCP installs, so switch the auth\n * strategy from \"none\" to \"bearer\" — the install modal then offers\n * an (optional) API key field and the agent server forwards it as a\n * Bearer header.\n *\n * Patches immutably — the imported catalog JSON is shared module\n * state and must not be mutated.\n */\nfunction patchLinearEntry(entry: MarketplaceEntry): MarketplaceEntry {\n if (entry.id !== \"linear\") return entry;\n return {\n ...entry,\n docsUrl: LINEAR_DOCS_URL,\n installHint:\n \"Authenticate with a Linear API key (Linear → Settings → Security & access) — sent as a Bearer token. Optional when the endpoint accepts your OAuth session.\",\n connectionOptions: entry.connectionOptions.map((option) =>\n option.transport?.kind === \"sse\" &&\n urlsMatch(option.transport.url, LINEAR_DEPRECATED_SSE_URL)\n ? {\n ...option,\n auth: { ...option.auth, strategy: \"bearer\" as const },\n transport: {\n kind: \"shttp\" as const,\n url: LINEAR_SHTTP_URL,\n apiKeyOptional: option.transport.apiKeyOptional,\n },\n }\n : option,\n ),\n };\n}\n\nexport function getMcpMarketplaceCatalog(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map(patchLinearEntry)\n .filter((entry) => !!getDefaultMcpConnectionOption(entry));\n}\n\nconst tryUrl = (raw: string): URL | null => {\n try {\n return new URL(raw);\n } catch {\n return null;\n }\n};\n\n/**\n * Loose URL match that ignores query strings, trailing slashes, and\n * default ports. We want clicking \"Linear\" to flag the entry as\n * installed even if the user pasted the URL with extra trailing slash\n * or a different port-equivalent variant.\n *\n * Defensive against runtime data that doesn't match the static type:\n * if either input is not a string (e.g. parsed from an older settings\n * blob), we fall through the URL parsing path and the safe trim\n * fallback below, never calling `.replace` on undefined.\n */\nexport function urlsMatch(a: unknown, b: unknown): boolean {\n const aStr = typeof a === \"string\" ? a : \"\";\n const bStr = typeof b === \"string\" ? b : \"\";\n if (!aStr || !bStr) return false;\n const left = tryUrl(aStr);\n const right = tryUrl(bStr);\n if (!left || !right) {\n return aStr.replace(/\\/+$/, \"\") === bStr.replace(/\\/+$/, \"\");\n }\n return (\n left.protocol === right.protocol &&\n left.host === right.host &&\n left.pathname.replace(/\\/+$/, \"\") === right.pathname.replace(/\\/+$/, \"\")\n );\n}\n\n/**\n * Decide whether a marketplace template is already represented by one\n * of the installed MCP servers. Used to render an \"Installed\" badge on\n * the marketplace tile. Returns the first matching server, or null.\n */\nexport function findInstalledMatch(\n transport: IntegrationTransport,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n return (\n servers.find((server) => transportMatchesServer(transport, server)) ?? null\n );\n}\n\nexport function findInstalledEntryMatch(\n entry: MarketplaceEntry,\n servers: MCPServerConfig[],\n): MCPServerConfig | null {\n for (const option of getMcpConnectionOptions(entry)) {\n const match = findInstalledMatch(option.transport, servers);\n if (match) return match;\n }\n return null;\n}\n\nfunction transportMatchesServer(\n transport: IntegrationTransport,\n server: MCPServerConfig,\n): boolean {\n if (transport.kind === \"shttp\") {\n const tplUrl = transport.url;\n return (\n server.type === \"shttp\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n if (transport.kind === \"sse\") {\n const tplUrl = transport.url;\n return (\n server.type === \"sse\" && !!server.url && urlsMatch(server.url, tplUrl)\n );\n }\n\n // stdio: match on the registered server name.\n return server.type === \"stdio\" && server.name === transport.serverName;\n}\n\nexport function isMarketplaceEntryAvailable(\n entry: MarketplaceEntry,\n backendKind: \"local\" | \"cloud\",\n): boolean {\n if (!entry.runtimeAvailability || entry.runtimeAvailability === \"all\")\n return true;\n return entry.runtimeAvailability === backendKind;\n}\n\nfunction normalize(query: string): string {\n return query.trim().toLowerCase();\n}\n\n/**\n * Case-insensitive substring match against the catalog entry's\n * user-visible identity (name, description, id, keywords). Empty\n * queries always match.\n */\nexport function getMarketplaceEntriesByPopularity(\n catalog: MarketplaceEntry[],\n): MarketplaceEntry[] {\n return catalog\n .map((entry, index) => ({ entry, index }))\n .sort((a, b) => {\n const byPopularity =\n (b.entry.popularityRank ?? 0) - (a.entry.popularityRank ?? 0);\n return byPopularity || a.index - b.index;\n })\n .map(({ entry }) => entry);\n}\n\nexport function getMarketplaceEntryById(\n id: string,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => entry.id === id);\n}\n\nexport function marketplaceEntryMatchesQuery(\n entry: MarketplaceEntry,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n entry.name,\n entry.description,\n entry.id,\n ...(entry.keywords ?? []),\n ]\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Search match for an installed (already-configured) server. We\n * search the server's own identifying fields and — if it's a catalog\n * entry — its catalog name/keywords too, so typing \"Slack\" matches\n * the installed Slack tile even though the persisted server is just\n * `{ type: \"stdio\", name: \"slack\", ... }`.\n */\nexport function installedServerMatchesQuery(\n server: MCPServerConfig,\n catalogEntry: MarketplaceEntry | undefined,\n rawQuery: string,\n): boolean {\n const q = normalize(rawQuery);\n if (!q) return true;\n const haystack = [\n server.type,\n \"name\" in server ? server.name : undefined,\n \"command\" in server ? server.command : undefined,\n \"args\" in server ? server.args?.join(\" \") : undefined,\n \"url\" in server ? server.url : undefined,\n catalogEntry?.name,\n catalogEntry?.description,\n catalogEntry?.id,\n ...(catalogEntry?.keywords ?? []),\n ]\n .filter(Boolean)\n .join(\" \")\n .toLowerCase();\n return haystack.includes(q);\n}\n\n/**\n * Look up the catalog entry that best matches an installed server.\n * Mirrors the lookup used in `installed-server-card.tsx` for\n * rendering the friendly icon.\n */\nexport function findCatalogEntryForServer(\n server: MCPServerConfig,\n catalog: MarketplaceEntry[],\n): MarketplaceEntry | undefined {\n return catalog.find((entry) => {\n // Check every MCP option rather than only the default. Some unified\n // integration entries default to OAuth-hosted MCP while still exposing\n // an API/stdio option; existing installed servers should match either.\n return getMcpConnectionOptions(entry).some((option) =>\n transportMatchesServer(option.transport, server),\n );\n });\n}\n"],"mappings":"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"}