@openhands/agent-canvas 1.13.0 → 1.14.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 (437) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{acp-providers-D1PORKc0.js → acp-providers-CYy8qA2z.js} +1 -1
  4. package/build/assets/active-backend-context-BZDG15C0.js +2 -0
  5. package/build/assets/{agent-context-settings-DMrObcIS.js → agent-context-settings-DGWBBbcp.js} +1 -1
  6. package/build/assets/{agent-profiles-settings-DororXN5.js → agent-profiles-settings-DrkGdf4_.js} +1 -1
  7. package/build/assets/{agent-server-conversation-service.api-DWBe0rbX.js → agent-server-conversation-service.api-DK56YPTs.js} +7 -7
  8. package/build/assets/{agent-settings-BJVk92PU.js → agent-settings-DfcSgkLD.js} +2 -2
  9. package/build/assets/agent-settings-Lrc5-twS.js +1 -0
  10. package/build/assets/{alert-banner-CzZsm7ms.js → alert-banner-BgDHhL7b.js} +1 -1
  11. package/build/assets/api-error-message-BEIE_yD1.js +1 -0
  12. package/build/assets/{api-key-entry-screen-D8Fade_-.js → api-key-entry-screen-DaZnOlVo.js} +1 -1
  13. package/build/assets/{app-settings-CpyGB3AT.js → app-settings-BG5NAUbC.js} +1 -1
  14. package/build/assets/automation-detail-D4Hs831Z.js +3 -0
  15. package/build/assets/automation-export-n0X6FB0C.js +2 -0
  16. package/build/assets/automation-git-sync-CDrm8WV7.js +1 -0
  17. package/build/assets/automation-setup-route-CZCl4ONs.js +1 -0
  18. package/build/assets/{automation-templates-DgpCGOJV.js → automation-templates-DFF96AdL.js} +1 -1
  19. package/build/assets/automations-list-Bl-oGOrV.js +1 -0
  20. package/build/assets/{back-nav-button-HHEneWMw.js → back-nav-button-H_ThtDA0.js} +1 -1
  21. package/build/assets/{backend-form-modal-DiVrD4Cm.js → backend-form-modal-CbCemvHt.js} +1 -1
  22. package/build/assets/backend-not-configured-DP8KLNq_.js +1 -0
  23. package/build/assets/{backend-synced-settings-badge-7MIjpr8M.js → backend-synced-settings-badge-BBASWqsb.js} +1 -1
  24. package/build/assets/{badge-CpYmcVZV.js → badge-B-51h-SW.js} +1 -1
  25. package/build/assets/{base-modal-Fnh6Uln9.js → base-modal-BbjycA8X.js} +1 -1
  26. package/build/assets/{bot-BQZx8e2i.js → bot-CGPCg03p.js} +1 -1
  27. package/build/assets/{brand-button-swT1RyO0.js → brand-button-Blxv5nww.js} +1 -1
  28. package/build/assets/browser-CuW0nWvh.js +7 -0
  29. package/build/assets/{browser-tab-zAY12m_i.js → browser-tab-D5G0ORwt.js} +1 -1
  30. package/build/assets/{chat-send-button-DsJtivmL.js → chat-send-button-BD2tii1r.js} +1 -1
  31. package/build/assets/check-DSuS-lyr.js +1 -0
  32. package/build/assets/chevron-down-CgGcmtE2.js +1 -0
  33. package/build/assets/circle-check-CG5-NPw7.js +1 -0
  34. package/build/assets/{circle-plus-check-toggle-Cuzx55bo.js → circle-plus-check-toggle-ASbpB8vN.js} +1 -1
  35. package/build/assets/{cloud-funnel-analytics-86CwNdSh.js → cloud-funnel-analytics-GlCUG0gk.js} +1 -1
  36. package/build/assets/cog-CZiR80uT.js +1 -0
  37. package/build/assets/{combobox-caret-D0fRotFi.js → combobox-caret-DvpEsh2O.js} +1 -1
  38. package/build/assets/{command-menu-BXy03FBl.js → command-menu-DwUQzjYP.js} +1 -1
  39. package/build/assets/{condenser-settings-BmZa0ui_.js → condenser-settings-BptcoiLA.js} +1 -1
  40. package/build/assets/{confirmation-modal-BgoN6lbN.js → confirmation-modal-CiEwxZAA.js} +1 -1
  41. package/build/assets/{context-menu-list-item-Bq02tc-P.js → context-menu-list-item-CdcamfM1.js} +1 -1
  42. package/build/assets/conversation-DREHj4nT.js +1 -0
  43. package/build/assets/conversation-DotRfiDz.js +90 -0
  44. package/build/assets/conversation-panel-DREHj4nT.js +1 -0
  45. package/build/assets/conversation-state-store-DMLtlHbE.js +1 -0
  46. package/build/assets/{conversation-tab-empty-state-CMqo_0Bd.js → conversation-tab-empty-state-D-puECpY.js} +1 -1
  47. package/build/assets/{copy-to-clipboard-button-Cg1pF0xn.js → copy-to-clipboard-button-BGxcUbgy.js} +1 -1
  48. package/build/assets/{device-verify-BfQLgNyq.js → device-verify-CZJfWaJf.js} +1 -1
  49. package/build/assets/{dropdown-classes-BIqQr5mx.js → dropdown-classes-tLhpwtxS.js} +1 -1
  50. package/build/assets/{entry.client-CxFjRYqH.js → entry.client-D8GnI6GZ.js} +2 -2
  51. package/build/assets/{enum-filter-dropdown-C_yV4_bo.js → enum-filter-dropdown-Fk8k60nk.js} +1 -1
  52. package/build/assets/{environment-switch-overlay-Bi4qbh2g.js → environment-switch-overlay-DeztVYuL.js} +1 -1
  53. package/build/assets/{extensions-hub-BqUINAhp.js → extensions-hub-Cd6GCYzq.js} +1 -1
  54. package/build/assets/{extensions-navigation-lraj88Wk.js → extensions-navigation-C4sgZg-y.js} +1 -1
  55. package/build/assets/{external-link-CgtpA_a1.js → external-link-C0UpBgsE.js} +1 -1
  56. package/build/assets/{file-text-DEK4LSVw.js → file-text-C6WYtNdV.js} +1 -1
  57. package/build/assets/files-tab-BW_qzyAm.js +1 -0
  58. package/build/assets/{form-control-classes-7yFSQJq_.js → form-control-classes-CGXBLcn1.js} +1 -1
  59. package/build/assets/format-model-name-BNPKVsIq.js +1 -0
  60. package/build/assets/format-relative-time-ZhQD4rsl.js +1 -0
  61. package/build/assets/git-provider-icon-CaQ1yLPi.js +1 -0
  62. package/build/assets/{git-repo-dropdown-ByMzpNko.js → git-repo-dropdown-CgXR6M_E.js} +1 -1
  63. package/build/assets/git-repo-dropdown-D2Uei3CO.js +1 -0
  64. package/build/assets/{highlighted-source-view-C_sfncLZ.js → highlighted-source-view-C_YOp8Fo.js} +1 -1
  65. package/build/assets/home-DNzzJcMP.js +1 -0
  66. package/build/assets/home-aCos-5vc.js +1 -0
  67. package/build/assets/{home-store-CxqsY9Fi.js → home-store-DgzViC3f.js} +1 -1
  68. package/build/assets/index-home-DNzzJcMP.js +1 -0
  69. package/build/assets/info-CLoC1LRo.js +1 -0
  70. package/build/assets/install-server-modal-D7zIqNKz.js +1 -0
  71. package/build/assets/kebab-menu-item-content-DERMdnDv.js +1 -0
  72. package/build/assets/{launch-CkqiSCqi.js → launch-5PjKeLak.js} +1 -1
  73. package/build/assets/{list-todo-DNWlzfaM.js → list-todo-BF049YaY.js} +1 -1
  74. package/build/assets/llm-not-configured-banner-BK9z5eS0.js +27 -0
  75. package/build/assets/llm-settings-BLAMK2tR.js +1 -0
  76. package/build/assets/llm-settings-BWsbarGM.js +1 -0
  77. package/build/assets/{llm-subscription-service-BAdOl5Xx.js → llm-subscription-service-SfnEV9Uc.js} +1 -1
  78. package/build/assets/loader-circle-D7IagAyF.js +1 -0
  79. package/build/assets/{loading-spinner-xhnrGP4Y.js → loading-spinner-DBgXgra0.js} +1 -1
  80. package/build/assets/{manage-backends-modal-DhGk5mJt.js → manage-backends-modal-BTqNPhzc.js} +1 -1
  81. package/build/assets/manifest-abebca3c.js +1 -0
  82. package/build/assets/manifest-subpage-layout-Rn-glaxV.js +1 -0
  83. package/build/assets/{map-provider-Mx8mZuE-.js → map-provider-BK4HnqYz.js} +1 -1
  84. package/build/assets/markdown-renderer-Cp6DlHQN.js +8 -0
  85. package/build/assets/{mcp-DCkvl6K0.js → mcp-BC-Ixn4T.js} +2 -2
  86. package/build/assets/{mcp-marketplace-utils-DEbMXlQr.js → mcp-marketplace-utils-BJWJgbB6.js} +1 -1
  87. package/build/assets/{messages-DSXvyc21.js → messages-CzFKrzPG.js} +5 -5
  88. package/build/assets/{modal-body-gIe21fDv.js → modal-body-BdKfD3Hl.js} +1 -1
  89. package/build/assets/{modal-classes-D66dlgZ7.js → modal-classes-D5_b5vMi.js} +1 -1
  90. package/build/assets/{modal-close-button-B6iOGYpI.js → modal-close-button-objukxk_.js} +1 -1
  91. package/build/assets/{mouse-pointer-click-BYX1Pw5W.js → mouse-pointer-click-C_gXyLyD.js} +1 -1
  92. package/build/assets/{npm-DLckoeJc.js → npm-CHVHV1MC.js} +1 -1
  93. package/build/assets/{onboarding-D7WDG66N.js → onboarding-Diw0V51A.js} +1 -1
  94. package/build/assets/onboarding-modal-Da4yHzsA.js +1 -0
  95. package/build/assets/{organization-service.api-D-s9z18a.js → organization-service.api-BztSy69u.js} +1 -1
  96. package/build/assets/parse-git-remote-url-BkXN9jzl.js +1 -0
  97. package/build/assets/{pencil-BGOCPRyu.js → pencil-49avMHlD.js} +1 -1
  98. package/build/assets/{planner-tab-AKaXIIfg.js → planner-tab-CJiSpNax.js} +1 -1
  99. package/build/assets/{plugins-management-service-wtmY4QQw.js → plugins-management-service-lHdNKWNU.js} +1 -1
  100. package/build/assets/plus-C0BagBpV.js +1 -0
  101. package/build/assets/profiles-service.api-BLr7FXec.js +1 -0
  102. package/build/assets/providers-CWXnb7S3.js +1 -0
  103. package/build/assets/{proxy-9Z_WZggZ.js → proxy-DYDXxtje.js} +1 -1
  104. package/build/assets/{puzzle-BWLmN8z4.js → puzzle-Bt2B1rzf.js} +1 -1
  105. package/build/assets/recommended-automations-launcher-CcFMfJRR.js +1 -0
  106. package/build/assets/{refresh-cw-CXtFWpwv.js → refresh-cw-nmcesifB.js} +1 -1
  107. package/build/assets/root-CT4t7apy.css +1 -0
  108. package/build/assets/root-DnRgLKpU.js +2 -0
  109. package/build/assets/root-layout-2L9tr-BN.js +2 -0
  110. package/build/assets/{schema-field-OnM5Uob_.js → schema-field-C59lVZTA.js} +1 -1
  111. package/build/assets/{sdk-section-page-m5WQm2sy.js → sdk-section-page-BbBEQQjq.js} +1 -1
  112. package/build/assets/search-CYSDBczG.js +1 -0
  113. package/build/assets/secrets-service-BtsgYSlj.js +1 -0
  114. package/build/assets/secrets-settings-DJ0OKa4_.js +1 -0
  115. package/build/assets/{segmented-toggle-DCspm2U5.js → segmented-toggle-DiVfTZuN.js} +1 -1
  116. package/build/assets/{settings-BJQI_iBM.js → settings-CDqL_Uqh.js} +1 -1
  117. package/build/assets/{settings-CWFxymAf.js → settings-DcNPxz0v.js} +1 -1
  118. package/build/assets/{settings-dropdown-input-BI8V_Nov.js → settings-dropdown-input-Ba1UN5Du.js} +1 -1
  119. package/build/assets/{settings-index-BiTqJoH7.js → settings-index-Cz2cKt2K.js} +1 -1
  120. package/build/assets/{settings-input-Cz62hWVW.js → settings-input-CWUEHHvS.js} +1 -1
  121. package/build/assets/{settings-list-classes-z_3FW5H3.js → settings-list-classes-DdcLXDGn.js} +1 -1
  122. package/build/assets/{settings-modal-BIrIVn97.js → settings-modal-nEEK1Kh1.js} +1 -1
  123. package/build/assets/{settings-service.api--rJmnuLa.js → settings-service.api-3yAUFcaP.js} +1 -1
  124. package/build/assets/{settings-switch-BRsSI8Vs.js → settings-switch-Dk9hChx1.js} +1 -1
  125. package/build/assets/{settings-utils-Ba7vel6T.js → settings-utils-CxjtCSB3.js} +1 -1
  126. package/build/assets/{shared-conversation-CQK_PqHx.js → shared-conversation-DbAcrx3s.js} +1 -1
  127. package/build/assets/{shield-check-T3JsDJiM.js → shield-check-CiOTXxV2.js} +1 -1
  128. package/build/assets/{sidebar-layout-Bhi3hVcu.js → sidebar-layout-lrfPooIB.js} +1 -1
  129. package/build/assets/{sidebar-mobile-menu-toggle-YWJNRN4p.js → sidebar-mobile-menu-toggle-KERXfNu1.js} +1 -1
  130. package/build/assets/{sidebar-nav-link-BSUlY5Ml.js → sidebar-nav-link-B8lFD6Cb.js} +1 -1
  131. package/build/assets/{sidebar-store-Cl1cUv_F.js → sidebar-store-u6pUpxJ7.js} +1 -1
  132. package/build/assets/{skill-card-pill-row-BXz8L8Rn.js → skill-card-pill-row-dGIUzr_b.js} +1 -1
  133. package/build/assets/{skill-icon-badge-QgfKv31M.js → skill-icon-badge-kkOWZ89w.js} +1 -1
  134. package/build/assets/{skill-scope-Dc3JPW-8.js → skill-scope-DWN1PBPH.js} +1 -1
  135. package/build/assets/{skills-plugins-CpTV27n3.js → skills-plugins-DFjH2Nq6.js} +1 -1
  136. package/build/assets/{skills-settings-CSW__IW4.js → skills-settings-DIHpl3ol.js} +2 -2
  137. package/build/assets/{sparkles-TJ0mdECe.js → sparkles-ntyMLnbj.js} +1 -1
  138. package/build/assets/{styled-tooltip-BUMSbgrF.js → styled-tooltip-B6JXcsmL.js} +1 -1
  139. package/build/assets/{task-list-tab-mAHMOkUz.js → task-list-tab-aEJzEcHD.js} +1 -1
  140. package/build/assets/{terminal-BzxBaHx2.js → terminal-Dch7Ty5K.js} +1 -1
  141. package/build/assets/{toggle-switch-O5v1FgxW.js → toggle-switch-B2MLKsgt.js} +1 -1
  142. package/build/assets/{trash-2-C0xNVp9c.js → trash-2-yZnDM4Ji.js} +1 -1
  143. package/build/assets/{typography-NOKSk9_R.js → typography-DB5HoCk0.js} +1 -1
  144. package/build/assets/usage-tab-DrEtBmBT.js +1 -0
  145. package/build/assets/use-acp-credential-form-c8KXAa8A.js +1 -0
  146. package/build/assets/{use-activate-agent-profile-D9YRQPTF.js → use-activate-agent-profile-B0p34N3q.js} +1 -1
  147. package/build/assets/use-agent-profiles-CPIc6wLH.js +1 -0
  148. package/build/assets/{use-can-manage-org-profiles-Dxzq2AEd.js → use-can-manage-org-profiles-Dz_8Eqkv.js} +1 -1
  149. package/build/assets/{use-cloud-current-user-id-Co2eJk2A.js → use-cloud-current-user-id-DvUVqCle.js} +1 -1
  150. package/build/assets/use-config-C3PO4TBw.js +1 -0
  151. package/build/assets/{use-create-conversation-CoHTiCbI.js → use-create-conversation-DVrA_uLD.js} +1 -1
  152. package/build/assets/use-create-secret-DbBIpaCR.js +1 -0
  153. package/build/assets/use-get-secrets-6PBitf54.js +1 -0
  154. package/build/assets/use-has-permission-CQ31Cl8j.js +1 -0
  155. package/build/assets/use-is-authed-2UrDIziV.js +1 -0
  156. package/build/assets/use-is-creating-conversation-BvYpmb3v.js +1 -0
  157. package/build/assets/use-llm-profiles-diZS4FiN.js +1 -0
  158. package/build/assets/use-manifest-capabilities-HIWCtIUm.js +1 -0
  159. package/build/assets/{use-onboarding-completion-CCE8OQgV.js → use-onboarding-completion-Bs8PcXik.js} +1 -1
  160. package/build/assets/{use-plugins-marketplace-CEiak69h.js → use-plugins-marketplace-BCuNLFsr.js} +1 -1
  161. package/build/assets/use-save-agent-profile-BaMNSpBV.js +1 -0
  162. package/build/assets/{use-save-settings-DNmnbG6f.js → use-save-settings-BahvC2Gw.js} +1 -1
  163. package/build/assets/{use-settings-wCwPaSIv.js → use-settings-C6ZxpBDX.js} +1 -1
  164. package/build/assets/{use-settings-nav-items-BlFL7YhM.js → use-settings-nav-items-Bqn69Ggw.js} +2 -2
  165. package/build/assets/use-tracking-Bhu7gk2y.js +3 -0
  166. package/build/assets/{use-user-conversation-DVtfg0B_.js → use-user-conversation-BkNXpXrd.js} +1 -1
  167. package/build/assets/{useInfiniteQuery-p1xq35r2.js → useInfiniteQuery-c_OmS3lK.js} +1 -1
  168. package/build/assets/{useMutation-BFDQmgk9.js → useMutation-6ZsQCqAn.js} +1 -1
  169. package/build/assets/{useQueries-DF1NAAeC.js → useQueries-xDrGlTO_.js} +1 -1
  170. package/build/assets/{useQuery-DRv--6bK.js → useQuery-Bs5NFz2s.js} +1 -1
  171. package/build/assets/{utils-zxEjKE70.js → utils-DUD5ScdA.js} +1 -1
  172. package/build/assets/{vendor~entry.client-CgNUwtQL.js → vendor~entry.client-BFE7Llz9.js} +1 -1
  173. package/build/assets/{vendor~entry.client-zSIea5EO.js → vendor~entry.client-BaKt_PIp.js} +1 -1
  174. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kv3pap4j-B8i84bh4.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kepq2zme-D87mAf6K.js} +1 -1
  175. package/build/assets/{vendor~files-tab-D9wR9yM2.js → vendor~files-tab-B6POTS66.js} +1 -1
  176. package/build/assets/{vendor~index-home~home~conversation-panel~conversation-Ipoe6hvY.js → vendor~index-home~home~conversation-panel~conversation-53Q_U6uP.js} +1 -1
  177. package/build/assets/{vendor~root-layout-C-IgVx6e.js → vendor~root-layout-DeQACFVA.js} +1 -1
  178. package/build/assets/{vendor~root-layout~conversation-panel~conversation-Dhd_I4H4.js → vendor~root-layout~conversation-panel~conversation-bv7ooVgH.js} +1 -1
  179. package/build/assets/{vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-Cc0f9kPP.js → vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-e8pX05xf.js} +1 -1
  180. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-d7svw-BB.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-BvS6PCK2.js} +1 -1
  181. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-4WsUP-tz.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-DOPLJMH9.js} +1 -1
  182. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~gxu4j2ea-D5DWZVc7.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iw7io29p-Dz8NQve5.js} +1 -1
  183. package/build/assets/{verification-settings-B8UMQ7MG.js → verification-settings-COueVXmM.js} +1 -1
  184. package/build/assets/{wrench-DIka3hCq.js → wrench-C95ujnYo.js} +1 -1
  185. package/build/assets/x-86ZCfxKY.js +1 -0
  186. package/build/assets/{zap-DHddTPc0.js → zap-DRa-r-Tf.js} +1 -1
  187. package/build/index.html +4 -4
  188. package/build/locales/ar/openhands.json +60 -2
  189. package/build/locales/ca/openhands.json +60 -2
  190. package/build/locales/de/openhands.json +60 -2
  191. package/build/locales/en/openhands.json +60 -2
  192. package/build/locales/es/openhands.json +60 -2
  193. package/build/locales/fr/openhands.json +60 -2
  194. package/build/locales/it/openhands.json +60 -2
  195. package/build/locales/ja/openhands.json +60 -2
  196. package/build/locales/ko-KR/openhands.json +60 -2
  197. package/build/locales/no/openhands.json +60 -2
  198. package/build/locales/pt/openhands.json +60 -2
  199. package/build/locales/tr/openhands.json +60 -2
  200. package/build/locales/uk/openhands.json +60 -2
  201. package/build/locales/zh-CN/openhands.json +60 -2
  202. package/build/locales/zh-TW/openhands.json +60 -2
  203. package/config/defaults.json +1 -1
  204. package/dist/api/automation-service/automation-service.api.cjs +1 -1
  205. package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
  206. package/dist/api/automation-service/automation-service.api.d.ts +10 -0
  207. package/dist/api/automation-service/automation-service.api.js +42 -0
  208. package/dist/api/automation-service/automation-service.api.js.map +1 -1
  209. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  210. package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
  211. package/dist/api/cloud/conversation-service.api.d.ts +9 -0
  212. package/dist/api/cloud/conversation-service.api.js +11 -1
  213. package/dist/api/cloud/conversation-service.api.js.map +1 -1
  214. package/dist/api/profiles-service/profiles-service.api.cjs +1 -1
  215. package/dist/api/profiles-service/profiles-service.api.cjs.map +1 -1
  216. package/dist/api/profiles-service/profiles-service.api.d.ts +15 -2
  217. package/dist/api/profiles-service/profiles-service.api.js +25 -2
  218. package/dist/api/profiles-service/profiles-service.api.js.map +1 -1
  219. package/dist/api/secrets-service.cjs +1 -1
  220. package/dist/api/secrets-service.cjs.map +1 -1
  221. package/dist/api/secrets-service.d.ts +5 -0
  222. package/dist/api/secrets-service.js +12 -6
  223. package/dist/api/secrets-service.js.map +1 -1
  224. package/dist/components/conversation-events/chat/event-message-components/index.d.ts +0 -1
  225. package/dist/components/features/automations/git-sync/git-sync-activity-row.d.ts +15 -0
  226. package/dist/components/features/automations/git-sync/git-sync-config-form.d.ts +13 -0
  227. package/dist/components/features/automations/git-sync/git-sync-error-banner.d.ts +6 -0
  228. package/dist/components/features/automations/git-sync/git-sync-not-local-state.d.ts +1 -0
  229. package/dist/components/features/automations/git-sync/git-sync-overview-section.d.ts +12 -0
  230. package/dist/components/features/automations/git-sync/git-sync-skeleton.d.ts +1 -0
  231. package/dist/components/features/automations/git-sync/git-sync-status-pill.d.ts +7 -0
  232. package/dist/components/features/automations/git-sync/git-sync-unsupported-state.d.ts +7 -0
  233. package/dist/components/features/automations/responder-deployment-modal.d.ts +2 -1
  234. package/dist/components/features/chat/chat-interface.cjs +2 -2
  235. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  236. package/dist/components/features/chat/chat-interface.js +177 -176
  237. package/dist/components/features/chat/chat-interface.js.map +1 -1
  238. package/dist/components/features/chat/error-message-banner.cjs +1 -1
  239. package/dist/components/features/chat/error-message-banner.cjs.map +1 -1
  240. package/dist/components/features/chat/error-message-banner.d.ts +3 -1
  241. package/dist/components/features/chat/error-message-banner.js +64 -58
  242. package/dist/components/features/chat/error-message-banner.js.map +1 -1
  243. package/dist/components/features/conversation-panel/compact-conversation-row.cjs +1 -1
  244. package/dist/components/features/conversation-panel/compact-conversation-row.js +6 -6
  245. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  246. package/dist/components/features/conversation-panel/conversation-panel.js +19 -19
  247. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +10 -7
  248. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs +1 -1
  249. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs.map +1 -1
  250. package/dist/components/shared/buttons/conversation-confirmation-buttons.js +1 -1
  251. package/dist/components/shared/buttons/conversation-confirmation-buttons.js.map +1 -1
  252. package/dist/config/defaults.cjs +1 -1
  253. package/dist/config/defaults.cjs.map +1 -1
  254. package/dist/config/defaults.js +1 -1
  255. package/dist/config/defaults.js.map +1 -1
  256. package/dist/contexts/conversation-websocket-context.cjs +2 -2
  257. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  258. package/dist/contexts/conversation-websocket-context.js +12 -12
  259. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  260. package/dist/hooks/query/use-commit-changes.cjs +1 -1
  261. package/dist/hooks/query/use-commit-changes.js +4 -4
  262. package/dist/hooks/query/use-git-sync.d.ts +19 -0
  263. package/dist/hooks/query/use-task-polling.cjs +1 -1
  264. package/dist/hooks/query/use-task-polling.cjs.map +1 -1
  265. package/dist/hooks/query/use-task-polling.js +12 -10
  266. package/dist/hooks/query/use-task-polling.js.map +1 -1
  267. package/dist/hooks/query/use-unified-get-git-changes.cjs +1 -1
  268. package/dist/hooks/query/use-unified-get-git-changes.js +4 -4
  269. package/dist/hooks/query/use-unified-git-commits.cjs +1 -1
  270. package/dist/hooks/query/use-unified-git-commits.js +4 -4
  271. package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
  272. package/dist/hooks/query/use-unified-git-diff.js +4 -4
  273. package/dist/hooks/query/use-workspace-files.cjs +1 -1
  274. package/dist/hooks/query/use-workspace-files.cjs.map +1 -1
  275. package/dist/hooks/query/use-workspace-files.d.ts +14 -4
  276. package/dist/hooks/query/use-workspace-files.js +45 -32
  277. package/dist/hooks/query/use-workspace-files.js.map +1 -1
  278. package/dist/hooks/use-responder-url-secret.d.ts +5 -0
  279. package/dist/hooks/use-tracking.cjs +1 -1
  280. package/dist/hooks/use-tracking.cjs.map +1 -1
  281. package/dist/hooks/use-tracking.d.ts +6 -0
  282. package/dist/hooks/use-tracking.js +6 -0
  283. package/dist/hooks/use-tracking.js.map +1 -1
  284. package/dist/i18n/declaration.cjs +1 -1
  285. package/dist/i18n/declaration.cjs.map +1 -1
  286. package/dist/i18n/declaration.d.ts +59 -1
  287. package/dist/i18n/declaration.js +1 -1
  288. package/dist/i18n/declaration.js.map +1 -1
  289. package/dist/i18n/translation.cjs +1 -1
  290. package/dist/i18n/translation.cjs.map +1 -1
  291. package/dist/i18n/translation.js +1001 -15
  292. package/dist/i18n/translation.js.map +1 -1
  293. package/dist/locales/ar/openhands.json +60 -2
  294. package/dist/locales/ca/openhands.json +60 -2
  295. package/dist/locales/de/openhands.json +60 -2
  296. package/dist/locales/en/openhands.json +60 -2
  297. package/dist/locales/es/openhands.json +60 -2
  298. package/dist/locales/fr/openhands.json +60 -2
  299. package/dist/locales/it/openhands.json +60 -2
  300. package/dist/locales/ja/openhands.json +60 -2
  301. package/dist/locales/ko-KR/openhands.json +60 -2
  302. package/dist/locales/no/openhands.json +60 -2
  303. package/dist/locales/pt/openhands.json +60 -2
  304. package/dist/locales/tr/openhands.json +60 -2
  305. package/dist/locales/uk/openhands.json +60 -2
  306. package/dist/locales/zh-CN/openhands.json +60 -2
  307. package/dist/locales/zh-TW/openhands.json +60 -2
  308. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.cjs +2 -0
  309. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.cjs.map +1 -0
  310. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.js +27 -0
  311. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.js.map +1 -0
  312. package/dist/package.cjs +1 -1
  313. package/dist/package.cjs.map +1 -1
  314. package/dist/package.js +1 -1
  315. package/dist/package.js.map +1 -1
  316. package/dist/routes/files-tab.cjs +1 -1
  317. package/dist/routes/files-tab.js +11 -11
  318. package/dist/routes/files-tab.js.map +1 -1
  319. package/dist/services/settings.cjs +1 -1
  320. package/dist/services/settings.cjs.map +1 -1
  321. package/dist/services/settings.js +2 -2
  322. package/dist/services/settings.js.map +1 -1
  323. package/dist/services/telemetry.cjs +1 -1
  324. package/dist/services/telemetry.cjs.map +1 -1
  325. package/dist/services/telemetry.js +47 -51
  326. package/dist/services/telemetry.js.map +1 -1
  327. package/dist/stores/error-message-store.cjs +1 -1
  328. package/dist/stores/error-message-store.cjs.map +1 -1
  329. package/dist/stores/error-message-store.d.ts +3 -1
  330. package/dist/stores/error-message-store.js +5 -3
  331. package/dist/stores/error-message-store.js.map +1 -1
  332. package/dist/types/git-sync.d.ts +51 -0
  333. package/dist/utils/error-handler.cjs +1 -1
  334. package/dist/utils/error-handler.cjs.map +1 -1
  335. package/dist/utils/error-handler.d.ts +3 -2
  336. package/dist/utils/error-handler.js +18 -5
  337. package/dist/utils/error-handler.js.map +1 -1
  338. package/dist/utils/format-model-name.cjs +1 -1
  339. package/dist/utils/format-model-name.cjs.map +1 -1
  340. package/dist/utils/format-model-name.d.ts +1 -0
  341. package/dist/utils/format-model-name.js +1 -0
  342. package/dist/utils/format-model-name.js.map +1 -1
  343. package/dist/utils/transcript-export/load-complete-events.cjs +1 -1
  344. package/dist/utils/transcript-export/load-complete-events.cjs.map +1 -1
  345. package/dist/utils/transcript-export/load-complete-events.js +24 -16
  346. package/dist/utils/transcript-export/load-complete-events.js.map +1 -1
  347. package/package.json +1 -1
  348. package/scripts/check-translation-completeness.cjs +3 -0
  349. package/scripts/dev-with-automation.mjs +98 -9
  350. package/build/assets/active-backend-context-IqOjobNK.js +0 -2
  351. package/build/assets/agent-settings-COfJsQ1d.js +0 -1
  352. package/build/assets/automation-detail-B-jQ39Mm.js +0 -3
  353. package/build/assets/automation-export-B1dywoqk.js +0 -2
  354. package/build/assets/automation-setup-route-C-bWUZBi.js +0 -1
  355. package/build/assets/automations-list-DnMxlxsL.js +0 -1
  356. package/build/assets/browser-B9AbV6kG.js +0 -7
  357. package/build/assets/check-WAFXrrs_.js +0 -1
  358. package/build/assets/chevron-down-CifNWzme.js +0 -1
  359. package/build/assets/circle-check-CtnimJa8.js +0 -1
  360. package/build/assets/conversation-BPo8SvLN.js +0 -1
  361. package/build/assets/conversation-CPCVsCUY.js +0 -90
  362. package/build/assets/conversation-panel-BPo8SvLN.js +0 -1
  363. package/build/assets/conversation-state-store-C5YpjMNy.js +0 -1
  364. package/build/assets/files-tab-DV0L-lFE.js +0 -1
  365. package/build/assets/format-model-name-BN_rx1Y3.js +0 -1
  366. package/build/assets/format-relative-time-DSErAuVr.js +0 -1
  367. package/build/assets/git-provider-icon-BvlLAche.js +0 -1
  368. package/build/assets/git-repo-dropdown-C6R7hgM8.js +0 -1
  369. package/build/assets/home-BOrlwLqG.js +0 -1
  370. package/build/assets/home-BvMNC6E-.js +0 -1
  371. package/build/assets/index-home-BvMNC6E-.js +0 -1
  372. package/build/assets/info-BRVcJAUg.js +0 -1
  373. package/build/assets/install-server-modal-Bl4G1zF4.js +0 -1
  374. package/build/assets/kebab-menu-item-content-BGoGFEAg.js +0 -1
  375. package/build/assets/llm-not-configured-banner-DLw_tu-G.js +0 -27
  376. package/build/assets/llm-settings-B-pkVCrt.js +0 -1
  377. package/build/assets/llm-settings-DSC0-IQ7.js +0 -1
  378. package/build/assets/loader-circle-B47W9XqT.js +0 -1
  379. package/build/assets/manifest-653b89aa.js +0 -1
  380. package/build/assets/manifest-subpage-layout-CKWvL2ig.js +0 -1
  381. package/build/assets/markdown-renderer-DUmk7pFc.js +0 -8
  382. package/build/assets/onboarding-modal-C_xVrFNv.js +0 -1
  383. package/build/assets/plus-zNEL8rcX.js +0 -1
  384. package/build/assets/profiles-service.api-DCMYxHMi.js +0 -1
  385. package/build/assets/providers-BjrtjX4L.js +0 -1
  386. package/build/assets/recommended-automations-launcher-CFlzhu1o.js +0 -1
  387. package/build/assets/root-CanxKlNV.css +0 -1
  388. package/build/assets/root-DkfG_UWR.js +0 -2
  389. package/build/assets/root-layout-D75LHYhh.js +0 -2
  390. package/build/assets/search-CycosyjN.js +0 -1
  391. package/build/assets/secrets-service-B26R61C1.js +0 -1
  392. package/build/assets/secrets-settings-CbLYCKOX.js +0 -1
  393. package/build/assets/usage-tab-tEmVN0KD.js +0 -1
  394. package/build/assets/use-acp-credential-form-BPNi0Zbm.js +0 -1
  395. package/build/assets/use-agent-profiles-Cb2zUW3l.js +0 -1
  396. package/build/assets/use-config-DKAMiI5R.js +0 -1
  397. package/build/assets/use-create-secret-2zBlCDsF.js +0 -1
  398. package/build/assets/use-is-authed-C52-ivnB.js +0 -1
  399. package/build/assets/use-is-creating-conversation-DdaAE6jV.js +0 -1
  400. package/build/assets/use-llm-profiles-C_BHue65.js +0 -1
  401. package/build/assets/use-manifest-capabilities-DPrQsaPP.js +0 -1
  402. package/build/assets/use-save-agent-profile-DK9idD6a.js +0 -1
  403. package/build/assets/use-tracking-CVwPmhlX.js +0 -3
  404. package/build/assets/x-BWKA2Ver.js +0 -1
  405. package/dist/api/cloud/suggestions-service.api.d.ts +0 -8
  406. package/dist/api/suggestions-service/suggestions-service.api.d.ts +0 -11
  407. package/dist/components/conversation-events/chat/event-message-components/observation-pair-event-message.d.ts +0 -7
  408. package/dist/components/conversation-events/index.d.ts +0 -1
  409. package/dist/components/features/automations/metadata-chip.d.ts +0 -6
  410. package/dist/components/features/chat/task-tracking/status-badge.d.ts +0 -5
  411. package/dist/components/features/chat/task-tracking/status-icon.d.ts +0 -5
  412. package/dist/components/features/chat/task-tracking/task-list-section.d.ts +0 -10
  413. package/dist/components/features/controls/tools.d.ts +0 -2
  414. package/dist/components/features/conversation-panel/new-conversation-button.d.ts +0 -23
  415. package/dist/components/features/home/home-header/guide-message.d.ts +0 -1
  416. package/dist/components/features/home/home-header/home-header.d.ts +0 -1
  417. package/dist/components/features/home/new-conversation/create-conversation-button.d.ts +0 -1
  418. package/dist/components/features/home/new-conversation/new-conversation.d.ts +0 -1
  419. package/dist/components/features/home/new-conversation.d.ts +0 -1
  420. package/dist/components/features/home/tasks/task-card.d.ts +0 -6
  421. package/dist/components/features/home/tasks/task-group.d.ts +0 -7
  422. package/dist/components/features/home/tasks/task-issue-number.d.ts +0 -6
  423. package/dist/components/features/home/tasks/task-item-title.d.ts +0 -1
  424. package/dist/components/features/home/tasks/task-suggestions-skeleton.d.ts +0 -1
  425. package/dist/components/features/home/tasks/task-suggestions.d.ts +0 -6
  426. package/dist/components/features/settings/agent-server-onboarding.d.ts +0 -10
  427. package/dist/components/features/settings/mcp-settings/mcp-server-list-item.d.ts +0 -6
  428. package/dist/components/features/settings/mcp-settings/mcp-server-list.d.ts +0 -8
  429. package/dist/hooks/query/use-suggested-tasks.d.ts +0 -1
  430. package/dist/hooks/use-should-show-user-features.d.ts +0 -1
  431. package/dist/ui/card-title.d.ts +0 -16
  432. package/dist/ui/card.d.ts +0 -14
  433. package/dist/utils/group-suggested-tasks.d.ts +0 -7
  434. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dh9v73sa-rfV8mQ9m.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dcc51rps-rfV8mQ9m.js} +0 -0
  435. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~lq5hicxx-cHFhpUBq.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ox0jfsa0-cHFhpUBq.js} +0 -0
  436. /package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~9pstowjp-BOVIFA_g.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-BOVIFA_g.js} +0 -0
  437. /package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~ht43enc2-C0JXQb_q.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-C0JXQb_q.js} +0 -0
@@ -1,3 +1,4 @@
1
+ import type { ErrorClassification } from "@openhands/typescript-client";
1
2
  /**
2
3
  * "connection" errors auto-clear once connectivity recovers; "conversation"
3
4
  * errors (e.g. a wrong API key) are sticky and clear only on an explicit user
@@ -13,9 +14,10 @@ interface ErrorMessageState {
13
14
  * null for errors that carry no code (connection errors, plain strings).
14
15
  */
15
16
  errorCode: string | null;
17
+ errorClassification: ErrorClassification | null;
16
18
  }
17
19
  interface ErrorMessageActions {
18
- setErrorMessage: (message: string, type?: ErrorMessageType, code?: string | null) => void;
20
+ setErrorMessage: (message: string, type?: ErrorMessageType, code?: string | null, classification?: ErrorClassification | null) => void;
19
21
  removeErrorMessage: () => void;
20
22
  /** Clears the error only when it is a transient connection error. */
21
23
  clearConnectionError: () => void;
@@ -3,13 +3,15 @@ import { create as e } from "../node_modules/zustand/esm/react.js";
3
3
  var t = {
4
4
  errorMessage: null,
5
5
  errorType: null,
6
- errorCode: null
6
+ errorCode: null,
7
+ errorClassification: null
7
8
  }, n = e((e) => ({
8
9
  ...t,
9
- setErrorMessage: (t, n = "conversation", r = null) => e(() => ({
10
+ setErrorMessage: (t, n = "conversation", r = null, i = null) => e(() => ({
10
11
  errorMessage: t,
11
12
  errorType: n,
12
- errorCode: r
13
+ errorCode: r,
14
+ errorClassification: i
13
15
  })),
14
16
  removeErrorMessage: () => e(() => ({ ...t })),
15
17
  clearConnectionError: () => e((e) => e.errorType === "connection" ? { ...t } : e)
@@ -1 +1 @@
1
- {"version":3,"file":"error-message-store.js","names":[],"sources":["../../src/stores/error-message-store.ts"],"sourcesContent":["import { create } from \"zustand\";\n\n/**\n * \"connection\" errors auto-clear once connectivity recovers; \"conversation\"\n * errors (e.g. a wrong API key) are sticky and clear only on an explicit user\n * action (dismiss, retry, new message).\n */\nexport type ErrorMessageType = \"connection\" | \"conversation\";\n\ninterface ErrorMessageState {\n errorMessage: string | null;\n errorType: ErrorMessageType | null;\n /**\n * Structured error code from a ConversationErrorEvent/ServerErrorEvent (e.g.\n * \"ACPAuthRequired\"), used to render a code-specific header + recovery action.\n * null for errors that carry no code (connection errors, plain strings).\n */\n errorCode: string | null;\n}\n\ninterface ErrorMessageActions {\n setErrorMessage: (\n message: string,\n type?: ErrorMessageType,\n code?: string | null,\n ) => void;\n removeErrorMessage: () => void;\n /** Clears the error only when it is a transient connection error. */\n clearConnectionError: () => void;\n}\n\ntype ErrorMessageStore = ErrorMessageState & ErrorMessageActions;\n\nconst initialState: ErrorMessageState = {\n errorMessage: null,\n errorType: null,\n errorCode: null,\n};\n\nexport const useErrorMessageStore = create<ErrorMessageStore>((set) => ({\n ...initialState,\n\n setErrorMessage: (\n message: string,\n type: ErrorMessageType = \"conversation\",\n code: string | null = null,\n ) =>\n set(() => ({\n errorMessage: message,\n errorType: type,\n errorCode: code,\n })),\n\n removeErrorMessage: () => set(() => ({ ...initialState })),\n\n clearConnectionError: () =>\n set((state) =>\n state.errorType === \"connection\" ? { ...initialState } : state,\n ),\n}));\n"],"mappings":";;AAiCA,IAAM,IAAkC;CACtC,cAAc;CACd,WAAW;CACX,WAAW;AACb,GAEa,IAAuB,GAA2B,OAAS;CACtE,GAAG;CAEH,kBACE,GACA,IAAyB,gBACzB,IAAsB,SAEtB,SAAW;EACT,cAAc;EACd,WAAW;EACX,WAAW;CACb,EAAE;CAEJ,0BAA0B,SAAW,EAAE,GAAG,EAAa,EAAE;CAEzD,4BACE,GAAK,MACH,EAAM,cAAc,eAAe,EAAE,GAAG,EAAa,IAAI,CAC3D;AACJ,EAAE"}
1
+ {"version":3,"file":"error-message-store.js","names":[],"sources":["../../src/stores/error-message-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport type { ErrorClassification } from \"@openhands/typescript-client\";\n\n/**\n * \"connection\" errors auto-clear once connectivity recovers; \"conversation\"\n * errors (e.g. a wrong API key) are sticky and clear only on an explicit user\n * action (dismiss, retry, new message).\n */\nexport type ErrorMessageType = \"connection\" | \"conversation\";\n\ninterface ErrorMessageState {\n errorMessage: string | null;\n errorType: ErrorMessageType | null;\n /**\n * Structured error code from a ConversationErrorEvent/ServerErrorEvent (e.g.\n * \"ACPAuthRequired\"), used to render a code-specific header + recovery action.\n * null for errors that carry no code (connection errors, plain strings).\n */\n errorCode: string | null;\n errorClassification: ErrorClassification | null;\n}\n\ninterface ErrorMessageActions {\n setErrorMessage: (\n message: string,\n type?: ErrorMessageType,\n code?: string | null,\n classification?: ErrorClassification | null,\n ) => void;\n removeErrorMessage: () => void;\n /** Clears the error only when it is a transient connection error. */\n clearConnectionError: () => void;\n}\n\ntype ErrorMessageStore = ErrorMessageState & ErrorMessageActions;\n\nconst initialState: ErrorMessageState = {\n errorMessage: null,\n errorType: null,\n errorCode: null,\n errorClassification: null,\n};\n\nexport const useErrorMessageStore = create<ErrorMessageStore>((set) => ({\n ...initialState,\n\n setErrorMessage: (\n message: string,\n type: ErrorMessageType = \"conversation\",\n code: string | null = null,\n classification: ErrorClassification | null = null,\n ) =>\n set(() => ({\n errorMessage: message,\n errorType: type,\n errorCode: code,\n errorClassification: classification,\n })),\n\n removeErrorMessage: () => set(() => ({ ...initialState })),\n\n clearConnectionError: () =>\n set((state) =>\n state.errorType === \"connection\" ? { ...initialState } : state,\n ),\n}));\n"],"mappings":";;AAoCA,IAAM,IAAkC;CACtC,cAAc;CACd,WAAW;CACX,WAAW;CACX,qBAAqB;AACvB,GAEa,IAAuB,GAA2B,OAAS;CACtE,GAAG;CAEH,kBACE,GACA,IAAyB,gBACzB,IAAsB,MACtB,IAA6C,SAE7C,SAAW;EACT,cAAc;EACd,WAAW;EACX,WAAW;EACX,qBAAqB;CACvB,EAAE;CAEJ,0BAA0B,SAAW,EAAE,GAAG,EAAa,EAAE;CAEzD,4BACE,GAAK,MACH,EAAM,cAAc,eAAe,EAAE,GAAG,EAAa,IAAI,CAC3D;AACJ,EAAE"}
@@ -0,0 +1,51 @@
1
+ export interface GitSyncStatus {
2
+ enabled: boolean;
3
+ repo_url: string;
4
+ branch: string;
5
+ path: string;
6
+ encryption_enabled: boolean;
7
+ /** Seconds between automatic syncs; 0 means manual-only. */
8
+ interval_seconds: number;
9
+ last_synced_commit: string | null;
10
+ last_synced_at: string | null;
11
+ last_error: string | null;
12
+ last_error_at: string | null;
13
+ dirty_count: number;
14
+ /**
15
+ * Whether a sync cycle is running right now, including one the backend's own
16
+ * interval started. A cycle reports its outcome only once it ends, so
17
+ * without this the page cannot tell a sync in flight from one that never
18
+ * started. Absent on automation backends that predate the field.
19
+ */
20
+ sync_in_progress?: boolean;
21
+ sync_started_at?: string | null;
22
+ }
23
+ export interface GitSyncConfigUpdateRequest {
24
+ enabled?: boolean | null;
25
+ interval_seconds?: number | null;
26
+ repo_url?: string | null;
27
+ branch?: string | null;
28
+ path?: string | null;
29
+ token?: string | null;
30
+ encryption_key?: string | null;
31
+ author_name?: string | null;
32
+ author_email?: string | null;
33
+ }
34
+ export interface GitSyncTriggerResponse {
35
+ triggered: boolean;
36
+ }
37
+ /**
38
+ * Whether a configuration can reach its repo, from `POST /v1/git-sync/check`.
39
+ *
40
+ * `ok` is about reachability only -- the backend runs a single `git ls-remote`
41
+ * rather than a sync, so a token without write scope passes here and still
42
+ * fails at push time, and the encryption key is never exercised.
43
+ * `branch_exists: false` alongside `ok: true` is normal for a repo that has
44
+ * never been synced: the first cycle creates the branch.
45
+ */
46
+ export interface GitSyncCheckResponse {
47
+ ok: boolean;
48
+ branch_exists: boolean;
49
+ /** git's own failure output, with credentials in the URL redacted. */
50
+ detail: string | null;
51
+ }
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");const e=require("../services/telemetry.cjs");function t({message:t,source:n,metadata:r={}}){e.trackException(Error(t),{error_source:n||`unknown`,...r})}exports.trackError=t;
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../services/telemetry.cjs");var t=new Set([`error_source`,`error_kind`,`error_id`,`error_telemetry`]);function n({source:n,metadata:r={},classification:i}){let a=Object.fromEntries(Object.entries(r).filter(([e])=>!t.has(e))),o=i?.kind||`unknown`,s=typeof r.eventId==`string`?r.eventId:void 0;s!=null&&delete a.eventId;let c=i?.error_id??s;e.trackEvent(`error_outcome`,{...a,current_url:window.location.href,error_source:n||`unknown`,error_kind:o,...c==null?{}:{error_id:c},error_telemetry:o===`internal`||o===`unknown`?`diagnostic`:`outcome`})}exports.trackError=n;
2
2
  //# sourceMappingURL=error-handler.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.cjs","names":[],"sources":["../../src/utils/error-handler.ts"],"sourcesContent":["import { trackException } from \"#/services/telemetry\";\n\ninterface ErrorDetails {\n message: string;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport function trackError({ message, source, metadata = {} }: ErrorDetails) {\n const error = new Error(message);\n void trackException(error, {\n error_source: source || \"unknown\",\n ...metadata,\n });\n}\n"],"mappings":"0FAQA,SAAgB,EAAW,CAAE,UAAS,SAAQ,WAAW,CAAC,GAAmB,CAE3E,EAAK,eADa,MAAM,CACJ,EAAO,CACzB,aAAc,GAAU,UACxB,GAAG,CACL,CAAC,CACH"}
1
+ {"version":3,"file":"error-handler.cjs","names":[],"sources":["../../src/utils/error-handler.ts"],"sourcesContent":["import { trackEvent } from \"#/services/telemetry\";\nimport type { ErrorClassification } from \"@openhands/typescript-client\";\n\ninterface ErrorDetails {\n source?: string;\n metadata?: Record<string, unknown>;\n classification?: ErrorClassification | null;\n}\n\nconst RESERVED_ERROR_KEYS = new Set([\n \"error_source\",\n \"error_kind\",\n \"error_id\",\n \"error_telemetry\",\n]);\n\nexport function trackError({\n source,\n metadata = {},\n classification,\n}: ErrorDetails) {\n // Reserved outcome fields are derived from `source`/`classification` and\n // must not be overridable through arbitrary caller metadata.\n const extra = Object.fromEntries(\n Object.entries(metadata).filter(([key]) => !RESERVED_ERROR_KEYS.has(key)),\n );\n const kind = classification?.kind || \"unknown\";\n // Promote a caller-provided `eventId` to the reserved `error_id` dimension as\n // a fallback so unclassified errors stay correlatable with server-side logs.\n const promotedEventId =\n typeof metadata.eventId === \"string\" ? metadata.eventId : undefined;\n if (promotedEventId != null) {\n delete extra.eventId;\n }\n const errorId = classification?.error_id ?? promotedEventId;\n\n void trackEvent(\"error_outcome\", {\n ...extra,\n current_url: window.location.href,\n error_source: source || \"unknown\",\n error_kind: kind,\n // Keep diagnostic errors correlatable without capturing raw messages.\n ...(errorId != null ? { error_id: errorId } : {}),\n error_telemetry:\n kind === \"internal\" || kind === \"unknown\" ? \"diagnostic\" : \"outcome\",\n });\n}\n"],"mappings":"0FASA,IAAM,EAAsB,IAAI,IAAI,CAClC,eACA,aACA,WACA,iBACF,CAAC,EAED,SAAgB,EAAW,CACzB,SACA,WAAW,CAAC,EACZ,kBACe,CAGf,IAAM,EAAQ,OAAO,YACnB,OAAO,QAAQ,CAAQ,EAAE,QAAQ,CAAC,KAAS,CAAC,EAAoB,IAAI,CAAG,CAAC,CAC1E,EACM,EAAO,GAAgB,MAAQ,UAG/B,EACJ,OAAO,EAAS,SAAY,SAAW,EAAS,QAAU,IAAA,GACxD,GAAmB,MACrB,OAAO,EAAM,QAEf,IAAM,EAAU,GAAgB,UAAY,EAE5C,EAAK,WAAW,gBAAiB,CAC/B,GAAG,EACH,YAAa,OAAO,SAAS,KAC7B,aAAc,GAAU,UACxB,WAAY,EAEZ,GAAI,GAAW,KAA+B,CAAC,EAAzB,CAAE,SAAU,CAAQ,EAC1C,gBACE,IAAS,YAAc,IAAS,UAAY,aAAe,SAC/D,CAAC,CACH"}
@@ -1,7 +1,8 @@
1
+ import type { ErrorClassification } from "@openhands/typescript-client";
1
2
  interface ErrorDetails {
2
- message: string;
3
3
  source?: string;
4
4
  metadata?: Record<string, unknown>;
5
+ classification?: ErrorClassification | null;
5
6
  }
6
- export declare function trackError({ message, source, metadata }: ErrorDetails): void;
7
+ export declare function trackError({ source, metadata, classification, }: ErrorDetails): void;
7
8
  export {};
@@ -1,12 +1,25 @@
1
- import { trackException as e } from "../services/telemetry.js";
1
+ import { trackEvent as e } from "../services/telemetry.js";
2
2
  //#region src/utils/error-handler.ts
3
- function t({ message: t, source: n, metadata: r = {} }) {
4
- e(Error(t), {
3
+ var t = new Set([
4
+ "error_source",
5
+ "error_kind",
6
+ "error_id",
7
+ "error_telemetry"
8
+ ]);
9
+ function n({ source: n, metadata: r = {}, classification: i }) {
10
+ let a = Object.fromEntries(Object.entries(r).filter(([e]) => !t.has(e))), o = i?.kind || "unknown", s = typeof r.eventId == "string" ? r.eventId : void 0;
11
+ s != null && delete a.eventId;
12
+ let c = i?.error_id ?? s;
13
+ e("error_outcome", {
14
+ ...a,
15
+ current_url: window.location.href,
5
16
  error_source: n || "unknown",
6
- ...r
17
+ error_kind: o,
18
+ ...c == null ? {} : { error_id: c },
19
+ error_telemetry: o === "internal" || o === "unknown" ? "diagnostic" : "outcome"
7
20
  });
8
21
  }
9
22
  //#endregion
10
- export { t as trackError };
23
+ export { n as trackError };
11
24
 
12
25
  //# sourceMappingURL=error-handler.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.js","names":[],"sources":["../../src/utils/error-handler.ts"],"sourcesContent":["import { trackException } from \"#/services/telemetry\";\n\ninterface ErrorDetails {\n message: string;\n source?: string;\n metadata?: Record<string, unknown>;\n}\n\nexport function trackError({ message, source, metadata = {} }: ErrorDetails) {\n const error = new Error(message);\n void trackException(error, {\n error_source: source || \"unknown\",\n ...metadata,\n });\n}\n"],"mappings":";;AAQA,SAAgB,EAAW,EAAE,YAAS,WAAQ,cAAW,CAAC,KAAmB;CAE3E,EADkB,MAAM,CACJ,GAAO;EACzB,cAAc,KAAU;EACxB,GAAG;CACL,CAAC;AACH"}
1
+ {"version":3,"file":"error-handler.js","names":[],"sources":["../../src/utils/error-handler.ts"],"sourcesContent":["import { trackEvent } from \"#/services/telemetry\";\nimport type { ErrorClassification } from \"@openhands/typescript-client\";\n\ninterface ErrorDetails {\n source?: string;\n metadata?: Record<string, unknown>;\n classification?: ErrorClassification | null;\n}\n\nconst RESERVED_ERROR_KEYS = new Set([\n \"error_source\",\n \"error_kind\",\n \"error_id\",\n \"error_telemetry\",\n]);\n\nexport function trackError({\n source,\n metadata = {},\n classification,\n}: ErrorDetails) {\n // Reserved outcome fields are derived from `source`/`classification` and\n // must not be overridable through arbitrary caller metadata.\n const extra = Object.fromEntries(\n Object.entries(metadata).filter(([key]) => !RESERVED_ERROR_KEYS.has(key)),\n );\n const kind = classification?.kind || \"unknown\";\n // Promote a caller-provided `eventId` to the reserved `error_id` dimension as\n // a fallback so unclassified errors stay correlatable with server-side logs.\n const promotedEventId =\n typeof metadata.eventId === \"string\" ? metadata.eventId : undefined;\n if (promotedEventId != null) {\n delete extra.eventId;\n }\n const errorId = classification?.error_id ?? promotedEventId;\n\n void trackEvent(\"error_outcome\", {\n ...extra,\n current_url: window.location.href,\n error_source: source || \"unknown\",\n error_kind: kind,\n // Keep diagnostic errors correlatable without capturing raw messages.\n ...(errorId != null ? { error_id: errorId } : {}),\n error_telemetry:\n kind === \"internal\" || kind === \"unknown\" ? \"diagnostic\" : \"outcome\",\n });\n}\n"],"mappings":";;AASA,IAAM,IAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;AACF,CAAC;AAED,SAAgB,EAAW,EACzB,WACA,cAAW,CAAC,GACZ,qBACe;CAGf,IAAM,IAAQ,OAAO,YACnB,OAAO,QAAQ,CAAQ,EAAE,QAAQ,CAAC,OAAS,CAAC,EAAoB,IAAI,CAAG,CAAC,CAC1E,GACM,IAAO,GAAgB,QAAQ,WAG/B,IACJ,OAAO,EAAS,WAAY,WAAW,EAAS,UAAU,KAAA;CAC5D,AAAI,KAAmB,QACrB,OAAO,EAAM;CAEf,IAAM,IAAU,GAAgB,YAAY;CAE5C,EAAgB,iBAAiB;EAC/B,GAAG;EACH,aAAa,OAAO,SAAS;EAC7B,cAAc,KAAU;EACxB,YAAY;EAEZ,GAAI,KAAW,OAA+B,CAAC,IAAzB,EAAE,UAAU,EAAQ;EAC1C,iBACE,MAAS,cAAc,MAAS,YAAY,eAAe;CAC/D,CAAC;AACH"}
@@ -1,2 +1,2 @@
1
- require("../_virtual/_rolldown/runtime.cjs");var e=`Free`,t={"openhands/glm-5.2":`OpenHands GLM-5.2 (free)`,"openhands/deepseek-v4-flash":`OpenHands DeepSeek V4 Flash (free)`,"openhands/minimax-m2.7":`OpenHands MiniMax M2.7 (free)`},n=`Free OpenHands models: ${Object.keys(t).join(`, `)}. Other provider endpoints with similar model names may require separate billing.`,r=e=>!!(e&&e in t);function i(e){return e?r(e)?t[e]:e:null}function a(e){return e?r(e)?t[e]:e.split(`/`).pop()||e:null}exports.FREE_MODEL_BADGE_LABEL=e,exports.FREE_OPENHANDS_MODEL_NOTE=n,exports.formatModelNameForDisplay=i,exports.formatNativeModelName=a,exports.isFreeOpenHandsModel=r;
1
+ require("../_virtual/_rolldown/runtime.cjs");var e=`Free`,t={"openhands/kimi-k3":`OpenHands Kimi K3 (free)`,"openhands/glm-5.2":`OpenHands GLM-5.2 (free)`,"openhands/deepseek-v4-flash":`OpenHands DeepSeek V4 Flash (free)`,"openhands/minimax-m2.7":`OpenHands MiniMax M2.7 (free)`},n=`Free OpenHands models: ${Object.keys(t).join(`, `)}. Other provider endpoints with similar model names may require separate billing.`,r=e=>!!(e&&e in t);function i(e){return e?r(e)?t[e]:e:null}function a(e){return e?r(e)?t[e]:e.split(`/`).pop()||e:null}exports.FREE_MODEL_BADGE_LABEL=e,exports.FREE_OPENHANDS_MODEL_NOTE=n,exports.formatModelNameForDisplay=i,exports.formatNativeModelName=a,exports.isFreeOpenHandsModel=r;
2
2
  //# sourceMappingURL=format-model-name.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"format-model-name.cjs","names":[],"sources":["../../src/utils/format-model-name.ts"],"sourcesContent":["export const FREE_MODEL_BADGE_LABEL = \"Free\";\n\nexport const FREE_OPENHANDS_MODELS = {\n \"openhands/glm-5.2\": \"OpenHands GLM-5.2 (free)\",\n \"openhands/deepseek-v4-flash\": \"OpenHands DeepSeek V4 Flash (free)\",\n \"openhands/minimax-m2.7\": \"OpenHands MiniMax M2.7 (free)\",\n} as const;\n\nexport const FREE_OPENHANDS_MODEL_IDS = Object.keys(FREE_OPENHANDS_MODELS);\nexport const FREE_OPENHANDS_MODEL_NOTE = `Free OpenHands models: ${FREE_OPENHANDS_MODEL_IDS.join(\n \", \",\n)}. Other provider endpoints with similar model names may require separate billing.`;\n\nexport const isFreeOpenHandsModel = (\n model: string | null | undefined,\n): model is keyof typeof FREE_OPENHANDS_MODELS =>\n Boolean(model && model in FREE_OPENHANDS_MODELS);\n\nexport function formatModelNameForDisplay(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n return isFreeOpenHandsModel(model) ? FREE_OPENHANDS_MODELS[model] : model;\n}\n\nexport function formatProviderModelNameForDisplay(\n provider: string | null | undefined,\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n const fullModel = provider ? `${provider}/${model}` : model;\n return isFreeOpenHandsModel(fullModel)\n ? FREE_OPENHANDS_MODELS[fullModel]\n : model;\n}\n\n/**\n * Format a native (OpenHands-kind) routing model string for display, stripping\n * the provider route prefix (e.g. ``\"anthropic/claude-sonnet-4-5-20250929\"`` →\n * ``\"claude-sonnet-4-5-20250929\"``, ``\"litellm_proxy/openai/gpt-4o\"`` →\n * ``\"gpt-4o\"``) so a conversation chip shows a meaningful model name rather than\n * the full routing path.\n *\n * Returns ``null`` for an empty/nullish input, and falls back to the original\n * string when stripping the prefix would leave nothing (e.g. a trailing slash)\n * — never an empty string, which would collapse the chip text.\n *\n * Display-only: unlike {@link deriveProfileNameFromModel} this does not sanitize\n * to an identifier, so it keeps the real model id intact for the chip.\n */\nexport function formatNativeModelName(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n if (isFreeOpenHandsModel(model)) return FREE_OPENHANDS_MODELS[model];\n const lastSegment = model.split(\"/\").pop();\n return lastSegment || model;\n}\n"],"mappings":"6CAAA,IAAa,EAAyB,OAEzB,EAAwB,CACnC,oBAAqB,2BACrB,8BAA+B,qCAC/B,yBAA0B,+BAC5B,EAGa,EAA4B,0BADD,OAAO,KAAK,CACe,EAAyB,KAC1F,IACF,EAAE,mFAEW,EACX,GAEA,GAAQ,GAAS,KAAS,GAE5B,SAAgB,EACd,EACe,CAEf,OADK,EACE,EAAqB,CAAK,EAAI,EAAsB,GAAS,EADjD,IAErB,CA2BA,SAAgB,EACd,EACe,CAIf,OAHK,EACD,EAAqB,CAAK,EAAU,EAAsB,GAC1C,EAAM,MAAM,GAAG,EAAE,IAC9B,GAAe,EAHH,IAIrB"}
1
+ {"version":3,"file":"format-model-name.cjs","names":[],"sources":["../../src/utils/format-model-name.ts"],"sourcesContent":["export const FREE_MODEL_BADGE_LABEL = \"Free\";\n\nexport const FREE_OPENHANDS_MODELS = {\n \"openhands/kimi-k3\": \"OpenHands Kimi K3 (free)\",\n \"openhands/glm-5.2\": \"OpenHands GLM-5.2 (free)\",\n \"openhands/deepseek-v4-flash\": \"OpenHands DeepSeek V4 Flash (free)\",\n \"openhands/minimax-m2.7\": \"OpenHands MiniMax M2.7 (free)\",\n} as const;\n\nexport const FREE_OPENHANDS_MODEL_IDS = Object.keys(FREE_OPENHANDS_MODELS);\nexport const FREE_OPENHANDS_MODEL_NOTE = `Free OpenHands models: ${FREE_OPENHANDS_MODEL_IDS.join(\n \", \",\n)}. Other provider endpoints with similar model names may require separate billing.`;\n\nexport const isFreeOpenHandsModel = (\n model: string | null | undefined,\n): model is keyof typeof FREE_OPENHANDS_MODELS =>\n Boolean(model && model in FREE_OPENHANDS_MODELS);\n\nexport function formatModelNameForDisplay(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n return isFreeOpenHandsModel(model) ? FREE_OPENHANDS_MODELS[model] : model;\n}\n\nexport function formatProviderModelNameForDisplay(\n provider: string | null | undefined,\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n const fullModel = provider ? `${provider}/${model}` : model;\n return isFreeOpenHandsModel(fullModel)\n ? FREE_OPENHANDS_MODELS[fullModel]\n : model;\n}\n\n/**\n * Format a native (OpenHands-kind) routing model string for display, stripping\n * the provider route prefix (e.g. ``\"anthropic/claude-sonnet-4-5-20250929\"`` →\n * ``\"claude-sonnet-4-5-20250929\"``, ``\"litellm_proxy/openai/gpt-4o\"`` →\n * ``\"gpt-4o\"``) so a conversation chip shows a meaningful model name rather than\n * the full routing path.\n *\n * Returns ``null`` for an empty/nullish input, and falls back to the original\n * string when stripping the prefix would leave nothing (e.g. a trailing slash)\n * — never an empty string, which would collapse the chip text.\n *\n * Display-only: unlike {@link deriveProfileNameFromModel} this does not sanitize\n * to an identifier, so it keeps the real model id intact for the chip.\n */\nexport function formatNativeModelName(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n if (isFreeOpenHandsModel(model)) return FREE_OPENHANDS_MODELS[model];\n const lastSegment = model.split(\"/\").pop();\n return lastSegment || model;\n}\n"],"mappings":"6CAAA,IAAa,EAAyB,OAEzB,EAAwB,CACnC,oBAAqB,2BACrB,oBAAqB,2BACrB,8BAA+B,qCAC/B,yBAA0B,+BAC5B,EAGa,EAA4B,0BADD,OAAO,KAAK,CACe,EAAyB,KAC1F,IACF,EAAE,mFAEW,EACX,GAEA,GAAQ,GAAS,KAAS,GAE5B,SAAgB,EACd,EACe,CAEf,OADK,EACE,EAAqB,CAAK,EAAI,EAAsB,GAAS,EADjD,IAErB,CA2BA,SAAgB,EACd,EACe,CAIf,OAHK,EACD,EAAqB,CAAK,EAAU,EAAsB,GAC1C,EAAM,MAAM,GAAG,EAAE,IAC9B,GAAe,EAHH,IAIrB"}
@@ -1,5 +1,6 @@
1
1
  export declare const FREE_MODEL_BADGE_LABEL = "Free";
2
2
  export declare const FREE_OPENHANDS_MODELS: {
3
+ readonly "openhands/kimi-k3": "OpenHands Kimi K3 (free)";
3
4
  readonly "openhands/glm-5.2": "OpenHands GLM-5.2 (free)";
4
5
  readonly "openhands/deepseek-v4-flash": "OpenHands DeepSeek V4 Flash (free)";
5
6
  readonly "openhands/minimax-m2.7": "OpenHands MiniMax M2.7 (free)";
@@ -1,5 +1,6 @@
1
1
  //#region src/utils/format-model-name.ts
2
2
  var e = "Free", t = {
3
+ "openhands/kimi-k3": "OpenHands Kimi K3 (free)",
3
4
  "openhands/glm-5.2": "OpenHands GLM-5.2 (free)",
4
5
  "openhands/deepseek-v4-flash": "OpenHands DeepSeek V4 Flash (free)",
5
6
  "openhands/minimax-m2.7": "OpenHands MiniMax M2.7 (free)"
@@ -1 +1 @@
1
- {"version":3,"file":"format-model-name.js","names":[],"sources":["../../src/utils/format-model-name.ts"],"sourcesContent":["export const FREE_MODEL_BADGE_LABEL = \"Free\";\n\nexport const FREE_OPENHANDS_MODELS = {\n \"openhands/glm-5.2\": \"OpenHands GLM-5.2 (free)\",\n \"openhands/deepseek-v4-flash\": \"OpenHands DeepSeek V4 Flash (free)\",\n \"openhands/minimax-m2.7\": \"OpenHands MiniMax M2.7 (free)\",\n} as const;\n\nexport const FREE_OPENHANDS_MODEL_IDS = Object.keys(FREE_OPENHANDS_MODELS);\nexport const FREE_OPENHANDS_MODEL_NOTE = `Free OpenHands models: ${FREE_OPENHANDS_MODEL_IDS.join(\n \", \",\n)}. Other provider endpoints with similar model names may require separate billing.`;\n\nexport const isFreeOpenHandsModel = (\n model: string | null | undefined,\n): model is keyof typeof FREE_OPENHANDS_MODELS =>\n Boolean(model && model in FREE_OPENHANDS_MODELS);\n\nexport function formatModelNameForDisplay(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n return isFreeOpenHandsModel(model) ? FREE_OPENHANDS_MODELS[model] : model;\n}\n\nexport function formatProviderModelNameForDisplay(\n provider: string | null | undefined,\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n const fullModel = provider ? `${provider}/${model}` : model;\n return isFreeOpenHandsModel(fullModel)\n ? FREE_OPENHANDS_MODELS[fullModel]\n : model;\n}\n\n/**\n * Format a native (OpenHands-kind) routing model string for display, stripping\n * the provider route prefix (e.g. ``\"anthropic/claude-sonnet-4-5-20250929\"`` →\n * ``\"claude-sonnet-4-5-20250929\"``, ``\"litellm_proxy/openai/gpt-4o\"`` →\n * ``\"gpt-4o\"``) so a conversation chip shows a meaningful model name rather than\n * the full routing path.\n *\n * Returns ``null`` for an empty/nullish input, and falls back to the original\n * string when stripping the prefix would leave nothing (e.g. a trailing slash)\n * — never an empty string, which would collapse the chip text.\n *\n * Display-only: unlike {@link deriveProfileNameFromModel} this does not sanitize\n * to an identifier, so it keeps the real model id intact for the chip.\n */\nexport function formatNativeModelName(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n if (isFreeOpenHandsModel(model)) return FREE_OPENHANDS_MODELS[model];\n const lastSegment = model.split(\"/\").pop();\n return lastSegment || model;\n}\n"],"mappings":";AAAA,IAAa,IAAyB,QAEzB,IAAwB;CACnC,qBAAqB;CACrB,+BAA+B;CAC/B,0BAA0B;AAC5B,GAGa,IAA4B,0BADD,OAAO,KAAK,CACe,EAAyB,KAC1F,IACF,EAAE,oFAEW,KACX,MAEA,GAAQ,KAAS,KAAS;AAE5B,SAAgB,EACd,GACe;CAEf,OADK,IACE,EAAqB,CAAK,IAAI,EAAsB,KAAS,IADjD;AAErB;AA2BA,SAAgB,EACd,GACe;CAIf,OAHK,IACD,EAAqB,CAAK,IAAU,EAAsB,KAC1C,EAAM,MAAM,GAAG,EAAE,IAC9B,KAAe,IAHH;AAIrB"}
1
+ {"version":3,"file":"format-model-name.js","names":[],"sources":["../../src/utils/format-model-name.ts"],"sourcesContent":["export const FREE_MODEL_BADGE_LABEL = \"Free\";\n\nexport const FREE_OPENHANDS_MODELS = {\n \"openhands/kimi-k3\": \"OpenHands Kimi K3 (free)\",\n \"openhands/glm-5.2\": \"OpenHands GLM-5.2 (free)\",\n \"openhands/deepseek-v4-flash\": \"OpenHands DeepSeek V4 Flash (free)\",\n \"openhands/minimax-m2.7\": \"OpenHands MiniMax M2.7 (free)\",\n} as const;\n\nexport const FREE_OPENHANDS_MODEL_IDS = Object.keys(FREE_OPENHANDS_MODELS);\nexport const FREE_OPENHANDS_MODEL_NOTE = `Free OpenHands models: ${FREE_OPENHANDS_MODEL_IDS.join(\n \", \",\n)}. Other provider endpoints with similar model names may require separate billing.`;\n\nexport const isFreeOpenHandsModel = (\n model: string | null | undefined,\n): model is keyof typeof FREE_OPENHANDS_MODELS =>\n Boolean(model && model in FREE_OPENHANDS_MODELS);\n\nexport function formatModelNameForDisplay(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n return isFreeOpenHandsModel(model) ? FREE_OPENHANDS_MODELS[model] : model;\n}\n\nexport function formatProviderModelNameForDisplay(\n provider: string | null | undefined,\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n const fullModel = provider ? `${provider}/${model}` : model;\n return isFreeOpenHandsModel(fullModel)\n ? FREE_OPENHANDS_MODELS[fullModel]\n : model;\n}\n\n/**\n * Format a native (OpenHands-kind) routing model string for display, stripping\n * the provider route prefix (e.g. ``\"anthropic/claude-sonnet-4-5-20250929\"`` →\n * ``\"claude-sonnet-4-5-20250929\"``, ``\"litellm_proxy/openai/gpt-4o\"`` →\n * ``\"gpt-4o\"``) so a conversation chip shows a meaningful model name rather than\n * the full routing path.\n *\n * Returns ``null`` for an empty/nullish input, and falls back to the original\n * string when stripping the prefix would leave nothing (e.g. a trailing slash)\n * — never an empty string, which would collapse the chip text.\n *\n * Display-only: unlike {@link deriveProfileNameFromModel} this does not sanitize\n * to an identifier, so it keeps the real model id intact for the chip.\n */\nexport function formatNativeModelName(\n model: string | null | undefined,\n): string | null {\n if (!model) return null;\n if (isFreeOpenHandsModel(model)) return FREE_OPENHANDS_MODELS[model];\n const lastSegment = model.split(\"/\").pop();\n return lastSegment || model;\n}\n"],"mappings":";AAAA,IAAa,IAAyB,QAEzB,IAAwB;CACnC,qBAAqB;CACrB,qBAAqB;CACrB,+BAA+B;CAC/B,0BAA0B;AAC5B,GAGa,IAA4B,0BADD,OAAO,KAAK,CACe,EAAyB,KAC1F,IACF,EAAE,oFAEW,KACX,MAEA,GAAQ,KAAS,KAAS;AAE5B,SAAgB,EACd,GACe;CAEf,OADK,IACE,EAAqB,CAAK,IAAI,EAAsB,KAAS,IADjD;AAErB;AA2BA,SAAgB,EACd,GACe;CAIf,OAHK,IACD,EAAqB,CAAK,IAAU,EAAsB,KAC1C,EAAM,MAAM,GAAG,EAAE,IAC9B,KAAe,IAHH;AAIrB"}
@@ -1,2 +1,2 @@
1
- require("../../_virtual/_rolldown/runtime.cjs");var e=(e,t)=>(e.timestamp??``).localeCompare(t.timestamp??``),t=async(t,n,r)=>{let i=[],a=new Set,o=new Set,s,c,l=!1,u=!1;for(;;){let e=await n({limit:100,sortOrder:`TIMESTAMP_DESC`,strictPagination:!0,...c?{pageId:c}:{},...s?{timestampLt:s}:{}});if(!Array.isArray(e.items))throw Error(`Invalid transcript history response: expected page.items to be an array.`);i.push(...e.items);let t,d=!1;if(e.items.forEach(e=>{a.has(e.id??``)||(e.id&&a.add(e.id),d=!0),(!t||(e.timestamp??``)<t)&&(t=e.timestamp??``)}),e.next_page_id){if(o.has(e.next_page_id))throw Error(`Transcript history pagination repeated a page cursor.`);o.add(e.next_page_id),c=e.next_page_id,s=void 0,l=!0;continue}if(l&&!u||e.items.length<100)break;if(r===void 0)throw Error(`Transcript history pagination cannot prove that all events were loaded.`);if(!t||s&&(!d||t>=s))throw Error(`Transcript history pagination did not advance.`);c=void 0,s=t,u=!0}let d=new Map;i.slice().reverse().forEach(e=>{d.has(e.id??``)||e.id&&d.set(e.id,e)}),t.forEach(e=>{d.has(e.id??``)||e.id&&d.set(e.id,e)});let f=[...d.values()].sort(e);if(r!==void 0&&a.size<r)throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size}.`);return f};exports.loadCompleteTranscriptEvents=t;
1
+ require("../../_virtual/_rolldown/runtime.cjs");var e=(e,t)=>(e.timestamp??``).localeCompare(t.timestamp??``),t=async(t,n,r)=>{let i=[],a=new Set,o=[],s=0,c=new Set,l,u,d=!1,f=!1;for(;;){let e=await n({limit:100,sortOrder:`TIMESTAMP_DESC`,strictPagination:!0,...u?{pageId:u}:{},...l?{timestampLt:l}:{}});if(!Array.isArray(e.items))throw Error(`Invalid transcript history response: expected page.items to be an array.`);i.push(...e.items);let t,o=!1;if(e.items.forEach(e=>{a.has(e.id??``)||(e.id?a.add(e.id):s+=1,o=!0),(!t||(e.timestamp??``)<t)&&(t=e.timestamp??``)}),e.next_page_id){if(c.has(e.next_page_id))throw Error(`Transcript history pagination repeated a page cursor.`);c.add(e.next_page_id),u=e.next_page_id,l=void 0,d=!0;continue}if(d&&!f||e.items.length<100)break;if(r===void 0)throw Error(`Transcript history pagination cannot prove that all events were loaded.`);if(!t||l&&(!o||t>=l))throw Error(`Transcript history pagination did not advance.`);u=void 0,l=t,f=!0}let p=new Map;i.slice().reverse().forEach(e=>{if(e.id!==void 0){p.has(e.id)||p.set(e.id,e);return}o.push(e)}),t.forEach(e=>{if(e.id!==void 0){p.has(e.id)||p.set(e.id,e);return}o.push(e)});let m=[...o,...p.values()].sort(e);if(r!==void 0&&a.size+s<r)throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size+s}.`);return m};exports.loadCompleteTranscriptEvents=t;
2
2
  //# sourceMappingURL=load-complete-events.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"load-complete-events.cjs","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => (first.timestamp ?? \"\").localeCompare(second.timestamp ?? \"\");\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id ?? \"\")) {\n if (event.id) fetchedEventIds.add(event.id);\n addedEvent = true;\n }\n if (\n !pageOldestTimestamp ||\n (event.timestamp ?? \"\") < pageOldestTimestamp\n ) {\n pageOldestTimestamp = event.timestamp ?? \"\";\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (!eventsById.has(event.id ?? \"\"))\n if (event.id) eventsById.set(event.id, event);\n });\n loadedEvents.forEach((event) => {\n if (!eventsById.has(event.id ?? \"\"))\n if (event.id) eventsById.set(event.id, event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...eventsById.values()].sort(compareEventTimestamps);\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"gDAqBA,IAAM,GACJ,EACA,KACY,EAAM,WAAa,IAAI,cAAc,EAAO,WAAa,EAAE,EAQ5D,EAA+B,MAC1C,EACA,EACA,IAC8B,CAC9B,IAAM,EAAwC,CAAC,EACzC,EAAkB,IAAI,IACtB,EAAc,IAAI,IACpB,EACA,EACA,EAAa,GACb,EAAyB,GAE7B,OAAa,CACX,IAAM,EAAO,MAAM,EAAa,CAC9B,MAAA,IACA,UAAW,iBACX,iBAAkB,GAClB,GAAI,EAAS,CAAE,QAAO,EAAI,CAAC,EAC3B,GAAI,EAAkB,CAAE,YAAa,CAAgB,EAAI,CAAC,CAC5D,CAAC,EAED,GAAI,CAAC,MAAM,QAAQ,EAAK,KAAK,EAC3B,MAAU,MACR,0EACF,EAGF,EAAoB,KAAK,GAAG,EAAK,KAAK,EACtC,IAAI,EACA,EAAa,GAcjB,GAbA,EAAK,MAAM,QAAS,GAAU,CACvB,EAAgB,IAAI,EAAM,IAAM,EAAE,IACjC,EAAM,IAAI,EAAgB,IAAI,EAAM,EAAE,EAC1C,EAAa,KAGb,CAAC,IACA,EAAM,WAAa,IAAM,KAE1B,EAAsB,EAAM,WAAa,GAE7C,CAAC,EAEG,EAAK,aAAc,CACrB,GAAI,EAAY,IAAI,EAAK,YAAY,EACnC,MAAU,MACR,uDACF,EAEF,EAAY,IAAI,EAAK,YAAY,EACjC,EAAS,EAAK,aACd,EAAkB,IAAA,GAClB,EAAa,GACb,QACF,CAKA,GADI,GAAc,CAAC,GACf,EAAK,MAAM,OAAA,IAAuC,MAKtD,GAAI,IAAuB,IAAA,GACzB,MAAU,MACR,yEACF,EAKF,GAHI,CAAC,GAIH,IACC,CAAC,GAAc,GAAuB,GAEvC,MAAU,MAAM,gDAAgD,EAGlE,EAAS,IAAA,GACT,EAAkB,EAClB,EAAyB,EAC3B,CAEA,IAAM,EAAa,IAAI,IACvB,EACG,MAAM,EACN,QAAQ,EACR,QAAS,GAAU,CACb,EAAW,IAAI,EAAM,IAAM,EAAE,GAC5B,EAAM,IAAI,EAAW,IAAI,EAAM,GAAI,CAAK,CAChD,CAAC,EACH,EAAa,QAAS,GAAU,CACzB,EAAW,IAAI,EAAM,IAAM,EAAE,GAC5B,EAAM,IAAI,EAAW,IAAI,EAAM,GAAI,CAAK,CAChD,CAAC,EAGD,IAAM,EAAiB,CAAC,GAAG,EAAW,OAAO,CAAC,EAAE,KAAK,CAAsB,EAC3E,GACE,IAAuB,IAAA,IACvB,EAAgB,KAAO,EAEvB,MAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,KAAK,EACtH,EAEF,OAAO,CACT"}
1
+ {"version":3,"file":"load-complete-events.cjs","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => (first.timestamp ?? \"\").localeCompare(second.timestamp ?? \"\");\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n // Id-less events can't be de-duplicated by id, so keep a separate ordered\n // list and a count so they survive the merge and count toward completeness.\n const idLessEvents: OpenHandsEvent[] = [];\n let idLessPersisted = 0;\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id ?? \"\")) {\n if (event.id) {\n fetchedEventIds.add(event.id);\n } else {\n idLessPersisted += 1;\n }\n addedEvent = true;\n }\n if (\n !pageOldestTimestamp ||\n (event.timestamp ?? \"\") < pageOldestTimestamp\n ) {\n pageOldestTimestamp = event.timestamp ?? \"\";\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (event.id !== undefined) {\n if (!eventsById.has(event.id)) {\n eventsById.set(event.id, event);\n }\n return;\n }\n idLessEvents.push(event);\n });\n loadedEvents.forEach((event) => {\n if (event.id !== undefined) {\n if (!eventsById.has(event.id)) {\n eventsById.set(event.id, event);\n }\n return;\n }\n idLessEvents.push(event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...idLessEvents, ...eventsById.values()].sort(\n compareEventTimestamps,\n );\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size + idLessPersisted < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size + idLessPersisted}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"gDAqBA,IAAM,GACJ,EACA,KACY,EAAM,WAAa,IAAI,cAAc,EAAO,WAAa,EAAE,EAQ5D,EAA+B,MAC1C,EACA,EACA,IAC8B,CAC9B,IAAM,EAAwC,CAAC,EACzC,EAAkB,IAAI,IAGtB,EAAiC,CAAC,EACpC,EAAkB,EAChB,EAAc,IAAI,IACpB,EACA,EACA,EAAa,GACb,EAAyB,GAE7B,OAAa,CACX,IAAM,EAAO,MAAM,EAAa,CAC9B,MAAA,IACA,UAAW,iBACX,iBAAkB,GAClB,GAAI,EAAS,CAAE,QAAO,EAAI,CAAC,EAC3B,GAAI,EAAkB,CAAE,YAAa,CAAgB,EAAI,CAAC,CAC5D,CAAC,EAED,GAAI,CAAC,MAAM,QAAQ,EAAK,KAAK,EAC3B,MAAU,MACR,0EACF,EAGF,EAAoB,KAAK,GAAG,EAAK,KAAK,EACtC,IAAI,EACA,EAAa,GAkBjB,GAjBA,EAAK,MAAM,QAAS,GAAU,CACvB,EAAgB,IAAI,EAAM,IAAM,EAAE,IACjC,EAAM,GACR,EAAgB,IAAI,EAAM,EAAE,EAE5B,GAAmB,EAErB,EAAa,KAGb,CAAC,IACA,EAAM,WAAa,IAAM,KAE1B,EAAsB,EAAM,WAAa,GAE7C,CAAC,EAEG,EAAK,aAAc,CACrB,GAAI,EAAY,IAAI,EAAK,YAAY,EACnC,MAAU,MACR,uDACF,EAEF,EAAY,IAAI,EAAK,YAAY,EACjC,EAAS,EAAK,aACd,EAAkB,IAAA,GAClB,EAAa,GACb,QACF,CAKA,GADI,GAAc,CAAC,GACf,EAAK,MAAM,OAAA,IAAuC,MAKtD,GAAI,IAAuB,IAAA,GACzB,MAAU,MACR,yEACF,EAKF,GAHI,CAAC,GAIH,IACC,CAAC,GAAc,GAAuB,GAEvC,MAAU,MAAM,gDAAgD,EAGlE,EAAS,IAAA,GACT,EAAkB,EAClB,EAAyB,EAC3B,CAEA,IAAM,EAAa,IAAI,IACvB,EACG,MAAM,EACN,QAAQ,EACR,QAAS,GAAU,CAClB,GAAI,EAAM,KAAO,IAAA,GAAW,CACrB,EAAW,IAAI,EAAM,EAAE,GAC1B,EAAW,IAAI,EAAM,GAAI,CAAK,EAEhC,MACF,CACA,EAAa,KAAK,CAAK,CACzB,CAAC,EACH,EAAa,QAAS,GAAU,CAC9B,GAAI,EAAM,KAAO,IAAA,GAAW,CACrB,EAAW,IAAI,EAAM,EAAE,GAC1B,EAAW,IAAI,EAAM,GAAI,CAAK,EAEhC,MACF,CACA,EAAa,KAAK,CAAK,CACzB,CAAC,EAGD,IAAM,EAAiB,CAAC,GAAG,EAAc,GAAG,EAAW,OAAO,CAAC,EAAE,KAC/D,CACF,EACA,GACE,IAAuB,IAAA,IACvB,EAAgB,KAAO,EAAkB,EAEzC,MAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,KAAO,EAAgB,EACxI,EAEF,OAAO,CACT"}
@@ -1,37 +1,45 @@
1
1
  var e = (e, t) => (e.timestamp ?? "").localeCompare(t.timestamp ?? ""), t = async (t, n, r) => {
2
- let i = [], a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set(), s, c, l = !1, u = !1;
2
+ let i = [], a = /* @__PURE__ */ new Set(), o = [], s = 0, c = /* @__PURE__ */ new Set(), l, u, d = !1, f = !1;
3
3
  for (;;) {
4
4
  let e = await n({
5
5
  limit: 100,
6
6
  sortOrder: "TIMESTAMP_DESC",
7
7
  strictPagination: !0,
8
- ...c ? { pageId: c } : {},
9
- ...s ? { timestampLt: s } : {}
8
+ ...u ? { pageId: u } : {},
9
+ ...l ? { timestampLt: l } : {}
10
10
  });
11
11
  if (!Array.isArray(e.items)) throw Error("Invalid transcript history response: expected page.items to be an array.");
12
12
  i.push(...e.items);
13
- let t, d = !1;
13
+ let t, o = !1;
14
14
  if (e.items.forEach((e) => {
15
- a.has(e.id ?? "") || (e.id && a.add(e.id), d = !0), (!t || (e.timestamp ?? "") < t) && (t = e.timestamp ?? "");
15
+ a.has(e.id ?? "") || (e.id ? a.add(e.id) : s += 1, o = !0), (!t || (e.timestamp ?? "") < t) && (t = e.timestamp ?? "");
16
16
  }), e.next_page_id) {
17
- if (o.has(e.next_page_id)) throw Error("Transcript history pagination repeated a page cursor.");
18
- o.add(e.next_page_id), c = e.next_page_id, s = void 0, l = !0;
17
+ if (c.has(e.next_page_id)) throw Error("Transcript history pagination repeated a page cursor.");
18
+ c.add(e.next_page_id), u = e.next_page_id, l = void 0, d = !0;
19
19
  continue;
20
20
  }
21
- if (l && !u || e.items.length < 100) break;
21
+ if (d && !f || e.items.length < 100) break;
22
22
  if (r === void 0) throw Error("Transcript history pagination cannot prove that all events were loaded.");
23
- if (!t || s && (!d || t >= s)) throw Error("Transcript history pagination did not advance.");
24
- c = void 0, s = t, u = !0;
23
+ if (!t || l && (!o || t >= l)) throw Error("Transcript history pagination did not advance.");
24
+ u = void 0, l = t, f = !0;
25
25
  }
26
- let d = /* @__PURE__ */ new Map();
26
+ let p = /* @__PURE__ */ new Map();
27
27
  i.slice().reverse().forEach((e) => {
28
- d.has(e.id ?? "") || e.id && d.set(e.id, e);
28
+ if (e.id !== void 0) {
29
+ p.has(e.id) || p.set(e.id, e);
30
+ return;
31
+ }
32
+ o.push(e);
29
33
  }), t.forEach((e) => {
30
- d.has(e.id ?? "") || e.id && d.set(e.id, e);
34
+ if (e.id !== void 0) {
35
+ p.has(e.id) || p.set(e.id, e);
36
+ return;
37
+ }
38
+ o.push(e);
31
39
  });
32
- let f = [...d.values()].sort(e);
33
- if (r !== void 0 && a.size < r) throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size}.`);
34
- return f;
40
+ let m = [...o, ...p.values()].sort(e);
41
+ if (r !== void 0 && a.size + s < r) throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size + s}.`);
42
+ return m;
35
43
  };
36
44
  //#endregion
37
45
  export { t as loadCompleteTranscriptEvents };
@@ -1 +1 @@
1
- {"version":3,"file":"load-complete-events.js","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => (first.timestamp ?? \"\").localeCompare(second.timestamp ?? \"\");\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id ?? \"\")) {\n if (event.id) fetchedEventIds.add(event.id);\n addedEvent = true;\n }\n if (\n !pageOldestTimestamp ||\n (event.timestamp ?? \"\") < pageOldestTimestamp\n ) {\n pageOldestTimestamp = event.timestamp ?? \"\";\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (!eventsById.has(event.id ?? \"\"))\n if (event.id) eventsById.set(event.id, event);\n });\n loadedEvents.forEach((event) => {\n if (!eventsById.has(event.id ?? \"\"))\n if (event.id) eventsById.set(event.id, event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...eventsById.values()].sort(compareEventTimestamps);\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"AAqBA,IAAM,KACJ,GACA,OACY,EAAM,aAAa,IAAI,cAAc,EAAO,aAAa,EAAE,GAQ5D,IAA+B,OAC1C,GACA,GACA,MAC8B;CAC9B,IAAM,IAAwC,CAAC,GACzC,oBAAkB,IAAI,IAAY,GAClC,oBAAc,IAAI,IAAY,GAChC,GACA,GACA,IAAa,IACb,IAAyB;CAE7B,SAAa;EACX,IAAM,IAAO,MAAM,EAAa;GAC9B,OAAA;GACA,WAAW;GACX,kBAAkB;GAClB,GAAI,IAAS,EAAE,UAAO,IAAI,CAAC;GAC3B,GAAI,IAAkB,EAAE,aAAa,EAAgB,IAAI,CAAC;EAC5D,CAAC;EAED,IAAI,CAAC,MAAM,QAAQ,EAAK,KAAK,GAC3B,MAAU,MACR,0EACF;EAGF,EAAoB,KAAK,GAAG,EAAK,KAAK;EACtC,IAAI,GACA,IAAa;EAcjB,IAbA,EAAK,MAAM,SAAS,MAAU;GAK5B,AAJK,EAAgB,IAAI,EAAM,MAAM,EAAE,MACjC,EAAM,MAAI,EAAgB,IAAI,EAAM,EAAE,GAC1C,IAAa,MAGb,CAAC,MACA,EAAM,aAAa,MAAM,OAE1B,IAAsB,EAAM,aAAa;EAE7C,CAAC,GAEG,EAAK,cAAc;GACrB,IAAI,EAAY,IAAI,EAAK,YAAY,GACnC,MAAU,MACR,uDACF;GAKF,AAHA,EAAY,IAAI,EAAK,YAAY,GACjC,IAAS,EAAK,cACd,IAAkB,KAAA,GAClB,IAAa;GACb;EACF;EAKA,IADI,KAAc,CAAC,KACf,EAAK,MAAM,SAAA,KAAuC;EAKtD,IAAI,MAAuB,KAAA,GACzB,MAAU,MACR,yEACF;EAKF,IAHI,CAAC,KAIH,MACC,CAAC,KAAc,KAAuB,IAEvC,MAAU,MAAM,gDAAgD;EAKlE,AAFA,IAAS,KAAA,GACT,IAAkB,GAClB,IAAyB;CAC3B;CAEA,IAAM,oBAAa,IAAI,IAA4B;CAQnD,AAPA,EACG,MAAM,EACN,QAAQ,EACR,SAAS,MAAU;EAClB,AAAK,EAAW,IAAI,EAAM,MAAM,EAAE,KAC5B,EAAM,MAAI,EAAW,IAAI,EAAM,IAAI,CAAK;CAChD,CAAC,GACH,EAAa,SAAS,MAAU;EAC9B,AAAK,EAAW,IAAI,EAAM,MAAM,EAAE,KAC5B,EAAM,MAAI,EAAW,IAAI,EAAM,IAAI,CAAK;CAChD,CAAC;CAGD,IAAM,IAAiB,CAAC,GAAG,EAAW,OAAO,CAAC,EAAE,KAAK,CAAsB;CAC3E,IACE,MAAuB,KAAA,KACvB,EAAgB,OAAO,GAEvB,MAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,KAAK,EACtH;CAEF,OAAO;AACT"}
1
+ {"version":3,"file":"load-complete-events.js","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => (first.timestamp ?? \"\").localeCompare(second.timestamp ?? \"\");\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n // Id-less events can't be de-duplicated by id, so keep a separate ordered\n // list and a count so they survive the merge and count toward completeness.\n const idLessEvents: OpenHandsEvent[] = [];\n let idLessPersisted = 0;\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id ?? \"\")) {\n if (event.id) {\n fetchedEventIds.add(event.id);\n } else {\n idLessPersisted += 1;\n }\n addedEvent = true;\n }\n if (\n !pageOldestTimestamp ||\n (event.timestamp ?? \"\") < pageOldestTimestamp\n ) {\n pageOldestTimestamp = event.timestamp ?? \"\";\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (event.id !== undefined) {\n if (!eventsById.has(event.id)) {\n eventsById.set(event.id, event);\n }\n return;\n }\n idLessEvents.push(event);\n });\n loadedEvents.forEach((event) => {\n if (event.id !== undefined) {\n if (!eventsById.has(event.id)) {\n eventsById.set(event.id, event);\n }\n return;\n }\n idLessEvents.push(event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...idLessEvents, ...eventsById.values()].sort(\n compareEventTimestamps,\n );\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size + idLessPersisted < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size + idLessPersisted}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"AAqBA,IAAM,KACJ,GACA,OACY,EAAM,aAAa,IAAI,cAAc,EAAO,aAAa,EAAE,GAQ5D,IAA+B,OAC1C,GACA,GACA,MAC8B;CAC9B,IAAM,IAAwC,CAAC,GACzC,oBAAkB,IAAI,IAAY,GAGlC,IAAiC,CAAC,GACpC,IAAkB,GAChB,oBAAc,IAAI,IAAY,GAChC,GACA,GACA,IAAa,IACb,IAAyB;CAE7B,SAAa;EACX,IAAM,IAAO,MAAM,EAAa;GAC9B,OAAA;GACA,WAAW;GACX,kBAAkB;GAClB,GAAI,IAAS,EAAE,UAAO,IAAI,CAAC;GAC3B,GAAI,IAAkB,EAAE,aAAa,EAAgB,IAAI,CAAC;EAC5D,CAAC;EAED,IAAI,CAAC,MAAM,QAAQ,EAAK,KAAK,GAC3B,MAAU,MACR,0EACF;EAGF,EAAoB,KAAK,GAAG,EAAK,KAAK;EACtC,IAAI,GACA,IAAa;EAkBjB,IAjBA,EAAK,MAAM,SAAS,MAAU;GAS5B,AARK,EAAgB,IAAI,EAAM,MAAM,EAAE,MACjC,EAAM,KACR,EAAgB,IAAI,EAAM,EAAE,IAE5B,KAAmB,GAErB,IAAa,MAGb,CAAC,MACA,EAAM,aAAa,MAAM,OAE1B,IAAsB,EAAM,aAAa;EAE7C,CAAC,GAEG,EAAK,cAAc;GACrB,IAAI,EAAY,IAAI,EAAK,YAAY,GACnC,MAAU,MACR,uDACF;GAKF,AAHA,EAAY,IAAI,EAAK,YAAY,GACjC,IAAS,EAAK,cACd,IAAkB,KAAA,GAClB,IAAa;GACb;EACF;EAKA,IADI,KAAc,CAAC,KACf,EAAK,MAAM,SAAA,KAAuC;EAKtD,IAAI,MAAuB,KAAA,GACzB,MAAU,MACR,yEACF;EAKF,IAHI,CAAC,KAIH,MACC,CAAC,KAAc,KAAuB,IAEvC,MAAU,MAAM,gDAAgD;EAKlE,AAFA,IAAS,KAAA,GACT,IAAkB,GAClB,IAAyB;CAC3B;CAEA,IAAM,oBAAa,IAAI,IAA4B;CAanD,AAZA,EACG,MAAM,EACN,QAAQ,EACR,SAAS,MAAU;EAClB,IAAI,EAAM,OAAO,KAAA,GAAW;GAC1B,AAAK,EAAW,IAAI,EAAM,EAAE,KAC1B,EAAW,IAAI,EAAM,IAAI,CAAK;GAEhC;EACF;EACA,EAAa,KAAK,CAAK;CACzB,CAAC,GACH,EAAa,SAAS,MAAU;EAC9B,IAAI,EAAM,OAAO,KAAA,GAAW;GAC1B,AAAK,EAAW,IAAI,EAAM,EAAE,KAC1B,EAAW,IAAI,EAAM,IAAI,CAAK;GAEhC;EACF;EACA,EAAa,KAAK,CAAK;CACzB,CAAC;CAGD,IAAM,IAAiB,CAAC,GAAG,GAAc,GAAG,EAAW,OAAO,CAAC,EAAE,KAC/D,CACF;CACA,IACE,MAAuB,KAAA,KACvB,EAAgB,OAAO,IAAkB,GAEzC,MAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,OAAO,EAAgB,EACxI;CAEF,OAAO;AACT"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/agent-canvas",
3
- "version": "1.13.0",
3
+ "version": "1.14.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,
@@ -17,6 +17,9 @@ const IDENTICAL_VALUE_ALLOWLIST = new Set([
17
17
  'API$TAVILY_KEY_EXAMPLE',
18
18
  'API$TVLY_KEY_EXAMPLE',
19
19
  'AUTOMATIONS$DOWNLOAD_TARBALL',
20
+ 'AUTOMATIONS$GIT_SYNC$BRANCH_PLACEHOLDER',
21
+ 'AUTOMATIONS$GIT_SYNC$PATH_PLACEHOLDER',
22
+ 'AUTOMATIONS$GIT_SYNC$REPO_URL_PLACEHOLDER',
20
23
  'BACKEND$CLOUD_TITLE',
21
24
  'BACKEND$VERSION_LABEL',
22
25
  'BRANDING$OPENHANDS',
@@ -46,7 +46,7 @@
46
46
 
47
47
  import { spawn, spawnSync } from "node:child_process";
48
48
  import { mkdirSync, existsSync, readFileSync } from "node:fs";
49
- import { join, resolve, dirname } from "node:path";
49
+ import { join, resolve, dirname, isAbsolute } from "node:path";
50
50
  import { fileURLToPath, pathToFileURL } from "node:url";
51
51
  import { homedir } from "node:os";
52
52
  import { setTimeout as delay } from "node:timers/promises";
@@ -252,6 +252,7 @@ ENVIRONMENT VARIABLES:
252
252
  PORT Alternative to --port
253
253
  OH_AUTOMATION_GIT_REF Git ref for automation (overrides default version)
254
254
  OH_AUTOMATION_VERSION Specific PyPI version for automation (default: ${DEFAULT_AUTOMATION_VERSION})
255
+ OH_AUTOMATION_LOCAL_PATH Absolute path to a local automation checkout (overridden only by --automation-git-ref)
255
256
  OH_AGENT_SERVER_LOCAL_PATH Absolute path to a local software-agent-sdk checkout (highest precedence)
256
257
  OH_AGENT_SERVER_GIT_REF Git ref for agent-server SDK (overrides default version)
257
258
  OH_AGENT_SERVER_VERSION Specific PyPI version for agent-server
@@ -270,10 +271,34 @@ ACCESS POINTS:
270
271
  `);
271
272
  }
272
273
 
274
+ /**
275
+ * Fail fast on an unusable OH_AUTOMATION_LOCAL_PATH instead of letting
276
+ * `uv run --project <bad path>` exit on its own -- that leaves the rest of the
277
+ * stack up and the automations UI just reporting "backend unavailable", with
278
+ * nothing pointing at the env var. Mirrors validateLocalAgentServerPath.
279
+ */
280
+ function validateLocalAutomationPath(localPath) {
281
+ if (!isAbsolute(localPath)) {
282
+ throw new Error(
283
+ `OH_AUTOMATION_LOCAL_PATH must be an absolute path, got: ${localPath}`,
284
+ );
285
+ }
286
+ if (!existsSync(localPath)) {
287
+ throw new Error(`OH_AUTOMATION_LOCAL_PATH does not exist: ${localPath}`);
288
+ }
289
+ const projectFile = join(localPath, "pyproject.toml");
290
+ if (!existsSync(projectFile)) {
291
+ throw new Error(
292
+ `OH_AUTOMATION_LOCAL_PATH is not a Python project (no pyproject.toml): ${projectFile}`,
293
+ );
294
+ }
295
+ }
296
+
273
297
  /**
274
298
  * Build the uvx command for running automation backend.
275
299
  *
276
300
  * Environment variables (highest precedence first):
301
+ * - OH_AUTOMATION_LOCAL_PATH: Absolute path to a local checkout
277
302
  * - OH_AUTOMATION_GIT_REF: Git commit SHA or branch name
278
303
  * - OH_AUTOMATION_VERSION: Specific PyPI version (e.g., "1.0.0a1")
279
304
  *
@@ -282,6 +307,7 @@ ACCESS POINTS:
282
307
  * git branch or commit instead.
283
308
  */
284
309
  function buildAutomationCommand(env = process.env) {
310
+ const localPath = env.OH_AUTOMATION_LOCAL_PATH;
285
311
  const gitRef = env.OH_AUTOMATION_GIT_REF;
286
312
  const version = env.OH_AUTOMATION_VERSION;
287
313
  const repoUrl = env.OH_AUTOMATION_REPO || DEFAULT_AUTOMATION_REPO;
@@ -289,6 +315,25 @@ function buildAutomationCommand(env = process.env) {
289
315
  const uvxArgs = [];
290
316
  let source = "";
291
317
 
318
+ if (localPath) {
319
+ // Run straight from a local checkout via `uv run --project`, so
320
+ // uncommitted working-tree changes are picked up. Outranks the other
321
+ // automation env vars, mirroring OH_AGENT_SERVER_LOCAL_PATH for the
322
+ // agent-server SDK; buildConfig drops it when --automation-git-ref asks
323
+ // for a specific ref.
324
+ return {
325
+ command: "uv",
326
+ args: [
327
+ "run",
328
+ "--project",
329
+ localPath,
330
+ "uvicorn",
331
+ "openhands.automation.app:app",
332
+ ],
333
+ source: `local (${localPath})`,
334
+ };
335
+ }
336
+
292
337
  if (gitRef) {
293
338
  // Use git ref - refresh to ensure latest commit is fetched
294
339
  const gitUrl = `git+${repoUrl}@${gitRef}`;
@@ -331,6 +376,17 @@ async function buildConfig(args, env = process.env) {
331
376
  // Apply args to env for buildAutomationCommand
332
377
  if (args.automationGitRef) {
333
378
  env.OH_AUTOMATION_GIT_REF = args.automationGitRef;
379
+ // An explicit flag outranks an ambient env var. Otherwise someone with
380
+ // OH_AUTOMATION_LOCAL_PATH exported in their shell profile would run their
381
+ // own working tree while believing they were reproducing against the ref
382
+ // they just passed.
383
+ if (env.OH_AUTOMATION_LOCAL_PATH) {
384
+ logStep(
385
+ "automation",
386
+ `--automation-git-ref ${args.automationGitRef} overrides OH_AUTOMATION_LOCAL_PATH (${env.OH_AUTOMATION_LOCAL_PATH})`,
387
+ );
388
+ delete env.OH_AUTOMATION_LOCAL_PATH;
389
+ }
334
390
  }
335
391
  if (args.automationRepo) {
336
392
  env.OH_AUTOMATION_REPO = args.automationRepo;
@@ -730,17 +786,34 @@ function getFrontendBackend(config) {
730
786
  }
731
787
 
732
788
  function buildViteBackendEnv(config, env = process.env) {
733
- const backendBaseUrl = config.launchAgentServer
734
- ? `http://127.0.0.1:${config.ingressPort}`
735
- : (env.VITE_BACKEND_BASE_URL ?? "http://127.0.0.1:8000");
789
+ // VITE_BACKEND_HOST tells the Vite dev-server proxy (vite.config.ts) where
790
+ // to forward /api, /sockets, etc. It is NOT read by the frontend at
791
+ // runtime, so it is safe to keep as an absolute address.
792
+ //
793
+ // VITE_BACKEND_BASE_URL is intentionally left unset so the frontend falls
794
+ // back to window.location.origin (same-origin) at runtime — matching the
795
+ // behaviour of dev:static / agent-canvas and keeping the dev server
796
+ // portable across localhost, LAN hosts, SSH tunnels, and ngrok.
736
797
  const backendHost = config.launchAgentServer
737
798
  ? `127.0.0.1:${config.ingressPort}`
738
- : (env.VITE_BACKEND_HOST ?? new URL(backendBaseUrl).host);
799
+ : (env.VITE_BACKEND_HOST ??
800
+ env.VITE_BACKEND_BASE_URL?.replace(/^https?:\/\//, "") ??
801
+ "127.0.0.1:8000");
739
802
 
740
- return {
741
- VITE_BACKEND_HOST: backendHost,
742
- VITE_BACKEND_BASE_URL: backendBaseUrl,
743
- };
803
+ const env_out = { VITE_BACKEND_HOST: backendHost };
804
+
805
+ // If the user supplied VITE_BACKEND_BASE_URL with an https:// scheme and
806
+ // did not explicitly set VITE_USE_TLS, propagate the HTTPS intent so the
807
+ // Vite proxy forwards over TLS instead of plain HTTP.
808
+ if (
809
+ !config.launchAgentServer &&
810
+ env.VITE_BACKEND_BASE_URL?.startsWith("https://") &&
811
+ env.VITE_USE_TLS === undefined
812
+ ) {
813
+ env_out.VITE_USE_TLS = "true";
814
+ }
815
+
816
+ return env_out;
744
817
  }
745
818
 
746
819
  function buildAgentServerAutomationEnv(config) {
@@ -1330,6 +1403,21 @@ async function main(options = {}) {
1330
1403
  }
1331
1404
  }
1332
1405
 
1406
+ // Same for the automation checkout -- skipped when --automation-git-ref was
1407
+ // passed, since buildConfig drops the env var in favor of the explicit flag.
1408
+ if (
1409
+ !args.frontendOnly &&
1410
+ !args.automationGitRef &&
1411
+ process.env.OH_AUTOMATION_LOCAL_PATH
1412
+ ) {
1413
+ try {
1414
+ validateLocalAutomationPath(process.env.OH_AUTOMATION_LOCAL_PATH);
1415
+ } catch (error) {
1416
+ logError(error instanceof Error ? error.message : String(error));
1417
+ process.exit(1);
1418
+ }
1419
+ }
1420
+
1333
1421
  // Build config with dynamic port allocation
1334
1422
  const config = await buildConfig(args);
1335
1423
  if (viteWorkingDir) config.viteWorkingDir = viteWorkingDir;
@@ -1504,6 +1592,7 @@ export {
1504
1592
  registerShutdownHook,
1505
1593
  spawnService,
1506
1594
  commandExists,
1595
+ validateLocalAutomationPath,
1507
1596
  logService,
1508
1597
  logStep,
1509
1598
  logSuccess,