@openhands/agent-canvas 1.7.1 → 1.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (354) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{Trans-DP5rcauH.js → Trans-B7lYw4uD.js} +1 -1
  4. package/build/assets/{acp-providers-BHKZvEm5.js → acp-providers-WUD4tfSq.js} +1 -1
  5. package/build/assets/active-backend-context-mY7Gff5c.js +2 -0
  6. package/build/assets/{agent-context-settings-BwAakl29.js → agent-context-settings-BTUmWEzL.js} +1 -1
  7. package/build/assets/{agent-profiles-settings-BYU6aWus.js → agent-profiles-settings-Ilqid_R5.js} +1 -1
  8. package/build/assets/agent-server-conversation-service.api-C_7YnjzP.js +41 -0
  9. package/build/assets/{agent-settings-DEPFl5hR.js → agent-settings-BGTyoT6X.js} +2 -2
  10. package/build/assets/agent-settings-CxfT4ksO.js +1 -0
  11. package/build/assets/{alert-banner-C1nzoXBC.js → alert-banner-eRyIwDyU.js} +1 -1
  12. package/build/assets/api-key-entry-screen-JOx3SH1-.js +1 -0
  13. package/build/assets/app-settings-D53QZy5p.js +1 -0
  14. package/build/assets/automation-detail-nEflClmc.js +2 -0
  15. package/build/assets/{automation-export-Ca2ehC-k.js → automation-export-BO_kfqC8.js} +1 -1
  16. package/build/assets/{automations-list-ZHQSW8qD.js → automations-list-BYncC7gv.js} +1 -1
  17. package/build/assets/{back-nav-button-Be_L157B.js → back-nav-button-C02hGa8t.js} +1 -1
  18. package/build/assets/backend-form-modal-CKrBF80P.js +1 -0
  19. package/build/assets/{backend-synced-settings-badge-DdUfuSN8.js → backend-synced-settings-badge-CQ9xtNWm.js} +1 -1
  20. package/build/assets/{badge-B6q3sVom.js → badge-CzXbf7tR.js} +1 -1
  21. package/build/assets/{base-modal-CSHhJTSr.js → base-modal-DkVyzFMt.js} +1 -1
  22. package/build/assets/{brand-button-CQO8oqVo.js → brand-button-BUlDL-mz.js} +1 -1
  23. package/build/assets/{browser-BUDJnBg4.js → browser-3QjrEr1D.js} +1 -1
  24. package/build/assets/{browser-tab-Cir7KbAL.js → browser-tab-BijpUbdt.js} +1 -1
  25. package/build/assets/{chat-send-button-D4614wUz.js → chat-send-button-BKsVjhtA.js} +1 -1
  26. package/build/assets/check-CrxQfJZ2.js +1 -0
  27. package/build/assets/chevron-down-D_LsyTA7.js +1 -0
  28. package/build/assets/circle-check-DZn9HkLE.js +1 -0
  29. package/build/assets/{circle-plus-check-toggle-E3Ecb_gg.js → circle-plus-check-toggle-DdjUiOo1.js} +1 -1
  30. package/build/assets/cloud-funnel-analytics-NlmDoeo2.js +1 -0
  31. package/build/assets/{combobox-caret-C8_eGdp2.js → combobox-caret-Dl9UtvD6.js} +1 -1
  32. package/build/assets/{command-menu-N_BriUy8.js → command-menu-CvLPs7dg.js} +1 -1
  33. package/build/assets/{condenser-settings-jNGOKFGz.js → condenser-settings-C4Y9CzQV.js} +1 -1
  34. package/build/assets/{confirmation-modal-gZtbojCm.js → confirmation-modal-CRBJSllP.js} +1 -1
  35. package/build/assets/{context-menu-list-item-BTHGFhIx.js → context-menu-list-item-FGaafXGh.js} +1 -1
  36. package/build/assets/conversation-D_ORtpKC.js +1 -0
  37. package/build/assets/conversation-Dpc5nmGi.js +90 -0
  38. package/build/assets/conversation-panel-D_ORtpKC.js +1 -0
  39. package/build/assets/conversation-state-store-CGRQemtx.js +1 -0
  40. package/build/assets/{conversation-tab-empty-state-BCMHkGQk.js → conversation-tab-empty-state-CDjvNgpG.js} +1 -1
  41. package/build/assets/{copy-B8Y5ufoR.js → copy-Drwu97Gx.js} +1 -1
  42. package/build/assets/{copy-to-clipboard-button-C4l3CMSd.js → copy-to-clipboard-button-NbN1clVr.js} +1 -1
  43. package/build/assets/{device-verify-C_M7Z8xs.js → device-verify-CDm_WrVu.js} +1 -1
  44. package/build/assets/{dropdown-classes-DQlj-MpS.js → dropdown-classes-CqA2BTCL.js} +1 -1
  45. package/build/assets/{entry.client-CCrlta8r.js → entry.client-BO2KQj0y.js} +2 -2
  46. package/build/assets/{enum-filter-dropdown-paB-lFSP.js → enum-filter-dropdown-DSqPZnsD.js} +1 -1
  47. package/build/assets/{environment-switch-overlay-D0FgCMqs.js → environment-switch-overlay-mGl2pqqx.js} +1 -1
  48. package/build/assets/extensions-hub-Br3Lgynp.js +1 -0
  49. package/build/assets/extensions-navigation-BQmstKDZ.js +1 -0
  50. package/build/assets/{external-link-Cnnd6ikC.js → external-link-D9B2QMPf.js} +1 -1
  51. package/build/assets/files-tab-4c9HFSLo.js +1 -0
  52. package/build/assets/{form-control-classes-CSPevi7c.js → form-control-classes-CtVAktAy.js} +1 -1
  53. package/build/assets/{format-time-delta-DuhE50B-.js → format-time-delta-DICE64sz.js} +1 -1
  54. package/build/assets/git-provider-icon-0PqmIS_S.js +1 -0
  55. package/build/assets/{highlighted-source-view-suUKd5if.js → highlighted-source-view-KMsA9c4a.js} +1 -1
  56. package/build/assets/home-D9JsyEmE.js +1 -0
  57. package/build/assets/{home-BZclW6kd.js → home-TFhnmZLx.js} +1 -1
  58. package/build/assets/index-home-D9JsyEmE.js +1 -0
  59. package/build/assets/install-server-modal-BgGRnqA0.js +1 -0
  60. package/build/assets/{launch-Bik37u9Y.js → launch-DjkKw55l.js} +1 -1
  61. package/build/assets/{list-todo-_BXQzOiB.js → list-todo-BpAf7aOT.js} +1 -1
  62. package/build/assets/{llm-not-configured-banner-znJFsW_O.js → llm-not-configured-banner-Y4W9-sar.js} +4 -4
  63. package/build/assets/llm-settings-D8zNkIlP.js +1 -0
  64. package/build/assets/llm-settings-nPwv7hKu.js +1 -0
  65. package/build/assets/{llm-subscription-service-_QUsVybQ.js → llm-subscription-service-COTgZc1C.js} +1 -1
  66. package/build/assets/loader-circle-CGD6-Vak.js +1 -0
  67. package/build/assets/{loading-spinner-CcpnCr_d.js → loading-spinner-DRFyh6Iy.js} +1 -1
  68. package/build/assets/manage-backends-modal-DiRWE3tJ.js +1 -0
  69. package/build/assets/manifest-1cf33a7b.js +1 -0
  70. package/build/assets/markdown-renderer-U40HF0Ak.js +8 -0
  71. package/build/assets/mcp-xDMaHzOw.js +9 -0
  72. package/build/assets/{messages-CYztjKLQ.js → messages-CdNfZPgd.js} +2 -2
  73. package/build/assets/{modal-body-DQhiw-AR.js → modal-body-C3bGicn4.js} +1 -1
  74. package/build/assets/{modal-classes-YaEEa5eZ.js → modal-classes-DFFO6BMU.js} +1 -1
  75. package/build/assets/{modal-close-button-turIYn0t.js → modal-close-button-vxkAIRyZ.js} +1 -1
  76. package/build/assets/{onboarding-9pEMFGtN.js → onboarding-DWuFieEA.js} +1 -1
  77. package/build/assets/onboarding-modal-C8m72Dpb.js +1 -0
  78. package/build/assets/organization-service.api-CQ73CB-S.js +1 -0
  79. package/build/assets/{pencil-8_lvWDoZ.js → pencil-DzW2GB2M.js} +1 -1
  80. package/build/assets/{planner-tab-Dr8B-ZBI.js → planner-tab-YG0bGxDY.js} +1 -1
  81. package/build/assets/plugins-management-service-ijhH8OSh.js +1 -0
  82. package/build/assets/plus-CTGLo3aa.js +1 -0
  83. package/build/assets/profiles-service.api-C32pPm70.js +1 -0
  84. package/build/assets/providers-DuSn9s2i.js +1 -0
  85. package/build/assets/proxy-BiyDhMBd.js +1 -0
  86. package/build/assets/{puzzle-DXWdfaa7.js → puzzle-DlqT0yvr.js} +1 -1
  87. package/build/assets/{recommended-automations-launcher-Dye8Li08.js → recommended-automations-launcher-Dru1yN46.js} +2 -2
  88. package/build/assets/{refresh-cw-D7CYLy-d.js → refresh-cw-Br5254f4.js} +1 -1
  89. package/build/assets/root-DOLLQteU.js +2 -0
  90. package/build/assets/root-layout-xmylWoew.js +3 -0
  91. package/build/assets/schema-field-htfNouTG.js +1 -0
  92. package/build/assets/{sdk-section-page-Di5mT86Y.js → sdk-section-page-D0K1dBQY.js} +1 -1
  93. package/build/assets/search-BrpWXWUi.js +1 -0
  94. package/build/assets/secrets-service-Dbv5Zb8G.js +1 -0
  95. package/build/assets/{secrets-settings-DPlA3moO.js → secrets-settings-Da4SSaX9.js} +1 -1
  96. package/build/assets/{segmented-toggle-DbbD3Tve.js → segmented-toggle-CZKOdIly.js} +1 -1
  97. package/build/assets/settings-F4a09IxY.js +1 -0
  98. package/build/assets/{settings-dropdown-input-Dx6vlkgf.js → settings-dropdown-input-jOiv5Yrk.js} +1 -1
  99. package/build/assets/{settings-index-D5dDKrDt.js → settings-index-CyJBf5NA.js} +1 -1
  100. package/build/assets/{settings-input-BjW_zyTN.js → settings-input-BEaTCem1.js} +1 -1
  101. package/build/assets/{settings-list-classes-gQdBahdt.js → settings-list-classes-VqMzkYYt.js} +1 -1
  102. package/build/assets/{settings-modal-DsJk2THd.js → settings-modal-C-S1NpbC.js} +1 -1
  103. package/build/assets/settings-service.api-CTJRrvMu.js +1 -0
  104. package/build/assets/{settings-switch-UNSf8K8I.js → settings-switch-B405oU_v.js} +1 -1
  105. package/build/assets/{shared-conversation-DU1kwDTF.js → shared-conversation-BvWtTwES.js} +1 -1
  106. package/build/assets/{sidebar-layout-DXHtbEj1.js → sidebar-layout-BGMuyC86.js} +1 -1
  107. package/build/assets/{sidebar-mobile-menu-toggle-BlmI8OrA.js → sidebar-mobile-menu-toggle-xiKoUfGa.js} +1 -1
  108. package/build/assets/{sidebar-nav-link-pvUivfK2.js → sidebar-nav-link-DA4_ct9G.js} +1 -1
  109. package/build/assets/{sidebar-store-DwrYKwYr.js → sidebar-store-FRJ3rADB.js} +1 -1
  110. package/build/assets/{skill-card-pill-row-D7XCz5uk.js → skill-card-pill-row-BMdhr_sd.js} +1 -1
  111. package/build/assets/{skill-icon-badge-HqBWZ2Eq.js → skill-icon-badge-CGUkgcVH.js} +1 -1
  112. package/build/assets/{skills-plugins-YJSV6A0M.js → skills-plugins-C5eJnyft.js} +1 -1
  113. package/build/assets/{skills-settings-BbREHv_-.js → skills-settings-pUxMr6WZ.js} +1 -1
  114. package/build/assets/{styled-tooltip-DsjDnR_W.js → styled-tooltip-B3kIdVyH.js} +1 -1
  115. package/build/assets/{task-list-tab-Dzim5ftn.js → task-list-tab-B3k4W1cK.js} +1 -1
  116. package/build/assets/{terminal-CYFAMiXx.js → terminal-CBI_41Zc.js} +1 -1
  117. package/build/assets/{toggle-switch-BptY3XRG.js → toggle-switch-DYRr9CAR.js} +1 -1
  118. package/build/assets/{trash-2-Bq4lHbTB.js → trash-2-gZPlx23R.js} +1 -1
  119. package/build/assets/{typography-C6fuRTTT.js → typography-B1UxuuEC.js} +1 -1
  120. package/build/assets/{use-acp-credential-form-CuBXKNvP.js → use-acp-credential-form-CjSvKWFH.js} +1 -1
  121. package/build/assets/use-activate-agent-profile-DCRf47e-.js +1 -0
  122. package/build/assets/use-agent-profiles-DmT9zxAN.js +1 -0
  123. package/build/assets/{use-can-manage-org-profiles-Uai-HHx2.js → use-can-manage-org-profiles-vBiP7rHF.js} +1 -1
  124. package/build/assets/use-cloud-current-user-id-BMq9gw93.js +1 -0
  125. package/build/assets/use-config-C959QcRC.js +1 -0
  126. package/build/assets/{use-create-conversation-DzcM79TE.js → use-create-conversation-CakhLF0X.js} +1 -1
  127. package/build/assets/{use-create-secret-2YLjoQw-.js → use-create-secret-RT4QtSS9.js} +1 -1
  128. package/build/assets/use-llm-profiles-CdaxVpyY.js +1 -0
  129. package/build/assets/use-onboarding-completion-CJU4MNAY.js +1 -0
  130. package/build/assets/use-plugins-marketplace-D-93nqtJ.js +1 -0
  131. package/build/assets/{use-save-agent-profile-Q2M72WgR.js → use-save-agent-profile-DGJ3-u0M.js} +1 -1
  132. package/build/assets/{use-save-settings-BZK7zVsK.js → use-save-settings--C6LZXfI.js} +1 -1
  133. package/build/assets/use-settings-DIAo5KFk.js +1 -0
  134. package/build/assets/use-settings-nav-items-DPK5sxvR.js +16 -0
  135. package/build/assets/use-skills-D_mLhYNa.js +1 -0
  136. package/build/assets/use-tracking-COqZLobY.js +1 -0
  137. package/build/assets/{use-user-conversation-43duhf7D.js → use-user-conversation-DhcAEyJX.js} +1 -1
  138. package/build/assets/{useMutation-DyQXZFEF.js → useMutation-DHLpZ6J0.js} +1 -1
  139. package/build/assets/utils-DsWhWn2F.js +1 -0
  140. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g4xrpilz-CJ7amdtQ.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~g4xrpilz-CYPMCkX1.js} +1 -1
  141. package/build/assets/{vendor~files-tab-vm3f67Pz.js → vendor~files-tab-atebg0iG.js} +1 -1
  142. package/build/assets/{vendor~index-home~home~conversation-panel~conversation-kX4LrGQC.js → vendor~index-home~home~conversation-panel~conversation-G9DMrR7m.js} +1 -1
  143. package/build/assets/{vendor~root-layout~conversation-panel~conversation-DpA6fir3.js → vendor~root-layout~conversation-panel~conversation-DEhW1h3I.js} +1 -1
  144. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation-EShpjxey.js → vendor~root-layout~index-home~home~conversation-panel~conversation-Acmf1sKj.js} +1 -1
  145. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-DD_UNO02.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-CmzCY1BT.js} +1 -1
  146. package/build/assets/{vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-Be63Q2Ln.js → vendor~root-layout~index-home~home~mcp~automations-list~onboarding-modal-C7Yh77RT.js} +1 -1
  147. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~bi2r7bko-Bw4AuHN1.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~bi2r7bko-VFnwct3q.js} +1 -1
  148. package/build/assets/{verification-settings-B4DL3Tez.js → verification-settings-Vr5cN4md.js} +1 -1
  149. package/build/assets/x-Dx_JbpHA.js +1 -0
  150. package/build/favicon-16x16.png +0 -0
  151. package/build/favicon-32x32.png +0 -0
  152. package/build/favicon.ico +0 -0
  153. package/build/index.html +4 -4
  154. package/build/locales/ar/openhands.json +2 -0
  155. package/build/locales/ca/openhands.json +2 -0
  156. package/build/locales/de/openhands.json +2 -0
  157. package/build/locales/en/openhands.json +2 -0
  158. package/build/locales/es/openhands.json +2 -0
  159. package/build/locales/fr/openhands.json +2 -0
  160. package/build/locales/it/openhands.json +2 -0
  161. package/build/locales/ja/openhands.json +2 -0
  162. package/build/locales/ko-KR/openhands.json +2 -0
  163. package/build/locales/no/openhands.json +2 -0
  164. package/build/locales/pt/openhands.json +2 -0
  165. package/build/locales/tr/openhands.json +2 -0
  166. package/build/locales/uk/openhands.json +2 -0
  167. package/build/locales/zh-CN/openhands.json +2 -0
  168. package/build/locales/zh-TW/openhands.json +2 -0
  169. package/config/defaults.json +4 -4
  170. package/dist/api/agent-server-adapter.cjs +1 -1
  171. package/dist/api/agent-server-adapter.cjs.map +1 -1
  172. package/dist/api/agent-server-adapter.js +7 -7
  173. package/dist/api/agent-server-adapter.js.map +1 -1
  174. package/dist/api/automation-service/automation-service.api.cjs +1 -1
  175. package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
  176. package/dist/api/automation-service/automation-service.api.js +95 -95
  177. package/dist/api/automation-service/automation-service.api.js.map +1 -1
  178. package/dist/api/backend-registry/storage.cjs +1 -1
  179. package/dist/api/backend-registry/storage.cjs.map +1 -1
  180. package/dist/api/backend-registry/storage.js +1 -1
  181. package/dist/api/backend-registry/storage.js.map +1 -1
  182. package/dist/api/backend-registry/types.d.ts +2 -0
  183. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  184. package/dist/api/cloud/conversation-service.api.js +22 -22
  185. package/dist/api/mcp-service/mcp-service.api.cjs +1 -1
  186. package/dist/api/mcp-service/mcp-service.api.cjs.map +1 -1
  187. package/dist/api/mcp-service/mcp-service.api.js +19 -19
  188. package/dist/api/mcp-service/mcp-service.api.js.map +1 -1
  189. package/dist/api/settings-service/settings-service.api.cjs +1 -1
  190. package/dist/api/settings-service/settings-service.api.cjs.map +1 -1
  191. package/dist/api/settings-service/settings-service.api.js +26 -12
  192. package/dist/api/settings-service/settings-service.api.js.map +1 -1
  193. package/dist/components/features/mcp-page/custom-server-editor.cjs +1 -1
  194. package/dist/components/features/mcp-page/custom-server-editor.js +16 -16
  195. package/dist/components/features/mcp-page/installed-server-card.cjs +1 -1
  196. package/dist/components/features/mcp-page/installed-server-card.cjs.map +1 -1
  197. package/dist/components/features/mcp-page/installed-server-card.d.ts +2 -2
  198. package/dist/components/features/mcp-page/installed-server-card.js +6 -7
  199. package/dist/components/features/mcp-page/installed-server-card.js.map +1 -1
  200. package/dist/components/features/mcp-page/installed-servers-section.cjs +1 -1
  201. package/dist/components/features/mcp-page/installed-servers-section.cjs.map +1 -1
  202. package/dist/components/features/mcp-page/installed-servers-section.d.ts +2 -2
  203. package/dist/components/features/mcp-page/installed-servers-section.js +2 -2
  204. package/dist/components/features/mcp-page/installed-servers-section.js.map +1 -1
  205. package/dist/components/features/mcp-page/mcp-server-health.cjs +1 -1
  206. package/dist/components/features/mcp-page/mcp-server-health.js +4 -4
  207. package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs +1 -1
  208. package/dist/components/features/settings/mcp-settings/mcp-server-form.cjs.map +1 -1
  209. package/dist/components/features/settings/mcp-settings/mcp-server-form.js +2 -1
  210. package/dist/components/features/settings/mcp-settings/mcp-server-form.js.map +1 -1
  211. package/dist/config/defaults.cjs +1 -1
  212. package/dist/config/defaults.cjs.map +1 -1
  213. package/dist/config/defaults.js +3 -3
  214. package/dist/config/defaults.js.map +1 -1
  215. package/dist/contexts/active-backend-context.cjs +1 -1
  216. package/dist/contexts/active-backend-context.cjs.map +1 -1
  217. package/dist/contexts/active-backend-context.d.ts +3 -2
  218. package/dist/contexts/active-backend-context.js +55 -42
  219. package/dist/contexts/active-backend-context.js.map +1 -1
  220. package/dist/favicon-16x16.png +0 -0
  221. package/dist/favicon-32x32.png +0 -0
  222. package/dist/favicon.ico +0 -0
  223. package/dist/hooks/mutation/use-update-mcp-server.cjs +1 -1
  224. package/dist/hooks/mutation/use-update-mcp-server.js +5 -5
  225. package/dist/hooks/query/use-cloud-current-user-id.cjs +1 -1
  226. package/dist/hooks/query/use-cloud-current-user-id.cjs.map +1 -1
  227. package/dist/hooks/query/use-cloud-current-user-id.js +7 -5
  228. package/dist/hooks/query/use-cloud-current-user-id.js.map +1 -1
  229. package/dist/hooks/query/use-cloud-organizations.cjs +1 -1
  230. package/dist/hooks/query/use-cloud-organizations.cjs.map +1 -1
  231. package/dist/hooks/query/use-cloud-organizations.js +5 -1
  232. package/dist/hooks/query/use-cloud-organizations.js.map +1 -1
  233. package/dist/hooks/use-can-manage-org-profiles.cjs +1 -1
  234. package/dist/hooks/use-can-manage-org-profiles.cjs.map +1 -1
  235. package/dist/hooks/use-can-manage-org-profiles.js +2 -1
  236. package/dist/hooks/use-can-manage-org-profiles.js.map +1 -1
  237. package/dist/hooks/use-tracking.cjs +1 -1
  238. package/dist/hooks/use-tracking.js +9 -9
  239. package/dist/i18n/declaration.cjs +1 -1
  240. package/dist/i18n/declaration.cjs.map +1 -1
  241. package/dist/i18n/declaration.d.ts +2 -0
  242. package/dist/i18n/declaration.js +1 -1
  243. package/dist/i18n/declaration.js.map +1 -1
  244. package/dist/i18n/translation.cjs +1 -1
  245. package/dist/i18n/translation.cjs.map +1 -1
  246. package/dist/i18n/translation.js +34 -0
  247. package/dist/i18n/translation.js.map +1 -1
  248. package/dist/locales/ar/openhands.json +2 -0
  249. package/dist/locales/ca/openhands.json +2 -0
  250. package/dist/locales/de/openhands.json +2 -0
  251. package/dist/locales/en/openhands.json +2 -0
  252. package/dist/locales/es/openhands.json +2 -0
  253. package/dist/locales/fr/openhands.json +2 -0
  254. package/dist/locales/it/openhands.json +2 -0
  255. package/dist/locales/ja/openhands.json +2 -0
  256. package/dist/locales/ko-KR/openhands.json +2 -0
  257. package/dist/locales/no/openhands.json +2 -0
  258. package/dist/locales/pt/openhands.json +2 -0
  259. package/dist/locales/tr/openhands.json +2 -0
  260. package/dist/locales/uk/openhands.json +2 -0
  261. package/dist/locales/zh-CN/openhands.json +2 -0
  262. package/dist/locales/zh-TW/openhands.json +2 -0
  263. package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.cjs +1 -1
  264. package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.cjs.map +1 -1
  265. package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.js +9 -0
  266. package/dist/node_modules/@openhands/typescript-client/dist/client/settings-client.js.map +1 -1
  267. package/dist/package.cjs +1 -1
  268. package/dist/package.cjs.map +1 -1
  269. package/dist/package.js +7 -2
  270. package/dist/package.js.map +1 -1
  271. package/dist/routes/app-settings.cjs +1 -1
  272. package/dist/routes/app-settings.js +5 -5
  273. package/dist/routes/mcp.cjs +1 -1
  274. package/dist/routes/mcp.cjs.map +1 -1
  275. package/dist/routes/mcp.js +73 -82
  276. package/dist/routes/mcp.js.map +1 -1
  277. package/dist/services/telemetry.cjs +1 -1
  278. package/dist/services/telemetry.cjs.map +1 -1
  279. package/dist/services/telemetry.d.ts +8 -0
  280. package/dist/services/telemetry.js +163 -116
  281. package/dist/services/telemetry.js.map +1 -1
  282. package/dist/types/mcp-server.d.ts +5 -0
  283. package/dist/types/settings.d.ts +3 -0
  284. package/dist/utils/mcp-config.cjs +1 -1
  285. package/dist/utils/mcp-config.cjs.map +1 -1
  286. package/dist/utils/mcp-config.d.ts +8 -0
  287. package/dist/utils/mcp-config.js +25 -20
  288. package/dist/utils/mcp-config.js.map +1 -1
  289. package/dist/utils/mcp-installed-servers.cjs +1 -1
  290. package/dist/utils/mcp-installed-servers.cjs.map +1 -1
  291. package/dist/utils/mcp-installed-servers.js +6 -3
  292. package/dist/utils/mcp-installed-servers.js.map +1 -1
  293. package/package.json +7 -2
  294. package/scripts/check-sdk-version-sync.mjs +6 -6
  295. package/scripts/dev-safe.mjs +2 -2
  296. package/scripts/stryker-diff.mjs +137 -0
  297. package/build/assets/active-backend-context-B9942JGX.js +0 -1
  298. package/build/assets/agent-server-conversation-service.api-D7qh9amG.js +0 -41
  299. package/build/assets/agent-settings-BbNLRtoh.js +0 -1
  300. package/build/assets/api-key-entry-screen-B2kVLRDQ.js +0 -1
  301. package/build/assets/app-settings-B92bjTei.js +0 -1
  302. package/build/assets/automation-detail-DoVSHQLs.js +0 -2
  303. package/build/assets/backend-form-modal-C0CIsQvd.js +0 -1
  304. package/build/assets/check-BMOG005H.js +0 -1
  305. package/build/assets/chevron-down-CV1zZP_K.js +0 -1
  306. package/build/assets/circle-check-lvN_jFN5.js +0 -1
  307. package/build/assets/client-source-D8jWKBfE.js +0 -1
  308. package/build/assets/cloud-funnel-analytics-CnCkPgQa.js +0 -1
  309. package/build/assets/conversation-DceG4LFS.js +0 -90
  310. package/build/assets/conversation-Do2Vg7uo.js +0 -1
  311. package/build/assets/conversation-panel-Do2Vg7uo.js +0 -1
  312. package/build/assets/conversation-state-store-BORusMiV.js +0 -1
  313. package/build/assets/extensions-hub-B2WudOET.js +0 -1
  314. package/build/assets/extensions-navigation-CYjIM65d.js +0 -1
  315. package/build/assets/files-tab-LSBijbEi.js +0 -1
  316. package/build/assets/git-provider-icon-DeQUgZVE.js +0 -1
  317. package/build/assets/home-C_R5pEbs.js +0 -1
  318. package/build/assets/index-home-C_R5pEbs.js +0 -1
  319. package/build/assets/install-server-modal-xCf76eim.js +0 -1
  320. package/build/assets/llm-settings-BYo9SN0I.js +0 -1
  321. package/build/assets/llm-settings-BaM0zJg5.js +0 -1
  322. package/build/assets/loader-circle-6zlEYCJw.js +0 -1
  323. package/build/assets/manage-backends-modal-n6PtHOER.js +0 -1
  324. package/build/assets/manifest-e9bbad7b.js +0 -1
  325. package/build/assets/markdown-renderer-Dkmjmx_d.js +0 -8
  326. package/build/assets/mcp-BJUOnO6v.js +0 -9
  327. package/build/assets/onboarding-modal-dYsjXJwx.js +0 -1
  328. package/build/assets/organization-service.api-HsxJhNCf.js +0 -1
  329. package/build/assets/plugins-management-service-DEjBqDoT.js +0 -1
  330. package/build/assets/plus-vIbGD459.js +0 -1
  331. package/build/assets/profiles-service.api-BddMsEhq.js +0 -1
  332. package/build/assets/providers-BxJi3BMO.js +0 -1
  333. package/build/assets/proxy-CN3HaaOW.js +0 -1
  334. package/build/assets/root-D4hpCAq9.js +0 -2
  335. package/build/assets/root-layout-a4-4sfbh.js +0 -3
  336. package/build/assets/schema-field-CZSs0hQP.js +0 -1
  337. package/build/assets/search-sKuYKllf.js +0 -1
  338. package/build/assets/secrets-service-i4CCd-UN.js +0 -1
  339. package/build/assets/settings-SHSt9Rq2.js +0 -1
  340. package/build/assets/settings-service.api-i_HHIqJP.js +0 -1
  341. package/build/assets/telemetry-B-2fXtqV.js +0 -2
  342. package/build/assets/use-activate-agent-profile-CXDcjVt8.js +0 -1
  343. package/build/assets/use-agent-profiles-CliaHofi.js +0 -1
  344. package/build/assets/use-cloud-current-user-id-BzamKpu-.js +0 -1
  345. package/build/assets/use-config-DPEYpfIj.js +0 -1
  346. package/build/assets/use-llm-profiles-Ci3zAVzV.js +0 -1
  347. package/build/assets/use-onboarding-completion-STApsofr.js +0 -1
  348. package/build/assets/use-plugins-marketplace-CzzdrUSK.js +0 -1
  349. package/build/assets/use-settings-BKpkUS4y.js +0 -1
  350. package/build/assets/use-settings-nav-items-BlXge_bf.js +0 -16
  351. package/build/assets/use-skills-BltA9XrV.js +0 -1
  352. package/build/assets/use-tracking-De7qByD4.js +0 -1
  353. package/build/assets/utils-qjs_KuS4.js +0 -1
  354. package/build/assets/x-EjvKGawc.js +0 -1
@@ -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\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPJsonValue,\n MCPOAuthState,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\nimport { toMcpServerName } from \"#/utils/mcp-server-name\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nexport const REDACTED_MCP_SECRET_VALUE = \"**********\";\n\ntype SdkMcpServerConfig = Record<string, SettingsValue | MCPAuthCredential>;\ntype SdkMcpConfig = Record<string, SdkMcpServerConfig>;\n\n/**\n * The dict key for an SSE/SHTTP server doubles as its display name once a\n * user provides one. Keys that match the auto-generated fallback pattern\n * (``sse``, ``shttp``, ``shttp_1``, …) emitted by {@link toSdkMcpConfig}\n * carry no user intent, so we surface them as \"no name\" — re-serializing\n * regenerates the identical fallback key and existing configs are left\n * untouched.\n */\nfunction userGivenServerName(\n serverName: string,\n base: \"sse\" | \"shttp\",\n): string | undefined {\n const autoGenerated = new RegExp(`^${base}(_\\\\d+)?$`);\n return autoGenerated.test(serverName) ? undefined : serverName;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function getSdkMcpServerMap(\n value: unknown,\n): Record<string, unknown> | null {\n if (!isRecord(value)) return null;\n\n const mcpServers = value.mcpServers;\n if (\n isRecord(mcpServers) &&\n !(\"url\" in mcpServers) &&\n !(\"command\" in mcpServers)\n ) {\n return mcpServers;\n }\n\n return value;\n}\n\nexport function stringRecord(\n value: unknown,\n): Record<string, string> | undefined {\n if (!isRecord(value)) return undefined;\n const entries = Object.entries(value).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nexport function hasRedactedMcpSecretLeaf(value: unknown): boolean {\n if (value === REDACTED_MCP_SECRET_VALUE) return true;\n if (Array.isArray(value)) return value.some(hasRedactedMcpSecretLeaf);\n if (isRecord(value))\n return Object.values(value).some(hasRedactedMcpSecretLeaf);\n return false;\n}\n\nfunction getOAuthState(value: unknown): MCPOAuthState | undefined {\n if (!isRecord(value)) return undefined;\n const state: MCPOAuthState = {};\n if (isRecord(value.tokens)) {\n state.tokens = value.tokens as NonNullable<MCPOAuthState[\"tokens\"]>;\n }\n if (isRecord(value.client_info)) {\n state.client_info = value.client_info as NonNullable<\n MCPOAuthState[\"client_info\"]\n >;\n }\n if (\n typeof value.token_expires_at === \"number\" ||\n value.token_expires_at === null\n ) {\n state.token_expires_at = value.token_expires_at;\n }\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\nfunction normalizeTaggedAuthCredential(\n value: unknown,\n): MCPAuthCredential | undefined {\n if (!isRecord(value)) return undefined;\n switch (value.strategy) {\n case \"none\":\n return { strategy: \"none\" };\n case \"api_key\":\n if (typeof value.value !== \"string\") return undefined;\n return {\n strategy: \"api_key\",\n value: value.value,\n ...(typeof value.header_name === \"string\" && {\n header_name: value.header_name,\n }),\n };\n case \"bearer\":\n if (typeof value.value !== \"string\") return undefined;\n return { strategy: \"bearer\", value: value.value };\n case \"basic\":\n if (\n typeof value.username !== \"string\" ||\n typeof value.password !== \"string\"\n ) {\n return undefined;\n }\n return {\n strategy: \"basic\",\n username: value.username,\n password: value.password,\n };\n case \"header\": {\n const headers = stringRecord(value.headers);\n return headers ? { strategy: \"header\", headers } : undefined;\n }\n case \"oauth2\": {\n const authentication = getAuthenticationConfig(value.authentication);\n const state = getOAuthState(value.state);\n return {\n strategy: \"oauth2\",\n ...(authentication && { authentication }),\n ...(state && { state }),\n };\n }\n default:\n return undefined;\n }\n}\n\nfunction authCredentialFromServerConfig(\n serverConfig: Record<string, unknown>,\n): MCPAuthCredential | undefined {\n return normalizeTaggedAuthCredential(serverConfig.auth);\n}\n\nfunction getRemoteSecretFields(entry: {\n auth?: MCPAuthCredential;\n headers?: Record<string, string>;\n}): Partial<SdkMcpServerConfig> {\n const fields: Partial<SdkMcpServerConfig> = {};\n if (entry.auth && !hasRedactedMcpSecretLeaf(entry.auth)) {\n fields.auth = entry.auth;\n }\n if (entry.headers && Object.keys(entry.headers).length > 0) {\n fields.headers = entry.headers;\n }\n return fields;\n}\n\nfunction getRemoteServerFields(server: MCPServerConfig) {\n return {\n ...(server.name && { name: server.name }),\n url: server.url!,\n ...(server.headers && { headers: server.headers }),\n ...(server.auth && { auth: server.auth }),\n };\n}\n\nexport function toMcpSseServer(server: MCPServerConfig): MCPSSEServer {\n return getRemoteServerFields(server);\n}\n\nexport function toMcpShttpServer(server: MCPServerConfig): MCPSHTTPServer {\n return {\n ...getRemoteServerFields(server),\n ...(server.timeout !== undefined && { timeout: server.timeout }),\n };\n}\n\nexport function toMcpStdioServer(server: MCPServerConfig): MCPStdioServer {\n return {\n name: server.name!,\n command: server.command!,\n ...(server.args && { args: server.args }),\n ...(server.env && { env: server.env }),\n };\n}\n\nfunction getAuthenticationConfig(\n value: unknown,\n): MCPAuthenticationConfig | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const record = value as Record<string, unknown>;\n if (record.type !== \"oauth\") return undefined;\n const authentication: MCPAuthenticationConfig = { type: \"oauth\" };\n if (\n record.client_auth_method === \"none\" ||\n record.client_auth_method === \"client_secret_post\" ||\n record.client_auth_method === \"client_secret_basic\" ||\n record.client_auth_method === \"private_key_jwt\"\n ) {\n authentication.client_auth_method = record.client_auth_method;\n }\n if (typeof record.scopes === \"string\") {\n authentication.scopes = record.scopes;\n } else if (\n Array.isArray(record.scopes) &&\n record.scopes.every((scope) => typeof scope === \"string\")\n ) {\n authentication.scopes = record.scopes;\n }\n if (typeof record.client_name === \"string\") {\n authentication.client_name = record.client_name;\n }\n if (typeof record.client_metadata_url === \"string\") {\n authentication.client_metadata_url = record.client_metadata_url;\n }\n if (typeof record.client_id === \"string\") {\n authentication.client_id = record.client_id;\n }\n if (typeof record.client_secret === \"string\") {\n authentication.client_secret = record.client_secret;\n }\n if (\n record.additional_client_metadata &&\n typeof record.additional_client_metadata === \"object\" &&\n !Array.isArray(record.additional_client_metadata)\n ) {\n authentication.additional_client_metadata =\n record.additional_client_metadata as Record<string, MCPJsonValue>;\n }\n return authentication;\n}\n\n/**\n * Parse the SDK-native ``agent_settings.mcp_config`` value and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n const mcpConfig = getSdkMcpServerMap(value);\n if (!mcpConfig) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {\n if (!isRecord(serverConfig)) 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 = authCredentialFromServerConfig(serverConfig);\n const headers = stringRecord(serverConfig.headers);\n\n if (transport === \"sse\") {\n const name = userGivenServerName(serverName, \"sse\");\n const server: MCPSSEServer = { url };\n if (name) server.name = name;\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n sseServers.push(server);\n } else {\n const name = userGivenServerName(serverName, \"shttp\");\n const server: MCPSHTTPServer = { url };\n if (name) server.name = name;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n shttpServers.push(server);\n }\n } else {\n if (typeof serverConfig.command !== \"string\") continue;\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command,\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-native server map\n * 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 mcpConfig: SdkMcpConfig = {};\n\n const reserve = (base: string): string => {\n const safeBase = toMcpServerName(base);\n if (!(safeBase in mcpConfig)) return safeBase;\n let i = 1;\n while (`${safeBase}_${i}` in mcpConfig) i += 1;\n return `${safeBase}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry));\n }\n server.transport = \"sse\";\n mcpConfig[reserve(name || \"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = { transport: \"http\" };\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry));\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpConfig[reserve(name || \"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 mcpConfig[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpConfig).length > 0 ? mcpConfig : null;\n}\n"],"mappings":"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"}
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\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPJsonValue,\n MCPOAuthState,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\nimport { toMcpServerName } from \"#/utils/mcp-server-name\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nexport const REDACTED_MCP_SECRET_VALUE = \"**********\";\n\ntype SdkMcpServerConfig = Record<string, SettingsValue | MCPAuthCredential>;\ntype SdkMcpConfig = Record<string, SdkMcpServerConfig>;\n\n/**\n * The dict key for an SSE/SHTTP server doubles as its display name once a\n * user provides one. Keys that match the auto-generated fallback pattern\n * (``sse``, ``shttp``, ``shttp_1``, …) emitted by {@link toSdkMcpConfig}\n * carry no user intent, so we surface them as \"no name\" — re-serializing\n * regenerates the identical fallback key and existing configs are left\n * untouched.\n */\nfunction userGivenServerName(\n serverName: string,\n base: \"sse\" | \"shttp\",\n): string | undefined {\n const autoGenerated = new RegExp(`^${base}(_\\\\d+)?$`);\n return autoGenerated.test(serverName) ? undefined : serverName;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function getSdkMcpServerMap(\n value: unknown,\n): Record<string, unknown> | null {\n if (!isRecord(value)) return null;\n\n const mcpServers = value.mcpServers;\n if (\n isRecord(mcpServers) &&\n !(\"url\" in mcpServers) &&\n !(\"command\" in mcpServers)\n ) {\n return mcpServers;\n }\n\n return value;\n}\n\nexport function stringRecord(\n value: unknown,\n): Record<string, string> | undefined {\n if (!isRecord(value)) return undefined;\n const entries = Object.entries(value).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nexport function hasRedactedMcpSecretLeaf(value: unknown): boolean {\n if (value === REDACTED_MCP_SECRET_VALUE) return true;\n if (Array.isArray(value)) return value.some(hasRedactedMcpSecretLeaf);\n if (isRecord(value))\n return Object.values(value).some(hasRedactedMcpSecretLeaf);\n return false;\n}\n\nfunction getOAuthState(value: unknown): MCPOAuthState | undefined {\n if (!isRecord(value)) return undefined;\n const state: MCPOAuthState = {};\n if (isRecord(value.tokens)) {\n state.tokens = value.tokens as NonNullable<MCPOAuthState[\"tokens\"]>;\n }\n if (isRecord(value.client_info)) {\n state.client_info = value.client_info as NonNullable<\n MCPOAuthState[\"client_info\"]\n >;\n }\n if (\n typeof value.token_expires_at === \"number\" ||\n value.token_expires_at === null\n ) {\n state.token_expires_at = value.token_expires_at;\n }\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\nfunction normalizeTaggedAuthCredential(\n value: unknown,\n): MCPAuthCredential | undefined {\n if (!isRecord(value)) return undefined;\n switch (value.strategy) {\n case \"none\":\n return { strategy: \"none\" };\n case \"api_key\":\n if (typeof value.value !== \"string\") return undefined;\n return {\n strategy: \"api_key\",\n value: value.value,\n ...(typeof value.header_name === \"string\" && {\n header_name: value.header_name,\n }),\n };\n case \"bearer\":\n if (typeof value.value !== \"string\") return undefined;\n return { strategy: \"bearer\", value: value.value };\n case \"basic\":\n if (\n typeof value.username !== \"string\" ||\n typeof value.password !== \"string\"\n ) {\n return undefined;\n }\n return {\n strategy: \"basic\",\n username: value.username,\n password: value.password,\n };\n case \"header\": {\n const headers = stringRecord(value.headers);\n return headers ? { strategy: \"header\", headers } : undefined;\n }\n case \"oauth2\": {\n const authentication = getAuthenticationConfig(value.authentication);\n const state = getOAuthState(value.state);\n return {\n strategy: \"oauth2\",\n ...(authentication && { authentication }),\n ...(state && { state }),\n };\n }\n default:\n return undefined;\n }\n}\n\nfunction authCredentialFromServerConfig(\n serverConfig: Record<string, unknown>,\n): MCPAuthCredential | undefined {\n return normalizeTaggedAuthCredential(serverConfig.auth);\n}\n\nfunction getRemoteSecretFields(entry: {\n auth?: MCPAuthCredential;\n headers?: Record<string, string>;\n}): Partial<SdkMcpServerConfig> {\n const fields: Partial<SdkMcpServerConfig> = {};\n if (entry.auth && !hasRedactedMcpSecretLeaf(entry.auth)) {\n fields.auth = entry.auth;\n }\n if (entry.headers && Object.keys(entry.headers).length > 0) {\n fields.headers = entry.headers;\n }\n return fields;\n}\n\n/**\n * Only an explicit ``false`` is carried. Every mutation round-trips the whole\n * server map through these converters, so preserving the flag here is what\n * stops adding or deleting one server from silently re-enabling another.\n */\nfunction disabledField(server: { enabled?: boolean }) {\n return server.enabled === false ? { enabled: false as const } : {};\n}\n\nfunction getRemoteServerFields(server: MCPServerConfig) {\n return {\n ...(server.name && { name: server.name }),\n url: server.url!,\n ...(server.headers && { headers: server.headers }),\n ...(server.auth && { auth: server.auth }),\n ...disabledField(server),\n };\n}\n\nexport function toMcpSseServer(server: MCPServerConfig): MCPSSEServer {\n return getRemoteServerFields(server);\n}\n\nexport function toMcpShttpServer(server: MCPServerConfig): MCPSHTTPServer {\n return {\n ...getRemoteServerFields(server),\n ...(server.timeout !== undefined && { timeout: server.timeout }),\n };\n}\n\nexport function toMcpStdioServer(server: MCPServerConfig): MCPStdioServer {\n return {\n name: server.name!,\n command: server.command!,\n ...(server.args && { args: server.args }),\n ...(server.env && { env: server.env }),\n ...disabledField(server),\n };\n}\n\nfunction getAuthenticationConfig(\n value: unknown,\n): MCPAuthenticationConfig | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const record = value as Record<string, unknown>;\n if (record.type !== \"oauth\") return undefined;\n const authentication: MCPAuthenticationConfig = { type: \"oauth\" };\n if (\n record.client_auth_method === \"none\" ||\n record.client_auth_method === \"client_secret_post\" ||\n record.client_auth_method === \"client_secret_basic\" ||\n record.client_auth_method === \"private_key_jwt\"\n ) {\n authentication.client_auth_method = record.client_auth_method;\n }\n if (typeof record.scopes === \"string\") {\n authentication.scopes = record.scopes;\n } else if (\n Array.isArray(record.scopes) &&\n record.scopes.every((scope) => typeof scope === \"string\")\n ) {\n authentication.scopes = record.scopes;\n }\n if (typeof record.client_name === \"string\") {\n authentication.client_name = record.client_name;\n }\n if (typeof record.client_metadata_url === \"string\") {\n authentication.client_metadata_url = record.client_metadata_url;\n }\n if (typeof record.client_id === \"string\") {\n authentication.client_id = record.client_id;\n }\n if (typeof record.client_secret === \"string\") {\n authentication.client_secret = record.client_secret;\n }\n if (\n record.additional_client_metadata &&\n typeof record.additional_client_metadata === \"object\" &&\n !Array.isArray(record.additional_client_metadata)\n ) {\n authentication.additional_client_metadata =\n record.additional_client_metadata as Record<string, MCPJsonValue>;\n }\n return authentication;\n}\n\n/**\n * Parse the SDK-native ``agent_settings.mcp_config`` value and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n const mcpConfig = getSdkMcpServerMap(value);\n if (!mcpConfig) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {\n if (!isRecord(serverConfig)) continue;\n\n const url = serverConfig.url as string | undefined;\n\n // The SDK omits ``enabled`` while it holds its default (true), so only an\n // explicit ``false`` marks a server as switched off.\n const disabled = serverConfig.enabled === false;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = authCredentialFromServerConfig(serverConfig);\n const headers = stringRecord(serverConfig.headers);\n\n if (transport === \"sse\") {\n const name = userGivenServerName(serverName, \"sse\");\n const server: MCPSSEServer = { url };\n if (name) server.name = name;\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n if (disabled) server.enabled = false;\n sseServers.push(server);\n } else {\n const name = userGivenServerName(serverName, \"shttp\");\n const server: MCPSHTTPServer = { url };\n if (name) server.name = name;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n if (disabled) server.enabled = false;\n shttpServers.push(server);\n }\n } else {\n if (typeof serverConfig.command !== \"string\") continue;\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command,\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 if (disabled) stdioServer.enabled = false;\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-native server map\n * 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 *\n * ``enabled`` is only emitted when a server is switched off — the SDK defaults\n * it to true. Omitting it is what *clears* a previously persisted\n * ``enabled: false``, and that only works because ``saveSettings`` pre-clears\n * ``mcp_config`` to null before every write (see\n * ``settings-service.api.ts``), turning the backend's RFC 7386 merge into a\n * full replace. If that pre-clear is ever removed, re-enabling a server\n * becomes a silent no-op.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpConfig: SdkMcpConfig = {};\n\n const reserve = (base: string): string => {\n const safeBase = toMcpServerName(base);\n if (!(safeBase in mcpConfig)) return safeBase;\n let i = 1;\n while (`${safeBase}_${i}` in mcpConfig) i += 1;\n return `${safeBase}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry), disabledField(entry));\n }\n server.transport = \"sse\";\n mcpConfig[reserve(name || \"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = { transport: \"http\" };\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry), disabledField(entry));\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpConfig[reserve(name || \"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 Object.assign(server, disabledField(entry));\n mcpConfig[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpConfig).length > 0 ? mcpConfig : null;\n}\n"],"mappings":"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"}
@@ -26,6 +26,14 @@ export declare function parseMcpConfig(value: unknown): MCPConfig;
26
26
  * persisted ahead of it, and shift the suffix on every save as the count
27
27
  * of other server types changes. With per-base collision suffixing,
28
28
  * unrelated entries keep their human-meaningful names stable across edits.
29
+ *
30
+ * ``enabled`` is only emitted when a server is switched off — the SDK defaults
31
+ * it to true. Omitting it is what *clears* a previously persisted
32
+ * ``enabled: false``, and that only works because ``saveSettings`` pre-clears
33
+ * ``mcp_config`` to null before every write (see
34
+ * ``settings-service.api.ts``), turning the backend's RFC 7386 merge into a
35
+ * full replace. If that pre-clear is ever removed, re-enabling a server
36
+ * becomes a silent no-op.
29
37
  */
30
38
  export declare function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null;
31
39
  export {};
@@ -54,7 +54,7 @@ function l(e) {
54
54
  } : void 0;
55
55
  }
56
56
  case "oauth2": {
57
- let t = g(e.authentication), n = c(e.state);
57
+ let t = _(e.authentication), n = c(e.state);
58
58
  return {
59
59
  strategy: "oauth2",
60
60
  ...t && { authentication: t },
@@ -72,52 +72,57 @@ function d(e) {
72
72
  return e.auth && !s(e.auth) && (t.auth = e.auth), e.headers && Object.keys(e.headers).length > 0 && (t.headers = e.headers), t;
73
73
  }
74
74
  function f(e) {
75
+ return e.enabled === !1 ? { enabled: !1 } : {};
76
+ }
77
+ function p(e) {
75
78
  return {
76
79
  ...e.name && { name: e.name },
77
80
  url: e.url,
78
81
  ...e.headers && { headers: e.headers },
79
- ...e.auth && { auth: e.auth }
82
+ ...e.auth && { auth: e.auth },
83
+ ...f(e)
80
84
  };
81
85
  }
82
- function p(e) {
83
- return f(e);
84
- }
85
86
  function m(e) {
87
+ return p(e);
88
+ }
89
+ function h(e) {
86
90
  return {
87
- ...f(e),
91
+ ...p(e),
88
92
  ...e.timeout !== void 0 && { timeout: e.timeout }
89
93
  };
90
94
  }
91
- function h(e) {
95
+ function g(e) {
92
96
  return {
93
97
  name: e.name,
94
98
  command: e.command,
95
99
  ...e.args && { args: e.args },
96
- ...e.env && { env: e.env }
100
+ ...e.env && { env: e.env },
101
+ ...f(e)
97
102
  };
98
103
  }
99
- function g(e) {
104
+ function _(e) {
100
105
  if (!e || typeof e != "object") return;
101
106
  let t = e;
102
107
  if (t.type !== "oauth") return;
103
108
  let n = { type: "oauth" };
104
109
  return (t.client_auth_method === "none" || t.client_auth_method === "client_secret_post" || t.client_auth_method === "client_secret_basic" || t.client_auth_method === "private_key_jwt") && (n.client_auth_method = t.client_auth_method), (typeof t.scopes == "string" || Array.isArray(t.scopes) && t.scopes.every((e) => typeof e == "string")) && (n.scopes = t.scopes), typeof t.client_name == "string" && (n.client_name = t.client_name), typeof t.client_metadata_url == "string" && (n.client_metadata_url = t.client_metadata_url), typeof t.client_id == "string" && (n.client_id = t.client_id), typeof t.client_secret == "string" && (n.client_secret = t.client_secret), t.additional_client_metadata && typeof t.additional_client_metadata == "object" && !Array.isArray(t.additional_client_metadata) && (n.additional_client_metadata = t.additional_client_metadata), n;
105
110
  }
106
- function _(e) {
111
+ function v(e) {
107
112
  let n = a(e);
108
113
  if (!n) return { ...t };
109
114
  let s = [], c = [], l = [];
110
115
  for (let [e, t] of Object.entries(n)) {
111
116
  if (!i(t)) continue;
112
- let n = t.url;
117
+ let n = t.url, a = t.enabled === !1;
113
118
  if (n) {
114
- let i = t.transport, a = u(t), c = o(t.headers);
119
+ let i = t.transport, c = u(t), d = o(t.headers);
115
120
  if (i === "sse") {
116
121
  let t = r(e, "sse"), i = { url: n };
117
- t && (i.name = t), a && (i.auth = a), c && (i.headers = c), s.push(i);
122
+ t && (i.name = t), c && (i.auth = c), d && (i.headers = d), a && (i.enabled = !1), s.push(i);
118
123
  } else {
119
124
  let i = r(e, "shttp"), o = { url: n };
120
- i && (o.name = i), t.timeout != null && (o.timeout = t.timeout), a && (o.auth = a), c && (o.headers = c), l.push(o);
125
+ i && (o.name = i), t.timeout != null && (o.timeout = t.timeout), c && (o.auth = c), d && (o.headers = d), a && (o.enabled = !1), l.push(o);
121
126
  }
122
127
  } else {
123
128
  if (typeof t.command != "string") continue;
@@ -125,7 +130,7 @@ function _(e) {
125
130
  name: e,
126
131
  command: t.command
127
132
  };
128
- t.args && (n.args = t.args), t.env && (n.env = t.env), c.push(n);
133
+ t.args && (n.args = t.args), t.env && (n.env = t.env), a && (n.enabled = !1), c.push(n);
129
134
  }
130
135
  }
131
136
  return {
@@ -134,7 +139,7 @@ function _(e) {
134
139
  shttp_servers: l
135
140
  };
136
141
  }
137
- function v(t) {
142
+ function y(t) {
138
143
  let n = {}, r = (t) => {
139
144
  let r = e(t);
140
145
  if (!(r in n)) return r;
@@ -144,19 +149,19 @@ function v(t) {
144
149
  };
145
150
  for (let e of t.sse_servers) {
146
151
  let t = {}, i;
147
- typeof e == "string" ? t.url = e : (i = e.name, t.url = e.url, Object.assign(t, d(e))), t.transport = "sse", n[r(i || "sse")] = t;
152
+ typeof e == "string" ? t.url = e : (i = e.name, t.url = e.url, Object.assign(t, d(e), f(e))), t.transport = "sse", n[r(i || "sse")] = t;
148
153
  }
149
154
  for (let e of t.shttp_servers) {
150
155
  let t = { transport: "http" }, i;
151
- typeof e == "string" ? t.url = e : (i = e.name, t.url = e.url, Object.assign(t, d(e)), e.timeout != null && (t.timeout = e.timeout)), n[r(i || "shttp")] = t;
156
+ typeof e == "string" ? t.url = e : (i = e.name, t.url = e.url, Object.assign(t, d(e), f(e)), e.timeout != null && (t.timeout = e.timeout)), n[r(i || "shttp")] = t;
152
157
  }
153
158
  for (let e of t.stdio_servers) {
154
159
  let t = { command: e.command };
155
- e.args && (t.args = e.args), e.env && (t.env = e.env), n[r(e.name || "stdio")] = t;
160
+ e.args && (t.args = e.args), e.env && (t.env = e.env), Object.assign(t, f(e)), n[r(e.name || "stdio")] = t;
156
161
  }
157
162
  return Object.keys(n).length > 0 ? n : null;
158
163
  }
159
164
  //#endregion
160
- export { n as REDACTED_MCP_SECRET_VALUE, a as getSdkMcpServerMap, s as hasRedactedMcpSecretLeaf, _ as parseMcpConfig, o as stringRecord, m as toMcpShttpServer, p as toMcpSseServer, h as toMcpStdioServer, v as toSdkMcpConfig };
165
+ export { n as REDACTED_MCP_SECRET_VALUE, a as getSdkMcpServerMap, s as hasRedactedMcpSecretLeaf, v as parseMcpConfig, o as stringRecord, h as toMcpShttpServer, m as toMcpSseServer, g as toMcpStdioServer, y as toSdkMcpConfig };
161
166
 
162
167
  //# 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\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPJsonValue,\n MCPOAuthState,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\nimport { toMcpServerName } from \"#/utils/mcp-server-name\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nexport const REDACTED_MCP_SECRET_VALUE = \"**********\";\n\ntype SdkMcpServerConfig = Record<string, SettingsValue | MCPAuthCredential>;\ntype SdkMcpConfig = Record<string, SdkMcpServerConfig>;\n\n/**\n * The dict key for an SSE/SHTTP server doubles as its display name once a\n * user provides one. Keys that match the auto-generated fallback pattern\n * (``sse``, ``shttp``, ``shttp_1``, …) emitted by {@link toSdkMcpConfig}\n * carry no user intent, so we surface them as \"no name\" — re-serializing\n * regenerates the identical fallback key and existing configs are left\n * untouched.\n */\nfunction userGivenServerName(\n serverName: string,\n base: \"sse\" | \"shttp\",\n): string | undefined {\n const autoGenerated = new RegExp(`^${base}(_\\\\d+)?$`);\n return autoGenerated.test(serverName) ? undefined : serverName;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function getSdkMcpServerMap(\n value: unknown,\n): Record<string, unknown> | null {\n if (!isRecord(value)) return null;\n\n const mcpServers = value.mcpServers;\n if (\n isRecord(mcpServers) &&\n !(\"url\" in mcpServers) &&\n !(\"command\" in mcpServers)\n ) {\n return mcpServers;\n }\n\n return value;\n}\n\nexport function stringRecord(\n value: unknown,\n): Record<string, string> | undefined {\n if (!isRecord(value)) return undefined;\n const entries = Object.entries(value).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nexport function hasRedactedMcpSecretLeaf(value: unknown): boolean {\n if (value === REDACTED_MCP_SECRET_VALUE) return true;\n if (Array.isArray(value)) return value.some(hasRedactedMcpSecretLeaf);\n if (isRecord(value))\n return Object.values(value).some(hasRedactedMcpSecretLeaf);\n return false;\n}\n\nfunction getOAuthState(value: unknown): MCPOAuthState | undefined {\n if (!isRecord(value)) return undefined;\n const state: MCPOAuthState = {};\n if (isRecord(value.tokens)) {\n state.tokens = value.tokens as NonNullable<MCPOAuthState[\"tokens\"]>;\n }\n if (isRecord(value.client_info)) {\n state.client_info = value.client_info as NonNullable<\n MCPOAuthState[\"client_info\"]\n >;\n }\n if (\n typeof value.token_expires_at === \"number\" ||\n value.token_expires_at === null\n ) {\n state.token_expires_at = value.token_expires_at;\n }\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\nfunction normalizeTaggedAuthCredential(\n value: unknown,\n): MCPAuthCredential | undefined {\n if (!isRecord(value)) return undefined;\n switch (value.strategy) {\n case \"none\":\n return { strategy: \"none\" };\n case \"api_key\":\n if (typeof value.value !== \"string\") return undefined;\n return {\n strategy: \"api_key\",\n value: value.value,\n ...(typeof value.header_name === \"string\" && {\n header_name: value.header_name,\n }),\n };\n case \"bearer\":\n if (typeof value.value !== \"string\") return undefined;\n return { strategy: \"bearer\", value: value.value };\n case \"basic\":\n if (\n typeof value.username !== \"string\" ||\n typeof value.password !== \"string\"\n ) {\n return undefined;\n }\n return {\n strategy: \"basic\",\n username: value.username,\n password: value.password,\n };\n case \"header\": {\n const headers = stringRecord(value.headers);\n return headers ? { strategy: \"header\", headers } : undefined;\n }\n case \"oauth2\": {\n const authentication = getAuthenticationConfig(value.authentication);\n const state = getOAuthState(value.state);\n return {\n strategy: \"oauth2\",\n ...(authentication && { authentication }),\n ...(state && { state }),\n };\n }\n default:\n return undefined;\n }\n}\n\nfunction authCredentialFromServerConfig(\n serverConfig: Record<string, unknown>,\n): MCPAuthCredential | undefined {\n return normalizeTaggedAuthCredential(serverConfig.auth);\n}\n\nfunction getRemoteSecretFields(entry: {\n auth?: MCPAuthCredential;\n headers?: Record<string, string>;\n}): Partial<SdkMcpServerConfig> {\n const fields: Partial<SdkMcpServerConfig> = {};\n if (entry.auth && !hasRedactedMcpSecretLeaf(entry.auth)) {\n fields.auth = entry.auth;\n }\n if (entry.headers && Object.keys(entry.headers).length > 0) {\n fields.headers = entry.headers;\n }\n return fields;\n}\n\nfunction getRemoteServerFields(server: MCPServerConfig) {\n return {\n ...(server.name && { name: server.name }),\n url: server.url!,\n ...(server.headers && { headers: server.headers }),\n ...(server.auth && { auth: server.auth }),\n };\n}\n\nexport function toMcpSseServer(server: MCPServerConfig): MCPSSEServer {\n return getRemoteServerFields(server);\n}\n\nexport function toMcpShttpServer(server: MCPServerConfig): MCPSHTTPServer {\n return {\n ...getRemoteServerFields(server),\n ...(server.timeout !== undefined && { timeout: server.timeout }),\n };\n}\n\nexport function toMcpStdioServer(server: MCPServerConfig): MCPStdioServer {\n return {\n name: server.name!,\n command: server.command!,\n ...(server.args && { args: server.args }),\n ...(server.env && { env: server.env }),\n };\n}\n\nfunction getAuthenticationConfig(\n value: unknown,\n): MCPAuthenticationConfig | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const record = value as Record<string, unknown>;\n if (record.type !== \"oauth\") return undefined;\n const authentication: MCPAuthenticationConfig = { type: \"oauth\" };\n if (\n record.client_auth_method === \"none\" ||\n record.client_auth_method === \"client_secret_post\" ||\n record.client_auth_method === \"client_secret_basic\" ||\n record.client_auth_method === \"private_key_jwt\"\n ) {\n authentication.client_auth_method = record.client_auth_method;\n }\n if (typeof record.scopes === \"string\") {\n authentication.scopes = record.scopes;\n } else if (\n Array.isArray(record.scopes) &&\n record.scopes.every((scope) => typeof scope === \"string\")\n ) {\n authentication.scopes = record.scopes;\n }\n if (typeof record.client_name === \"string\") {\n authentication.client_name = record.client_name;\n }\n if (typeof record.client_metadata_url === \"string\") {\n authentication.client_metadata_url = record.client_metadata_url;\n }\n if (typeof record.client_id === \"string\") {\n authentication.client_id = record.client_id;\n }\n if (typeof record.client_secret === \"string\") {\n authentication.client_secret = record.client_secret;\n }\n if (\n record.additional_client_metadata &&\n typeof record.additional_client_metadata === \"object\" &&\n !Array.isArray(record.additional_client_metadata)\n ) {\n authentication.additional_client_metadata =\n record.additional_client_metadata as Record<string, MCPJsonValue>;\n }\n return authentication;\n}\n\n/**\n * Parse the SDK-native ``agent_settings.mcp_config`` value and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n const mcpConfig = getSdkMcpServerMap(value);\n if (!mcpConfig) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {\n if (!isRecord(serverConfig)) 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 = authCredentialFromServerConfig(serverConfig);\n const headers = stringRecord(serverConfig.headers);\n\n if (transport === \"sse\") {\n const name = userGivenServerName(serverName, \"sse\");\n const server: MCPSSEServer = { url };\n if (name) server.name = name;\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n sseServers.push(server);\n } else {\n const name = userGivenServerName(serverName, \"shttp\");\n const server: MCPSHTTPServer = { url };\n if (name) server.name = name;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n shttpServers.push(server);\n }\n } else {\n if (typeof serverConfig.command !== \"string\") continue;\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command,\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-native server map\n * 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 mcpConfig: SdkMcpConfig = {};\n\n const reserve = (base: string): string => {\n const safeBase = toMcpServerName(base);\n if (!(safeBase in mcpConfig)) return safeBase;\n let i = 1;\n while (`${safeBase}_${i}` in mcpConfig) i += 1;\n return `${safeBase}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry));\n }\n server.transport = \"sse\";\n mcpConfig[reserve(name || \"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = { transport: \"http\" };\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry));\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpConfig[reserve(name || \"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 mcpConfig[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpConfig).length > 0 ? mcpConfig : null;\n}\n"],"mappings":"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"}
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\";\nimport type {\n MCPAuthCredential,\n MCPAuthenticationConfig,\n MCPJsonValue,\n MCPOAuthState,\n} from \"#/types/mcp-auth\";\nimport type { MCPServerConfig } from \"#/types/mcp-server\";\nimport { toMcpServerName } from \"#/utils/mcp-server-name\";\n\nconst EMPTY_MCP_CONFIG: MCPConfig = {\n sse_servers: [],\n stdio_servers: [],\n shttp_servers: [],\n};\n\nexport const REDACTED_MCP_SECRET_VALUE = \"**********\";\n\ntype SdkMcpServerConfig = Record<string, SettingsValue | MCPAuthCredential>;\ntype SdkMcpConfig = Record<string, SdkMcpServerConfig>;\n\n/**\n * The dict key for an SSE/SHTTP server doubles as its display name once a\n * user provides one. Keys that match the auto-generated fallback pattern\n * (``sse``, ``shttp``, ``shttp_1``, …) emitted by {@link toSdkMcpConfig}\n * carry no user intent, so we surface them as \"no name\" — re-serializing\n * regenerates the identical fallback key and existing configs are left\n * untouched.\n */\nfunction userGivenServerName(\n serverName: string,\n base: \"sse\" | \"shttp\",\n): string | undefined {\n const autoGenerated = new RegExp(`^${base}(_\\\\d+)?$`);\n return autoGenerated.test(serverName) ? undefined : serverName;\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n return !!value && typeof value === \"object\" && !Array.isArray(value);\n}\n\nexport function getSdkMcpServerMap(\n value: unknown,\n): Record<string, unknown> | null {\n if (!isRecord(value)) return null;\n\n const mcpServers = value.mcpServers;\n if (\n isRecord(mcpServers) &&\n !(\"url\" in mcpServers) &&\n !(\"command\" in mcpServers)\n ) {\n return mcpServers;\n }\n\n return value;\n}\n\nexport function stringRecord(\n value: unknown,\n): Record<string, string> | undefined {\n if (!isRecord(value)) return undefined;\n const entries = Object.entries(value).filter(\n (entry): entry is [string, string] => typeof entry[1] === \"string\",\n );\n return entries.length > 0 ? Object.fromEntries(entries) : undefined;\n}\n\nexport function hasRedactedMcpSecretLeaf(value: unknown): boolean {\n if (value === REDACTED_MCP_SECRET_VALUE) return true;\n if (Array.isArray(value)) return value.some(hasRedactedMcpSecretLeaf);\n if (isRecord(value))\n return Object.values(value).some(hasRedactedMcpSecretLeaf);\n return false;\n}\n\nfunction getOAuthState(value: unknown): MCPOAuthState | undefined {\n if (!isRecord(value)) return undefined;\n const state: MCPOAuthState = {};\n if (isRecord(value.tokens)) {\n state.tokens = value.tokens as NonNullable<MCPOAuthState[\"tokens\"]>;\n }\n if (isRecord(value.client_info)) {\n state.client_info = value.client_info as NonNullable<\n MCPOAuthState[\"client_info\"]\n >;\n }\n if (\n typeof value.token_expires_at === \"number\" ||\n value.token_expires_at === null\n ) {\n state.token_expires_at = value.token_expires_at;\n }\n return Object.keys(state).length > 0 ? state : undefined;\n}\n\nfunction normalizeTaggedAuthCredential(\n value: unknown,\n): MCPAuthCredential | undefined {\n if (!isRecord(value)) return undefined;\n switch (value.strategy) {\n case \"none\":\n return { strategy: \"none\" };\n case \"api_key\":\n if (typeof value.value !== \"string\") return undefined;\n return {\n strategy: \"api_key\",\n value: value.value,\n ...(typeof value.header_name === \"string\" && {\n header_name: value.header_name,\n }),\n };\n case \"bearer\":\n if (typeof value.value !== \"string\") return undefined;\n return { strategy: \"bearer\", value: value.value };\n case \"basic\":\n if (\n typeof value.username !== \"string\" ||\n typeof value.password !== \"string\"\n ) {\n return undefined;\n }\n return {\n strategy: \"basic\",\n username: value.username,\n password: value.password,\n };\n case \"header\": {\n const headers = stringRecord(value.headers);\n return headers ? { strategy: \"header\", headers } : undefined;\n }\n case \"oauth2\": {\n const authentication = getAuthenticationConfig(value.authentication);\n const state = getOAuthState(value.state);\n return {\n strategy: \"oauth2\",\n ...(authentication && { authentication }),\n ...(state && { state }),\n };\n }\n default:\n return undefined;\n }\n}\n\nfunction authCredentialFromServerConfig(\n serverConfig: Record<string, unknown>,\n): MCPAuthCredential | undefined {\n return normalizeTaggedAuthCredential(serverConfig.auth);\n}\n\nfunction getRemoteSecretFields(entry: {\n auth?: MCPAuthCredential;\n headers?: Record<string, string>;\n}): Partial<SdkMcpServerConfig> {\n const fields: Partial<SdkMcpServerConfig> = {};\n if (entry.auth && !hasRedactedMcpSecretLeaf(entry.auth)) {\n fields.auth = entry.auth;\n }\n if (entry.headers && Object.keys(entry.headers).length > 0) {\n fields.headers = entry.headers;\n }\n return fields;\n}\n\n/**\n * Only an explicit ``false`` is carried. Every mutation round-trips the whole\n * server map through these converters, so preserving the flag here is what\n * stops adding or deleting one server from silently re-enabling another.\n */\nfunction disabledField(server: { enabled?: boolean }) {\n return server.enabled === false ? { enabled: false as const } : {};\n}\n\nfunction getRemoteServerFields(server: MCPServerConfig) {\n return {\n ...(server.name && { name: server.name }),\n url: server.url!,\n ...(server.headers && { headers: server.headers }),\n ...(server.auth && { auth: server.auth }),\n ...disabledField(server),\n };\n}\n\nexport function toMcpSseServer(server: MCPServerConfig): MCPSSEServer {\n return getRemoteServerFields(server);\n}\n\nexport function toMcpShttpServer(server: MCPServerConfig): MCPSHTTPServer {\n return {\n ...getRemoteServerFields(server),\n ...(server.timeout !== undefined && { timeout: server.timeout }),\n };\n}\n\nexport function toMcpStdioServer(server: MCPServerConfig): MCPStdioServer {\n return {\n name: server.name!,\n command: server.command!,\n ...(server.args && { args: server.args }),\n ...(server.env && { env: server.env }),\n ...disabledField(server),\n };\n}\n\nfunction getAuthenticationConfig(\n value: unknown,\n): MCPAuthenticationConfig | undefined {\n if (!value || typeof value !== \"object\") return undefined;\n const record = value as Record<string, unknown>;\n if (record.type !== \"oauth\") return undefined;\n const authentication: MCPAuthenticationConfig = { type: \"oauth\" };\n if (\n record.client_auth_method === \"none\" ||\n record.client_auth_method === \"client_secret_post\" ||\n record.client_auth_method === \"client_secret_basic\" ||\n record.client_auth_method === \"private_key_jwt\"\n ) {\n authentication.client_auth_method = record.client_auth_method;\n }\n if (typeof record.scopes === \"string\") {\n authentication.scopes = record.scopes;\n } else if (\n Array.isArray(record.scopes) &&\n record.scopes.every((scope) => typeof scope === \"string\")\n ) {\n authentication.scopes = record.scopes;\n }\n if (typeof record.client_name === \"string\") {\n authentication.client_name = record.client_name;\n }\n if (typeof record.client_metadata_url === \"string\") {\n authentication.client_metadata_url = record.client_metadata_url;\n }\n if (typeof record.client_id === \"string\") {\n authentication.client_id = record.client_id;\n }\n if (typeof record.client_secret === \"string\") {\n authentication.client_secret = record.client_secret;\n }\n if (\n record.additional_client_metadata &&\n typeof record.additional_client_metadata === \"object\" &&\n !Array.isArray(record.additional_client_metadata)\n ) {\n authentication.additional_client_metadata =\n record.additional_client_metadata as Record<string, MCPJsonValue>;\n }\n return authentication;\n}\n\n/**\n * Parse the SDK-native ``agent_settings.mcp_config`` value and convert it\n * to the frontend MCPConfig format used by UI components.\n */\nexport function parseMcpConfig(value: unknown): MCPConfig {\n const mcpConfig = getSdkMcpServerMap(value);\n if (!mcpConfig) {\n return { ...EMPTY_MCP_CONFIG };\n }\n\n const sseServers: (string | MCPSSEServer)[] = [];\n const stdioServers: MCPStdioServer[] = [];\n const shttpServers: (string | MCPSHTTPServer)[] = [];\n\n for (const [serverName, serverConfig] of Object.entries(mcpConfig)) {\n if (!isRecord(serverConfig)) continue;\n\n const url = serverConfig.url as string | undefined;\n\n // The SDK omits ``enabled`` while it holds its default (true), so only an\n // explicit ``false`` marks a server as switched off.\n const disabled = serverConfig.enabled === false;\n\n if (url) {\n const transport = serverConfig.transport as string | undefined;\n const auth = authCredentialFromServerConfig(serverConfig);\n const headers = stringRecord(serverConfig.headers);\n\n if (transport === \"sse\") {\n const name = userGivenServerName(serverName, \"sse\");\n const server: MCPSSEServer = { url };\n if (name) server.name = name;\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n if (disabled) server.enabled = false;\n sseServers.push(server);\n } else {\n const name = userGivenServerName(serverName, \"shttp\");\n const server: MCPSHTTPServer = { url };\n if (name) server.name = name;\n if (serverConfig.timeout != null) {\n server.timeout = serverConfig.timeout as number;\n }\n if (auth) server.auth = auth;\n if (headers) server.headers = headers;\n if (disabled) server.enabled = false;\n shttpServers.push(server);\n }\n } else {\n if (typeof serverConfig.command !== \"string\") continue;\n const stdioServer: MCPStdioServer = {\n name: serverName,\n command: serverConfig.command,\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 if (disabled) stdioServer.enabled = false;\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-native server map\n * 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 *\n * ``enabled`` is only emitted when a server is switched off — the SDK defaults\n * it to true. Omitting it is what *clears* a previously persisted\n * ``enabled: false``, and that only works because ``saveSettings`` pre-clears\n * ``mcp_config`` to null before every write (see\n * ``settings-service.api.ts``), turning the backend's RFC 7386 merge into a\n * full replace. If that pre-clear is ever removed, re-enabling a server\n * becomes a silent no-op.\n */\nexport function toSdkMcpConfig(config: MCPConfig): SdkMcpConfig | null {\n const mcpConfig: SdkMcpConfig = {};\n\n const reserve = (base: string): string => {\n const safeBase = toMcpServerName(base);\n if (!(safeBase in mcpConfig)) return safeBase;\n let i = 1;\n while (`${safeBase}_${i}` in mcpConfig) i += 1;\n return `${safeBase}_${i}`;\n };\n\n for (const entry of config.sse_servers) {\n const server: SdkMcpServerConfig = {};\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry), disabledField(entry));\n }\n server.transport = \"sse\";\n mcpConfig[reserve(name || \"sse\")] = server;\n }\n\n for (const entry of config.shttp_servers) {\n const server: SdkMcpServerConfig = { transport: \"http\" };\n let name: string | undefined;\n if (typeof entry === \"string\") {\n server.url = entry;\n } else {\n name = entry.name;\n server.url = entry.url;\n Object.assign(server, getRemoteSecretFields(entry), disabledField(entry));\n if (entry.timeout != null) server.timeout = entry.timeout;\n }\n mcpConfig[reserve(name || \"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 Object.assign(server, disabledField(entry));\n mcpConfig[reserve(entry.name || \"stdio\")] = server;\n }\n\n return Object.keys(mcpConfig).length > 0 ? mcpConfig : null;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");function e(e){return[...e.sse_servers.map((e,t)=>({id:`sse-${t}`,type:`sse`,name:typeof e==`object`?e.name:void 0,url:typeof e==`string`?e:e.url,headers:typeof e==`object`?e.headers:void 0,auth:typeof e==`object`?e.auth:void 0})),...e.stdio_servers.map((e,t)=>({id:`stdio-${t}`,type:`stdio`,name:e.name,command:e.command,args:e.args,env:e.env})),...e.shttp_servers.map((e,t)=>({id:`shttp-${t}`,type:`shttp`,name:typeof e==`object`?e.name:void 0,url:typeof e==`string`?e:e.url,headers:typeof e==`object`?e.headers:void 0,timeout:typeof e==`object`?e.timeout:void 0,auth:typeof e==`object`?e.auth:void 0}))]}exports.flattenMcpConfig=e;
1
+ require("../_virtual/_rolldown/runtime.cjs");function e(e){return[...e.sse_servers.map((e,t)=>({id:`sse-${t}`,type:`sse`,name:typeof e==`object`?e.name:void 0,url:typeof e==`string`?e:e.url,headers:typeof e==`object`?e.headers:void 0,auth:typeof e==`object`?e.auth:void 0,enabled:typeof e==`object`?e.enabled:void 0})),...e.stdio_servers.map((e,t)=>({id:`stdio-${t}`,type:`stdio`,name:e.name,command:e.command,args:e.args,env:e.env,enabled:e.enabled})),...e.shttp_servers.map((e,t)=>({id:`shttp-${t}`,type:`shttp`,name:typeof e==`object`?e.name:void 0,url:typeof e==`string`?e:e.url,headers:typeof e==`object`?e.headers:void 0,timeout:typeof e==`object`?e.timeout:void 0,auth:typeof e==`object`?e.auth:void 0,enabled:typeof e==`object`?e.enabled:void 0}))]}exports.flattenMcpConfig=e;
2
2
  //# sourceMappingURL=mcp-installed-servers.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-installed-servers.cjs","names":[],"sources":["../../src/utils/mcp-installed-servers.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport { MCPConfig } from \"#/types/settings\";\n\nexport function flattenMcpConfig(config: MCPConfig): MCPServerConfig[] {\n return [\n ...config.sse_servers.map((server, index) => ({\n id: `sse-${index}`,\n type: \"sse\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n })),\n ...config.stdio_servers.map((server, index) => ({\n id: `stdio-${index}`,\n type: \"stdio\" as const,\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env,\n })),\n ...config.shttp_servers.map((server, index) => ({\n id: `shttp-${index}`,\n type: \"shttp\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n timeout: typeof server === \"object\" ? server.timeout : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n })),\n ];\n}\n"],"mappings":"6CAGA,SAAgB,EAAiB,EAAsC,CACrE,MAAO,CACL,GAAG,EAAO,YAAY,KAAK,EAAQ,KAAW,CAC5C,GAAI,OAAO,IACX,KAAM,MACN,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,IAAK,OAAO,GAAW,SAAW,EAAS,EAAO,IAClD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,EACnD,EAAE,EACF,GAAG,EAAO,cAAc,KAAK,EAAQ,KAAW,CAC9C,GAAI,SAAS,IACb,KAAM,QACN,KAAM,EAAO,KACb,QAAS,EAAO,QAChB,KAAM,EAAO,KACb,IAAK,EAAO,GACd,EAAE,EACF,GAAG,EAAO,cAAc,KAAK,EAAQ,KAAW,CAC9C,GAAI,SAAS,IACb,KAAM,QACN,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,IAAK,OAAO,GAAW,SAAW,EAAS,EAAO,IAClD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,EACnD,EAAE,CACJ,CACF"}
1
+ {"version":3,"file":"mcp-installed-servers.cjs","names":[],"sources":["../../src/utils/mcp-installed-servers.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport { MCPConfig } from \"#/types/settings\";\n\nexport function flattenMcpConfig(config: MCPConfig): MCPServerConfig[] {\n return [\n ...config.sse_servers.map((server, index) => ({\n id: `sse-${index}`,\n type: \"sse\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n enabled: typeof server === \"object\" ? server.enabled : undefined,\n })),\n ...config.stdio_servers.map((server, index) => ({\n id: `stdio-${index}`,\n type: \"stdio\" as const,\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env,\n enabled: server.enabled,\n })),\n ...config.shttp_servers.map((server, index) => ({\n id: `shttp-${index}`,\n type: \"shttp\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n timeout: typeof server === \"object\" ? server.timeout : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n enabled: typeof server === \"object\" ? server.enabled : undefined,\n })),\n ];\n}\n"],"mappings":"6CAGA,SAAgB,EAAiB,EAAsC,CACrE,MAAO,CACL,GAAG,EAAO,YAAY,KAAK,EAAQ,KAAW,CAC5C,GAAI,OAAO,IACX,KAAM,MACN,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,IAAK,OAAO,GAAW,SAAW,EAAS,EAAO,IAClD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,EACzD,EAAE,EACF,GAAG,EAAO,cAAc,KAAK,EAAQ,KAAW,CAC9C,GAAI,SAAS,IACb,KAAM,QACN,KAAM,EAAO,KACb,QAAS,EAAO,QAChB,KAAM,EAAO,KACb,IAAK,EAAO,IACZ,QAAS,EAAO,OAClB,EAAE,EACF,GAAG,EAAO,cAAc,KAAK,EAAQ,KAAW,CAC9C,GAAI,SAAS,IACb,KAAM,QACN,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,IAAK,OAAO,GAAW,SAAW,EAAS,EAAO,IAClD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,GACvD,KAAM,OAAO,GAAW,SAAW,EAAO,KAAO,IAAA,GACjD,QAAS,OAAO,GAAW,SAAW,EAAO,QAAU,IAAA,EACzD,EAAE,CACJ,CACF"}
@@ -7,7 +7,8 @@ function e(e) {
7
7
  name: typeof e == "object" ? e.name : void 0,
8
8
  url: typeof e == "string" ? e : e.url,
9
9
  headers: typeof e == "object" ? e.headers : void 0,
10
- auth: typeof e == "object" ? e.auth : void 0
10
+ auth: typeof e == "object" ? e.auth : void 0,
11
+ enabled: typeof e == "object" ? e.enabled : void 0
11
12
  })),
12
13
  ...e.stdio_servers.map((e, t) => ({
13
14
  id: `stdio-${t}`,
@@ -15,7 +16,8 @@ function e(e) {
15
16
  name: e.name,
16
17
  command: e.command,
17
18
  args: e.args,
18
- env: e.env
19
+ env: e.env,
20
+ enabled: e.enabled
19
21
  })),
20
22
  ...e.shttp_servers.map((e, t) => ({
21
23
  id: `shttp-${t}`,
@@ -24,7 +26,8 @@ function e(e) {
24
26
  url: typeof e == "string" ? e : e.url,
25
27
  headers: typeof e == "object" ? e.headers : void 0,
26
28
  timeout: typeof e == "object" ? e.timeout : void 0,
27
- auth: typeof e == "object" ? e.auth : void 0
29
+ auth: typeof e == "object" ? e.auth : void 0,
30
+ enabled: typeof e == "object" ? e.enabled : void 0
28
31
  }))
29
32
  ];
30
33
  }
@@ -1 +1 @@
1
- {"version":3,"file":"mcp-installed-servers.js","names":[],"sources":["../../src/utils/mcp-installed-servers.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport { MCPConfig } from \"#/types/settings\";\n\nexport function flattenMcpConfig(config: MCPConfig): MCPServerConfig[] {\n return [\n ...config.sse_servers.map((server, index) => ({\n id: `sse-${index}`,\n type: \"sse\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n })),\n ...config.stdio_servers.map((server, index) => ({\n id: `stdio-${index}`,\n type: \"stdio\" as const,\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env,\n })),\n ...config.shttp_servers.map((server, index) => ({\n id: `shttp-${index}`,\n type: \"shttp\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n timeout: typeof server === \"object\" ? server.timeout : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n })),\n ];\n}\n"],"mappings":";AAGA,SAAgB,EAAiB,GAAsC;CACrE,OAAO;EACL,GAAG,EAAO,YAAY,KAAK,GAAQ,OAAW;GAC5C,IAAI,OAAO;GACX,MAAM;GACN,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,KAAK,OAAO,KAAW,WAAW,IAAS,EAAO;GAClD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;EACnD,EAAE;EACF,GAAG,EAAO,cAAc,KAAK,GAAQ,OAAW;GAC9C,IAAI,SAAS;GACb,MAAM;GACN,MAAM,EAAO;GACb,SAAS,EAAO;GAChB,MAAM,EAAO;GACb,KAAK,EAAO;EACd,EAAE;EACF,GAAG,EAAO,cAAc,KAAK,GAAQ,OAAW;GAC9C,IAAI,SAAS;GACb,MAAM;GACN,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,KAAK,OAAO,KAAW,WAAW,IAAS,EAAO;GAClD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;EACnD,EAAE;CACJ;AACF"}
1
+ {"version":3,"file":"mcp-installed-servers.js","names":[],"sources":["../../src/utils/mcp-installed-servers.ts"],"sourcesContent":["import { MCPServerConfig } from \"#/types/mcp-server\";\nimport { MCPConfig } from \"#/types/settings\";\n\nexport function flattenMcpConfig(config: MCPConfig): MCPServerConfig[] {\n return [\n ...config.sse_servers.map((server, index) => ({\n id: `sse-${index}`,\n type: \"sse\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n enabled: typeof server === \"object\" ? server.enabled : undefined,\n })),\n ...config.stdio_servers.map((server, index) => ({\n id: `stdio-${index}`,\n type: \"stdio\" as const,\n name: server.name,\n command: server.command,\n args: server.args,\n env: server.env,\n enabled: server.enabled,\n })),\n ...config.shttp_servers.map((server, index) => ({\n id: `shttp-${index}`,\n type: \"shttp\" as const,\n name: typeof server === \"object\" ? server.name : undefined,\n url: typeof server === \"string\" ? server : server.url,\n headers: typeof server === \"object\" ? server.headers : undefined,\n timeout: typeof server === \"object\" ? server.timeout : undefined,\n auth: typeof server === \"object\" ? server.auth : undefined,\n enabled: typeof server === \"object\" ? server.enabled : undefined,\n })),\n ];\n}\n"],"mappings":";AAGA,SAAgB,EAAiB,GAAsC;CACrE,OAAO;EACL,GAAG,EAAO,YAAY,KAAK,GAAQ,OAAW;GAC5C,IAAI,OAAO;GACX,MAAM;GACN,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,KAAK,OAAO,KAAW,WAAW,IAAS,EAAO;GAClD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;EACzD,EAAE;EACF,GAAG,EAAO,cAAc,KAAK,GAAQ,OAAW;GAC9C,IAAI,SAAS;GACb,MAAM;GACN,MAAM,EAAO;GACb,SAAS,EAAO;GAChB,MAAM,EAAO;GACb,KAAK,EAAO;GACZ,SAAS,EAAO;EAClB,EAAE;EACF,GAAG,EAAO,cAAc,KAAK,GAAQ,OAAW;GAC9C,IAAI,SAAS;GACb,MAAM;GACN,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,KAAK,OAAO,KAAW,WAAW,IAAS,EAAO;GAClD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;GACvD,MAAM,OAAO,KAAW,WAAW,EAAO,OAAO,KAAA;GACjD,SAAS,OAAO,KAAW,WAAW,EAAO,UAAU,KAAA;EACzD,EAAE;CACJ;AACF"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/agent-canvas",
3
- "version": "1.7.1",
3
+ "version": "1.8.0",
4
4
  "description": "Agent Canvas UI for OpenHands - run AI coding agents with a visual interface",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -24,7 +24,7 @@
24
24
  "@microlink/react-json-view": "1.31.20",
25
25
  "@monaco-editor/react": "4.7.0",
26
26
  "@openhands/extensions": "0.11.0",
27
- "@openhands/typescript-client": "1.34.0",
27
+ "@openhands/typescript-client": "1.36.1",
28
28
  "@react-router/node": "7.17.0",
29
29
  "@react-router/serve": "7.17.0",
30
30
  "@tailwindcss/vite": "4.2.4",
@@ -87,6 +87,9 @@
87
87
  "test:e2e:mock-llm": "playwright test --config=playwright.mock-llm.config.ts",
88
88
  "test:e2e:mock-llm:docker": "playwright test --config=playwright.mock-llm-docker.config.ts",
89
89
  "test:coverage": "npm run make-i18n && vitest run --coverage",
90
+ "test:mutation": "npm run make-i18n && stryker run",
91
+ "test:mutation:diff": "npm run make-i18n && node scripts/stryker-diff.mjs",
92
+ "test:mutation:incremental": "npm run make-i18n && stryker run --incremental",
90
93
  "dev_wsl": "VITE_WATCH_USE_POLLING=true vite",
91
94
  "preview": "vite preview",
92
95
  "make-i18n": "node scripts/make-i18n-translations.cjs",
@@ -125,6 +128,8 @@
125
128
  "@mswjs/socket.io-binding": "0.2.0",
126
129
  "@playwright/test": "1.59.1",
127
130
  "@react-router/dev": "7.17.0",
131
+ "@stryker-mutator/core": "9.6.1",
132
+ "@stryker-mutator/vitest-runner": "9.6.1",
128
133
  "@tailwindcss/typography": "0.5.19",
129
134
  "@tanstack/eslint-plugin-query": "5.100.9",
130
135
  "@testing-library/dom": "10.4.1",
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * Usage:
21
21
  * node scripts/check-sdk-version-sync.mjs
22
- * EXPECTED_SDK_VERSION=1.38.0 node scripts/check-sdk-version-sync.mjs
22
+ * EXPECTED_SDK_VERSION=1.39.1 node scripts/check-sdk-version-sync.mjs
23
23
  * node scripts/check-sdk-version-sync.mjs --check-pypi
24
24
  *
25
25
  * Environment variables:
@@ -78,7 +78,7 @@ Triggering from other repos:
78
78
  -H "Authorization: token \$GITHUB_TOKEN" \\
79
79
  -H "Accept: application/vnd.github.v3+json" \\
80
80
  https://api.github.com/repos/OpenHands/OpenHands/dispatches \\
81
- -d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.38.0"}}'
81
+ -d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.39.1"}}'
82
82
  `);
83
83
  process.exit(0);
84
84
  }
@@ -260,9 +260,9 @@ async function fetchPyPIDependencies(packageName, version) {
260
260
  * Parse PyPI requires_dist array and extract SDK package versions
261
261
  *
262
262
  * PyPI returns dependencies in PEP 508 format like:
263
- * "openhands-sdk>=1.38.0,<2.0.0"
264
- * "openhands-tools==1.38.0"
265
- * "openhands-workspace (>=1.38.0)"
263
+ * "openhands-sdk>=1.39.1,<2.0.0"
264
+ * "openhands-tools==1.39.1"
265
+ * "openhands-workspace (>=1.39.1)"
266
266
  */
267
267
  function parseSdkVersionsFromRequiresDist(requiresDist) {
268
268
  const versions = {};
@@ -276,7 +276,7 @@ function parseSdkVersionsFromRequiresDist(requiresDist) {
276
276
  }
277
277
 
278
278
  // Extract the version number - look for patterns like:
279
- // ">=1.38.0", "==1.38.0", "(>=1.38.0)", "~=1.38.0"
279
+ // ">=1.39.1", "==1.39.1", "(>=1.39.1)", "~=1.39.1"
280
280
  // After the package name and before any comma or closing paren
281
281
  const versionPattern = /[><=~!]+\s*([0-9]+(?:\.[0-9]+)*)/;
282
282
  const match = dep.match(versionPattern);
@@ -48,7 +48,7 @@ const LOCAL_AGENT_SERVER_SUBDIRS = [
48
48
  "openhands-workspace",
49
49
  ];
50
50
  const DEFAULT_AGENT_SERVER_VERSION = SHARED_DEFAULTS.versions.agentServer;
51
- // Temporary transitive-dep pin: openhands-sdk 1.38.0 leaves agent-client-protocol
51
+ // Temporary transitive-dep pin: openhands-sdk 1.39.1 leaves agent-client-protocol
52
52
  // unbounded (>=0.10.1), but acp 0.11.0 reordered the ACP prompt() args and breaks
53
53
  // the SDK's ACP client. Hold acp <0.11 until a fixed SDK ships. See config/defaults.json.
54
54
  const AGENT_CLIENT_PROTOCOL_CONSTRAINT =
@@ -399,7 +399,7 @@ export function validateFrontendDependencies(
399
399
  * edits are picked up without a manual reinstall. The agent-server itself
400
400
  * is rebuilt from local source on each invocation (--reinstall).
401
401
  * - OH_AGENT_SERVER_GIT_REF: Git commit SHA or branch name
402
- * - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.38.0")
402
+ * - OH_AGENT_SERVER_VERSION: Specific PyPI version (e.g., "1.39.1")
403
403
  *
404
404
  * If none are set, defaults to the released version specified by
405
405
  * DEFAULT_AGENT_SERVER_VERSION. Set OH_AGENT_SERVER_GIT_REF to use a
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { spawnSync } from "node:child_process";
4
+ import process from "node:process";
5
+ import { fileURLToPath, pathToFileURL } from "node:url";
6
+
7
+ const EXCLUDED_DIRECTORY =
8
+ /(^|\/)(__tests__|tests?|fixtures|__fixtures__|snapshots|__snapshots__|generated|mocks|dev)(\/|$)/;
9
+ const EXCLUDED_FILE = /\.(test|spec|d|gen|generated)\.(ts|tsx)$/;
10
+
11
+ function isProductionTypeScript(file) {
12
+ const normalized = file.replaceAll("\\", "/");
13
+ return (
14
+ /^src\/.+\.(ts|tsx)$/.test(normalized) &&
15
+ !EXCLUDED_DIRECTORY.test(normalized) &&
16
+ !EXCLUDED_FILE.test(normalized)
17
+ );
18
+ }
19
+
20
+ /**
21
+ * @typedef {object} ProcessResult
22
+ * @property {Error | undefined} [error]
23
+ * @property {number | null} status
24
+ * @property {string | null} stderr
25
+ * @property {string | null} stdout
26
+ */
27
+
28
+ /**
29
+ * @typedef {object} Dependencies
30
+ * @property {(command: string, args: readonly string[], options?: import("node:child_process").SpawnSyncOptions) => ProcessResult} spawn
31
+ * @property {(message: string) => void} writeError
32
+ * @property {(message: string) => void} writeOutput
33
+ */
34
+
35
+ // Stryker disable all: the real process adapter is verified by the CLI
36
+ // subprocess smoke test, which Vitest cannot attribute to the parent process.
37
+ /** @type {Dependencies} */
38
+ const defaultDependencies = {
39
+ spawn(command, args, options) {
40
+ const result = spawnSync(command, [...args], options);
41
+ return {
42
+ error: result.error,
43
+ status: result.status,
44
+ stderr: result.stderr?.toString() ?? null,
45
+ stdout: result.stdout?.toString() ?? null,
46
+ };
47
+ },
48
+ writeError(message) {
49
+ process.stderr.write(message);
50
+ },
51
+ writeOutput(message) {
52
+ process.stdout.write(message);
53
+ },
54
+ };
55
+ // Stryker restore all
56
+
57
+ /**
58
+ * Run mutation testing against production TypeScript files changed from a base
59
+ * git ref. The dependency parameter keeps the process boundary observable in
60
+ * tests while the command-line behavior remains the public API.
61
+ *
62
+ * @param {string[]} argv
63
+ * @param {Partial<Dependencies>} [overrides]
64
+ * @returns {number}
65
+ */
66
+ export function main(argv, overrides = {}) {
67
+ const dependencies = { ...defaultDependencies, ...overrides };
68
+ const baseRef = argv[0] ?? "main";
69
+ const gitArgs = [
70
+ "diff",
71
+ "--name-only",
72
+ "--diff-filter=ACMRTUXB",
73
+ `${baseRef}...HEAD`,
74
+ ];
75
+ const gitResult = dependencies.spawn("git", gitArgs, {
76
+ encoding: "utf8",
77
+ windowsHide: true,
78
+ });
79
+
80
+ if (gitResult.error || gitResult.status !== 0) {
81
+ const detail =
82
+ gitResult.stderr || gitResult.error?.message || "unknown error";
83
+ dependencies.writeError(
84
+ `Unable to determine changed files against ${baseRef}: ${detail.trim()}\n`,
85
+ );
86
+ return gitResult.status ?? 1;
87
+ }
88
+
89
+ const mutationTargets = gitResult.stdout
90
+ .split(/\r?\n/u)
91
+ .filter(isProductionTypeScript);
92
+
93
+ if (mutationTargets.length === 0) {
94
+ dependencies.writeOutput(
95
+ `No changed production files to mutate against ${baseRef}.\n`,
96
+ );
97
+ return 0;
98
+ }
99
+
100
+ const strykerCliPath = fileURLToPath(
101
+ new URL(
102
+ "../node_modules/@stryker-mutator/core/bin/stryker.js",
103
+ import.meta.url,
104
+ ),
105
+ );
106
+ const strykerResult = dependencies.spawn(
107
+ process.execPath,
108
+ [
109
+ strykerCliPath,
110
+ "run",
111
+ "--incremental",
112
+ "--force",
113
+ "--mutate",
114
+ mutationTargets.join(","),
115
+ ],
116
+ { stdio: "inherit", windowsHide: true },
117
+ );
118
+
119
+ if (strykerResult.error) {
120
+ dependencies.writeError(
121
+ `Unable to start Stryker: ${strykerResult.error.message}\n`,
122
+ );
123
+ return 1;
124
+ }
125
+
126
+ return strykerResult.status ?? 1;
127
+ }
128
+
129
+ // Stryker disable all: the CLI guard is verified by a real subprocess test,
130
+ // which the Vitest runner cannot attribute to the mutated parent process.
131
+ const isMainModule =
132
+ process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
133
+
134
+ if (isMainModule) {
135
+ process.exitCode = main(process.argv.slice(2));
136
+ }
137
+ // Stryker restore all