@openhands/agent-canvas 1.10.0 → 1.12.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.
- package/README.md +1 -1
- package/README.windows.md +2 -2
- package/build/assets/{acp-providers-DLe_gVDY.js → acp-providers-BkL3HWa3.js} +1 -1
- package/build/assets/{active-backend-context-B0wI7OzM.js → active-backend-context-B_bg5h9O.js} +1 -1
- package/build/assets/{agent-context-settings-CaQtdNA-.js → agent-context-settings-CzWS1DgR.js} +1 -1
- package/build/assets/{agent-profiles-settings-CvuErjp6.js → agent-profiles-settings-hd2cAIZM.js} +1 -1
- package/build/assets/agent-server-conversation-service.api-BheU4HX3.js +86 -0
- package/build/assets/{agent-settings-UE-U4Xii.js → agent-settings-D2J1dKVd.js} +1 -1
- package/build/assets/agent-settings-D_SVrWhI.js +1 -0
- package/build/assets/{alert-banner-BMaOtTCG.js → alert-banner-lrK-2_0Y.js} +1 -1
- package/build/assets/{api-key-entry-screen-Cy66vT4D.js → api-key-entry-screen-D70oJj2c.js} +1 -1
- package/build/assets/app-settings-D0OKHB3M.js +1 -0
- package/build/assets/{automation-detail-fuITm6i3.js → automation-detail-C0SxWTnQ.js} +3 -3
- package/build/assets/{automation-export-CRq40mux.js → automation-export-BjQa_fT6.js} +1 -1
- package/build/assets/{automation-setup-route-EXDazOHW.js → automation-setup-route-BzCNpljP.js} +1 -1
- package/build/assets/{automation-templates-BArZsaxP.js → automation-templates-D9gs6ihu.js} +1 -1
- package/build/assets/{automations-list-CG4HKOTn.js → automations-list-dZg8zOCl.js} +1 -1
- package/build/assets/{back-nav-button-BZB6H7xz.js → back-nav-button-B3pYwmUJ.js} +1 -1
- package/build/assets/{backend-form-modal-B_lXjOGa.js → backend-form-modal-HwclKrUS.js} +1 -1
- package/build/assets/{backend-synced-settings-badge-B_NXJ3u1.js → backend-synced-settings-badge-YI3k09Wr.js} +1 -1
- package/build/assets/{badge-C64c-7PO.js → badge-Dpn3-LTV.js} +1 -1
- package/build/assets/{base-modal-C0RQPug5.js → base-modal-3NmIOKDm.js} +1 -1
- package/build/assets/{brand-button-DJmFCyMK.js → brand-button-CJcwjnfb.js} +1 -1
- package/build/assets/{browser-D3gd2n83.js → browser--eoQOo4M.js} +1 -1
- package/build/assets/{browser-tab-BicAWuJ1.js → browser-tab-4CPdj3J3.js} +1 -1
- package/build/assets/{chat-send-button-CCrH1E8u.js → chat-send-button-B8n6-DQA.js} +1 -1
- package/build/assets/{circle-plus-check-toggle-CB_sNERY.js → circle-plus-check-toggle-BVzv5EkZ.js} +1 -1
- package/build/assets/{cloud-funnel-analytics-XynamQGU.js → cloud-funnel-analytics-CcSez21r.js} +1 -1
- package/build/assets/{combobox-caret-Bwiifgjs.js → combobox-caret-BlmRwiM8.js} +1 -1
- package/build/assets/command-menu-BKOLj-TC.js +1 -0
- package/build/assets/{condenser-settings-cNGy75gq.js → condenser-settings-DLQogFnB.js} +1 -1
- package/build/assets/{confirmation-modal-BATFmFyP.js → confirmation-modal-DAH0XtOp.js} +1 -1
- package/build/assets/{context-menu-list-item-DhJCcBA0.js → context-menu-list-item-BQhmFVEK.js} +1 -1
- package/build/assets/{conversation-Csdxh9Xt.js → conversation-Cmyw9bFh.js} +4 -4
- package/build/assets/conversation-CvoGZ5vC.js +1 -0
- package/build/assets/conversation-panel-CvoGZ5vC.js +1 -0
- package/build/assets/{conversation-state-store-C2QzhPaK.js → conversation-state-store-CtgILxWF.js} +1 -1
- package/build/assets/{conversation-tab-empty-state-D3fb5xIA.js → conversation-tab-empty-state-C9sfDOlt.js} +1 -1
- package/build/assets/{copy-to-clipboard-button-c4MQwKGj.js → copy-to-clipboard-button-DatoElj8.js} +1 -1
- package/build/assets/{device-verify-oFpQoLgm.js → device-verify-BIjXeW9N.js} +1 -1
- package/build/assets/{dropdown-classes-f91ARMV1.js → dropdown-classes-zFW7qPSK.js} +1 -1
- package/build/assets/{entry.client-BCzY2Bf3.js → entry.client-EBn3ZtNP.js} +2 -2
- package/build/assets/{enum-filter-dropdown-HG3q3rkV.js → enum-filter-dropdown-CctILg5t.js} +1 -1
- package/build/assets/{environment-switch-overlay-CquZXolz.js → environment-switch-overlay-Cvmo6clk.js} +1 -1
- package/build/assets/{extensions-hub-BeweE0PW.js → extensions-hub-BZ3HYztk.js} +1 -1
- package/build/assets/{extensions-navigation-Dd58NlUF.js → extensions-navigation-Bnx32WAb.js} +1 -1
- package/build/assets/{files-tab-CLLG55-q.js → files-tab-D5zuU5TY.js} +1 -1
- package/build/assets/{form-control-classes-pHtZUI6K.js → form-control-classes-DLN3Qgub.js} +1 -1
- package/build/assets/format-model-name-BN_rx1Y3.js +1 -0
- package/build/assets/{format-relative-time-6-3uvOXe.js → format-relative-time-BerVamGl.js} +1 -1
- package/build/assets/{git-provider-icon-wg7ShB98.js → git-provider-icon-zuKz2cwo.js} +1 -1
- package/build/assets/{git-repo-dropdown-CWnaUyxL.js → git-repo-dropdown-B5VnMftu.js} +1 -1
- package/build/assets/git-repo-dropdown-Bo7ciSNX.js +1 -0
- package/build/assets/{highlighted-source-view-DBtI3por.js → highlighted-source-view-CgJfD5VT.js} +1 -1
- package/build/assets/home-DqyeeFA_.js +1 -0
- package/build/assets/{home-BpZQkDWM.js → home-Y0aDou-a.js} +1 -1
- package/build/assets/{home-store-DM4qw1lF.js → home-store-Cubdq0uP.js} +1 -1
- package/build/assets/index-home-DqyeeFA_.js +1 -0
- package/build/assets/install-server-modal-DW6dSyGr.js +1 -0
- package/build/assets/{kebab-menu-item-content-By16Tm_0.js → kebab-menu-item-content-CKydZrbH.js} +1 -1
- package/build/assets/{launch-C9uijypN.js → launch-c4GEP8oo.js} +1 -1
- package/build/assets/llm-not-configured-banner-DCE_0B4I.js +27 -0
- package/build/assets/llm-settings-8eWBF3Ij.js +1 -0
- package/build/assets/llm-settings-D9ko42Pi.js +1 -0
- package/build/assets/{llm-subscription-service-DwjJ8-_X.js → llm-subscription-service-B3K-97lP.js} +1 -1
- package/build/assets/{loading-spinner-6Yj-YyoS.js → loading-spinner-CN3riY7k.js} +1 -1
- package/build/assets/{manage-backends-modal-DrwErS0N.js → manage-backends-modal-DTCME_Pu.js} +1 -1
- package/build/assets/manifest-e1c3bc3b.js +1 -0
- package/build/assets/{manifest-subpage-layout-CtqVwIvJ.js → manifest-subpage-layout-Ds7G5GZ8.js} +1 -1
- package/build/assets/map-provider-7Bp2aih5.js +1 -0
- package/build/assets/markdown-renderer-DzvgdZey.js +8 -0
- package/build/assets/mcp-Da7wFU7A.js +9 -0
- package/build/assets/{mcp-marketplace-utils-BMnYz0Gc.js → mcp-marketplace-utils-DSLou4n7.js} +1 -1
- package/build/assets/messages-B2l0qLVk.js +40 -0
- package/build/assets/{modal-body-BHGCSAtj.js → modal-body-duRaxj_K.js} +1 -1
- package/build/assets/{modal-classes-DSTLuxkA.js → modal-classes-CLEXO6dj.js} +1 -1
- package/build/assets/{modal-close-button-dheWZ2ys.js → modal-close-button-D0aT02hs.js} +1 -1
- package/build/assets/npm-Cswluf6-.js +1 -0
- package/build/assets/{onboarding-5CZvRBbh.js → onboarding-BKbTYlW1.js} +1 -1
- package/build/assets/{onboarding-modal-CD2ehuxt.js → onboarding-modal-DrrGlEQq.js} +1 -1
- package/build/assets/{organization-service.api-Dsuoep0g.js → organization-service.api-DK8csGvK.js} +1 -1
- package/build/assets/{planner-tab-DeO7Thb0.js → planner-tab-D-mZ1I7n.js} +1 -1
- package/build/assets/{plugins-management-service-C9f75R6X.js → plugins-management-service-CywqyfLK.js} +1 -1
- package/build/assets/{profiles-service.api-Dl20_2IM.js → profiles-service.api-vcE_jJCe.js} +1 -1
- package/build/assets/{providers-CeuhK46N.js → providers-CdrPPm0z.js} +1 -1
- package/build/assets/{proxy-CIGI9zQC.js → proxy-b4aqL4SX.js} +1 -1
- package/build/assets/recommended-automations-launcher-DIYgUdMm.js +1 -0
- package/build/assets/{root-BQPNqNvD.js → root-CBBi6IAe.js} +2 -2
- package/build/assets/root-CRK9fOFd.css +1 -0
- package/build/assets/root-layout-BFN2rCOy.js +2 -0
- package/build/assets/{schema-field-z2vSLuLo.js → schema-field-Z-rMg9ti.js} +1 -1
- package/build/assets/{sdk-section-page-BnCXAytf.js → sdk-section-page-m_cjygxR.js} +1 -1
- package/build/assets/{secrets-service-DGUNlwQd.js → secrets-service-BHF4fxML.js} +1 -1
- package/build/assets/{secrets-settings-DUmML_dK.js → secrets-settings-aBGXtQhX.js} +1 -1
- package/build/assets/{segmented-toggle-eSofF7gn.js → segmented-toggle-SmvsR3jW.js} +1 -1
- package/build/assets/settings-CZPGNens.js +1 -0
- package/build/assets/{settings-dropdown-input-Cx4g7Xfl.js → settings-dropdown-input-TWIeiG02.js} +1 -1
- package/build/assets/{settings-index-BECdgecg.js → settings-index-Dtlegd6s.js} +1 -1
- package/build/assets/{settings-input-BQY115xH.js → settings-input-D61Vzp9e.js} +1 -1
- package/build/assets/{settings-list-classes-C9cLrrcc.js → settings-list-classes-sE0ASr1s.js} +1 -1
- package/build/assets/{settings-modal-ChBJNTdd.js → settings-modal-CalzJP2l.js} +1 -1
- package/build/assets/{settings-service.api-Dz9cZyvx.js → settings-service.api-DfPYaxZJ.js} +1 -1
- package/build/assets/{settings-switch-BIodMVz9.js → settings-switch-Drt-rF5s.js} +1 -1
- package/build/assets/{settings-utils-BL_Z9uIe.js → settings-utils-gw2ppAvw.js} +1 -1
- package/build/assets/{shared-conversation-CSF0aTuE.js → shared-conversation-BIAcuORk.js} +1 -1
- package/build/assets/{wrench-DRitTfwe.js → shield-check-D2cpEpv3.js} +1 -1
- package/build/assets/{sidebar-layout-DEycoFd_.js → sidebar-layout-Dxg6zc2K.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-DeRvTupV.js → sidebar-mobile-menu-toggle-C6HpQ55l.js} +1 -1
- package/build/assets/{sidebar-nav-link-BjpQ-NVc.js → sidebar-nav-link-CxLoG-dG.js} +1 -1
- package/build/assets/sidebar-store-BJeYY-E9.js +1 -0
- package/build/assets/{skill-card-pill-row-BH6fPGba.js → skill-card-pill-row-B_BYBONu.js} +1 -1
- package/build/assets/{skill-icon-badge-D4uznfjU.js → skill-icon-badge-CN41Hy9l.js} +1 -1
- package/build/assets/{skill-scope-DH_AUUUP.js → skill-scope-Dt1V5X2p.js} +1 -1
- package/build/assets/{skills-plugins-vfvOk34h.js → skills-plugins-B-H0cCOP.js} +1 -1
- package/build/assets/skills-settings-DthdIDUb.js +2 -0
- package/build/assets/{styled-tooltip-CQ9JEFiO.js → styled-tooltip-C5C07RQC.js} +1 -1
- package/build/assets/{task-list-tab-DeF_uXdw.js → task-list-tab-BTde81Iu.js} +1 -1
- package/build/assets/{terminal-DRc4rup7.js → terminal-DRrJo-cR.js} +1 -1
- package/build/assets/{toggle-switch--8n8NeV_.js → toggle-switch-CIioisto.js} +1 -1
- package/build/assets/{typography-BSbUhJPn.js → typography-D9tiHct7.js} +1 -1
- package/build/assets/{use-acp-credential-form-BGKFi1VL.js → use-acp-credential-form-DGyo0QvL.js} +1 -1
- package/build/assets/{use-activate-agent-profile-2nvALjC-.js → use-activate-agent-profile-DrPzIKJF.js} +1 -1
- package/build/assets/{use-agent-profiles-BlQA67MW.js → use-agent-profiles-B37XWDLq.js} +1 -1
- package/build/assets/{use-can-manage-org-profiles-Clw5Dh1s.js → use-can-manage-org-profiles-BzE28H3x.js} +1 -1
- package/build/assets/{use-cloud-current-user-id-B8AQSQFM.js → use-cloud-current-user-id-U6HPjy50.js} +1 -1
- package/build/assets/{use-config-C3zGuUQI.js → use-config-DapX3pjC.js} +1 -1
- package/build/assets/{use-create-conversation-FaP0kUpg.js → use-create-conversation-CRf1dCm5.js} +1 -1
- package/build/assets/{use-create-secret-CgQ3pXd6.js → use-create-secret-DcpT3RPw.js} +1 -1
- package/build/assets/{use-llm-profiles-C-tDO6UB.js → use-llm-profiles-CHxqoHiN.js} +1 -1
- package/build/assets/{use-manifest-capabilities-DLP5wCxp.js → use-manifest-capabilities-zihHDbMW.js} +1 -1
- package/build/assets/{use-onboarding-completion-BiCdXvlU.js → use-onboarding-completion-6kWOW3ul.js} +1 -1
- package/build/assets/{use-plugins-marketplace-DRFNLBlr.js → use-plugins-marketplace-CqkR8kLE.js} +1 -1
- package/build/assets/{use-save-agent-profile-CaGVAudI.js → use-save-agent-profile-CH9_EXQG.js} +1 -1
- package/build/assets/{use-save-settings-C9B8Y-h3.js → use-save-settings-DIEurJ3D.js} +1 -1
- package/build/assets/{use-settings-3RcgdUuZ.js → use-settings-BmLzQjnC.js} +1 -1
- package/build/assets/use-settings-nav-items-DV9qgeu7.js +16 -0
- package/build/assets/{use-tracking-CTtkLAmx.js → use-tracking-salbPJxT.js} +1 -1
- package/build/assets/{use-user-conversation-Bei82QsZ.js → use-user-conversation-Dt-W6uNw.js} +1 -1
- package/build/assets/utils-DPQfkd-7.js +1 -0
- package/build/assets/{verification-settings-nD61Ijph.js → verification-settings-oi54wEc1.js} +1 -1
- package/build/assets/wrench-B1_H-tL1.js +1 -0
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +39 -2
- package/build/locales/ca/openhands.json +39 -2
- package/build/locales/de/openhands.json +39 -2
- package/build/locales/en/openhands.json +39 -2
- package/build/locales/es/openhands.json +39 -2
- package/build/locales/fr/openhands.json +39 -2
- package/build/locales/it/openhands.json +39 -2
- package/build/locales/ja/openhands.json +39 -2
- package/build/locales/ko-KR/openhands.json +39 -2
- package/build/locales/no/openhands.json +39 -2
- package/build/locales/pt/openhands.json +39 -2
- package/build/locales/tr/openhands.json +39 -2
- package/build/locales/uk/openhands.json +39 -2
- package/build/locales/zh-CN/openhands.json +39 -2
- package/build/locales/zh-TW/openhands.json +39 -2
- package/config/defaults.json +1 -1
- package/dist/api/agent-server-adapter.cjs +3 -3
- package/dist/api/agent-server-adapter.cjs.map +1 -1
- package/dist/api/agent-server-adapter.d.ts +32 -5
- package/dist/api/agent-server-adapter.js +120 -91
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/cloud/conversation-service.api.cjs +1 -1
- package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
- package/dist/api/cloud/conversation-service.api.d.ts +19 -2
- package/dist/api/cloud/conversation-service.api.js +51 -47
- package/dist/api/cloud/conversation-service.api.js.map +1 -1
- package/dist/api/conversation-file-upload.api.cjs +1 -1
- package/dist/api/conversation-file-upload.api.js +10 -10
- package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
- package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
- package/dist/api/conversation-service/agent-server-conversation-service.api.js +55 -61
- package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
- package/dist/api/event-service/event-service.api.cjs +1 -1
- package/dist/api/event-service/event-service.api.js +12 -12
- package/dist/api/launch-child-conversation-client-tool.cjs +47 -0
- package/dist/api/launch-child-conversation-client-tool.cjs.map +1 -0
- package/dist/api/launch-child-conversation-client-tool.d.ts +3 -0
- package/dist/api/launch-child-conversation-client-tool.js +93 -0
- package/dist/api/launch-child-conversation-client-tool.js.map +1 -0
- package/dist/api/open-hands.types.d.ts +1 -1
- package/dist/api/workspace-upload-path.cjs +1 -1
- package/dist/api/workspace-upload-path.js +8 -8
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.cjs +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.js +24 -18
- package/dist/components/conversation-events/chat/event-content-helpers/get-action-event-title.js.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +5 -0
- package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs +2 -2
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js +11 -7
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.cjs.map +1 -1
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.js +61 -60
- package/dist/components/features/chat/components/chat-input-llm-profile-picker.js.map +1 -1
- package/dist/components/features/chat/model-messages.cjs +1 -1
- package/dist/components/features/chat/model-messages.cjs.map +1 -1
- package/dist/components/features/chat/model-messages.js +26 -25
- package/dist/components/features/chat/model-messages.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -2
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.d.ts +2 -8
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +50 -53
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.d.ts +12 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.js +6 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-meta-chip.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.d.ts +16 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js +95 -78
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-preview.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.js +40 -38
- package/dist/components/features/conversation-panel/conversation-card/conversation-repo-link.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.d.ts +14 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js +167 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-chips.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.d.ts +63 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js +66 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-display.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.cjs +2 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.cjs.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.d.ts +15 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.js +117 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-tag-icons.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/no-repository.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/no-repository.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/no-repository.js +30 -19
- package/dist/components/features/conversation-panel/conversation-card/no-repository.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +7 -2
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +146 -104
- package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +20 -0
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +53 -27
- package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-panel.js +226 -213
- package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.cjs +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.cjs.map +1 -1
- package/dist/components/features/settings/agent-canvas-update-card.d.ts +10 -5
- package/dist/components/features/settings/agent-canvas-update-card.js +223 -149
- package/dist/components/features/settings/agent-canvas-update-card.js.map +1 -1
- package/dist/components/features/settings/agent-canvas-version-modal.cjs +2 -0
- package/dist/components/features/settings/agent-canvas-version-modal.cjs.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-modal.d.ts +11 -0
- package/dist/components/features/settings/agent-canvas-version-modal.js +166 -0
- package/dist/components/features/settings/agent-canvas-version-modal.js.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-tile.cjs +2 -0
- package/dist/components/features/settings/agent-canvas-version-tile.cjs.map +1 -0
- package/dist/components/features/settings/agent-canvas-version-tile.d.ts +13 -0
- package/dist/components/features/settings/agent-canvas-version-tile.js +60 -0
- package/dist/components/features/settings/agent-canvas-version-tile.js.map +1 -0
- package/dist/components/features/settings/llm-profiles/profile-row.cjs +1 -1
- package/dist/components/features/settings/llm-profiles/profile-row.js +1 -0
- package/dist/components/features/settings/settings-desktop-sidebar.cjs +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.cjs.map +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.js +1 -1
- package/dist/components/features/settings/settings-desktop-sidebar.js.map +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.cjs +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.cjs.map +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.js +1 -1
- package/dist/components/features/settings/settings-mobile-drawer.js.map +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.cjs +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.cjs.map +1 -1
- package/dist/components/features/sidebar/sidebar-rail-body.js +109 -108
- package/dist/components/features/sidebar/sidebar-rail-body.js.map +1 -1
- package/dist/components/features/skills/extensions-navigation.cjs +1 -1
- package/dist/components/features/skills/extensions-navigation.cjs.map +1 -1
- package/dist/components/features/skills/extensions-navigation.js +9 -9
- package/dist/components/features/skills/extensions-navigation.js.map +1 -1
- package/dist/components/shared/modals/settings/model-selector.cjs +1 -1
- package/dist/components/shared/modals/settings/model-selector.cjs.map +1 -1
- package/dist/components/shared/modals/settings/model-selector.js +154 -98
- package/dist/components/shared/modals/settings/model-selector.js.map +1 -1
- package/dist/config/defaults.cjs +1 -1
- package/dist/config/defaults.cjs.map +1 -1
- package/dist/config/defaults.js +1 -1
- package/dist/config/defaults.js.map +1 -1
- package/dist/constants/child-conversation.cjs +2 -0
- package/dist/constants/child-conversation.cjs.map +1 -0
- package/dist/constants/child-conversation.d.ts +29 -0
- package/dist/constants/child-conversation.js +6 -0
- package/dist/constants/child-conversation.js.map +1 -0
- package/dist/contexts/conversation-websocket-context.cjs +3 -3
- package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
- package/dist/contexts/conversation-websocket-context.js +189 -188
- package/dist/contexts/conversation-websocket-context.js.map +1 -1
- package/dist/hooks/chat/use-model-interceptor.cjs +1 -1
- package/dist/hooks/chat/use-model-interceptor.js +6 -6
- package/dist/hooks/chat/use-slash-command.cjs +1 -1
- package/dist/hooks/chat/use-slash-command.cjs.map +1 -1
- package/dist/hooks/chat/use-slash-command.js +61 -60
- package/dist/hooks/chat/use-slash-command.js.map +1 -1
- package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
- package/dist/hooks/mutation/use-create-conversation.js +6 -6
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +38 -1
- package/dist/i18n/declaration.js +1 -1
- package/dist/i18n/declaration.js.map +1 -1
- package/dist/i18n/translation.cjs +2 -2
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +644 -15
- package/dist/i18n/translation.js.map +1 -1
- package/dist/icons/docker.cjs +2 -0
- package/dist/icons/docker.cjs.map +1 -0
- package/dist/icons/docker.js +19 -0
- package/dist/icons/docker.js.map +1 -0
- package/dist/icons/npm.cjs +2 -0
- package/dist/icons/npm.cjs.map +1 -0
- package/dist/icons/npm.js +19 -0
- package/dist/icons/npm.js.map +1 -0
- package/dist/locales/ar/openhands.json +39 -2
- package/dist/locales/ca/openhands.json +39 -2
- package/dist/locales/de/openhands.json +39 -2
- package/dist/locales/en/openhands.json +39 -2
- package/dist/locales/es/openhands.json +39 -2
- package/dist/locales/fr/openhands.json +39 -2
- package/dist/locales/it/openhands.json +39 -2
- package/dist/locales/ja/openhands.json +39 -2
- package/dist/locales/ko-KR/openhands.json +39 -2
- package/dist/locales/no/openhands.json +39 -2
- package/dist/locales/pt/openhands.json +39 -2
- package/dist/locales/tr/openhands.json +39 -2
- package/dist/locales/uk/openhands.json +39 -2
- package/dist/locales/zh-CN/openhands.json +39 -2
- package/dist/locales/zh-TW/openhands.json +39 -2
- package/dist/node_modules/@openhands/typescript-client/dist/index.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/index.js +1 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.js +16 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/briefcase.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/building-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.js +21 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-arrow-up.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/circle-user-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.js +43 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/cloud-cog.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.js +25 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/earth.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/flag.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-git-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.js +25 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/folder-key.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.js +30 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/git-pull-request.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.js +35 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/hash.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.js +12 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/house.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.js +33 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/id-card.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.js +15 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/key-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.js +19 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/layers.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.js +22 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/link-2.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/mails.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.js +12 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/messages-square.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/plug.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/square-kanban.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.js +23 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/ticket.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.js +21 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/users-round.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.js +43 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/waypoints.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js +19 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/webhook.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.js +27 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/workflow.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/wrench.js.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.cjs +2 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.cjs.map +1 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.js +9 -0
- package/dist/node_modules/lucide-react/dist/esm/icons/zap.js.map +1 -0
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +6 -3
- package/dist/package.js.map +1 -1
- package/dist/routes/app-settings.cjs +1 -1
- package/dist/routes/app-settings.cjs.map +1 -1
- package/dist/routes/app-settings.js +94 -93
- package/dist/routes/app-settings.js.map +1 -1
- package/dist/routes/llm-settings.cjs +1 -1
- package/dist/routes/llm-settings.cjs.map +1 -1
- package/dist/routes/llm-settings.js +131 -119
- package/dist/routes/llm-settings.js.map +1 -1
- package/dist/services/child-conversation-launch.cjs +2 -0
- package/dist/services/child-conversation-launch.cjs.map +1 -0
- package/dist/services/child-conversation-launch.d.ts +41 -0
- package/dist/services/child-conversation-launch.js +182 -0
- package/dist/services/child-conversation-launch.js.map +1 -0
- package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
- package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
- package/dist/stores/conversation-panel-preferences-store.d.ts +5 -1
- package/dist/stores/conversation-panel-preferences-store.js +8 -2
- package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
- package/dist/types/agent-server/core/base/action.d.ts +17 -1
- package/dist/types/agent-server/core/base/base.d.ts +2 -1
- package/dist/types/agent-server/type-guards.cjs +1 -1
- package/dist/types/agent-server/type-guards.cjs.map +1 -1
- package/dist/types/agent-server/type-guards.d.ts +8 -1
- package/dist/types/agent-server/type-guards.js +4 -3
- package/dist/types/agent-server/type-guards.js.map +1 -1
- package/dist/types/automation.d.ts +14 -0
- package/dist/utils/custom-toast-handlers.cjs +1 -1
- package/dist/utils/custom-toast-handlers.cjs.map +1 -1
- package/dist/utils/custom-toast-handlers.js +18 -1
- package/dist/utils/custom-toast-handlers.js.map +1 -1
- package/dist/utils/format-model-name.cjs +1 -1
- package/dist/utils/format-model-name.cjs.map +1 -1
- package/dist/utils/format-model-name.d.ts +11 -0
- package/dist/utils/format-model-name.js +11 -3
- package/dist/utils/format-model-name.js.map +1 -1
- package/package.json +6 -3
- package/scripts/brand-dev-electron.mjs +244 -0
- package/scripts/check-translation-completeness.cjs +4 -0
- package/scripts/generate-icons.mjs +109 -0
- package/build/assets/agent-server-conversation-service.api-km_8sOVf.js +0 -41
- package/build/assets/agent-settings-nmO-KSXr.js +0 -1
- package/build/assets/app-settings-9ok42bV6.js +0 -1
- package/build/assets/command-menu-CA3IjGNc.js +0 -1
- package/build/assets/conversation-B6t6EiVp.js +0 -1
- package/build/assets/conversation-panel-B6t6EiVp.js +0 -1
- package/build/assets/copy-DaHuDtG4.js +0 -1
- package/build/assets/git-repo-dropdown-TzOuG5Ef.js +0 -1
- package/build/assets/home-OEx0XtfP.js +0 -1
- package/build/assets/index-home-OEx0XtfP.js +0 -1
- package/build/assets/install-server-modal-DnjZrJUy.js +0 -1
- package/build/assets/list-filter-C86CBRDE.js +0 -1
- package/build/assets/llm-not-configured-banner-DrBrhfN0.js +0 -27
- package/build/assets/llm-settings-CRsLqxgu.js +0 -1
- package/build/assets/llm-settings-RBm_3kNm.js +0 -1
- package/build/assets/manifest-c352d453.js +0 -1
- package/build/assets/map-provider-CXU6issd.js +0 -1
- package/build/assets/markdown-renderer-lsvsp7jn.js +0 -8
- package/build/assets/mcp-DD4hPTRE.js +0 -9
- package/build/assets/messages-Dt77e0qi.js +0 -40
- package/build/assets/recommended-automations-launcher-6xmbf7lh.js +0 -1
- package/build/assets/root-BeuHFY2s.css +0 -1
- package/build/assets/root-layout-Dl_2CDoq.js +0 -3
- package/build/assets/settings-Co-M5bK9.js +0 -1
- package/build/assets/sidebar-store-B04sX8aP.js +0 -1
- package/build/assets/skills-settings-DnbIu2eO.js +0 -2
- package/build/assets/use-settings-nav-items-y-Oyc93G.js +0 -16
- package/build/assets/utils-BX9XJ0IE.js +0 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs +0 -2
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.cjs.map +0 -1
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js +0 -9
- package/dist/node_modules/lucide-react/dist/esm/icons/chevron-up.js.map +0 -1
|
@@ -1,50 +1,52 @@
|
|
|
1
1
|
import { useTranslation as e } from "../node_modules/react-i18next/dist/es/useTranslation.js";
|
|
2
2
|
import { I18nKey as t } from "../i18n/declaration.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
3
|
+
import { Info as n } from "../node_modules/lucide-react/dist/esm/icons/info.js";
|
|
4
|
+
import { DEFAULT_SETTINGS as r } from "../services/settings.js";
|
|
5
|
+
import { LLM_AUTH_TYPE_API_KEY as i, LLM_AUTH_TYPE_KEY as a, LLM_AUTH_TYPE_SUBSCRIPTION as o, LLM_SUBSCRIPTION_VENDOR_KEY as s, OPENAI_SUBSCRIPTION_VENDOR as c, resolveLlmAuthType as l } from "../constants/llm-subscription.js";
|
|
6
|
+
import { FREE_OPENHANDS_MODEL_NOTE as u, isFreeOpenHandsModel as d } from "../utils/format-model-name.js";
|
|
7
|
+
import { useSettings as f } from "../hooks/query/use-settings.js";
|
|
8
|
+
import { SettingsInput as p } from "../components/features/settings/settings-input.js";
|
|
9
|
+
import { SettingsDropdownInput as m } from "../components/features/settings/settings-dropdown-input.js";
|
|
10
|
+
import { extractModelAndProvider as h } from "../utils/extract-model-and-provider.js";
|
|
11
|
+
import { HelpLink as g } from "../ui/help-link.js";
|
|
12
|
+
import { ModelSelector as _ } from "../components/shared/modals/settings/model-selector.js";
|
|
13
|
+
import { useAgentSettingsSchema as v } from "../hooks/query/use-agent-settings-schema.js";
|
|
14
|
+
import { OpenAISubscriptionAuthCard as y } from "../components/features/settings/llm-settings/openai-subscription-auth-card.js";
|
|
15
|
+
import { KeyStatusIcon as b } from "../components/features/settings/key-status-icon.js";
|
|
16
|
+
import { inferInitialView as x } from "../utils/sdk-settings-schema.js";
|
|
17
|
+
import { SdkSectionPage as S } from "../components/features/settings/sdk-settings/sdk-section-page.js";
|
|
16
18
|
import "../components/features/settings/llm-profiles/index.js";
|
|
17
|
-
import { useOpenAISubscriptionModels as
|
|
18
|
-
import
|
|
19
|
-
import { Fragment as
|
|
19
|
+
import { useOpenAISubscriptionModels as C } from "../hooks/query/use-llm-subscription-models.js";
|
|
20
|
+
import w from "react";
|
|
21
|
+
import { Fragment as T, jsx as E, jsxs as D } from "react/jsx-runtime";
|
|
20
22
|
//#region src/routes/llm-settings.tsx
|
|
21
|
-
var
|
|
23
|
+
var O = new Set([
|
|
22
24
|
"llm.model",
|
|
23
25
|
"llm.api_key",
|
|
24
26
|
"llm.base_url",
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
]),
|
|
27
|
+
a,
|
|
28
|
+
s
|
|
29
|
+
]), k = (e, t) => !e || !t ? null : `${e}/${t}`, A = (e, t) => e?.sections.flatMap((e) => e.fields).find((e) => e.key === t)?.default ?? null, j = {
|
|
28
30
|
openai: new Set(["https://api.openai.com", "https://api.openai.com/v1"]),
|
|
29
31
|
moonshot: new Set(["https://api.kimi.com/coding/v1"])
|
|
30
|
-
},
|
|
32
|
+
}, M = (e) => {
|
|
31
33
|
try {
|
|
32
34
|
let t = new URL(e), n = t.pathname.replace(/\/+$/, "") || "";
|
|
33
35
|
return `${t.origin}${n}`;
|
|
34
36
|
} catch {
|
|
35
37
|
return e.trim().replace(/\/+$/, "");
|
|
36
38
|
}
|
|
37
|
-
},
|
|
38
|
-
let n =
|
|
39
|
+
}, N = (e, t) => {
|
|
40
|
+
let n = M(t), { provider: r } = h(e);
|
|
39
41
|
if (r) {
|
|
40
|
-
let e =
|
|
42
|
+
let e = j[r];
|
|
41
43
|
if (e) return e.has(n);
|
|
42
44
|
}
|
|
43
|
-
return Object.values(
|
|
45
|
+
return Object.values(j).some((e) => e?.has(n));
|
|
44
46
|
};
|
|
45
|
-
function
|
|
47
|
+
function P({ testId: n }) {
|
|
46
48
|
let { t: r } = e("openhands");
|
|
47
|
-
return /* @__PURE__ */
|
|
49
|
+
return /* @__PURE__ */ E(g, {
|
|
48
50
|
testId: n,
|
|
49
51
|
text: r(t.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT),
|
|
50
52
|
linkText: r(t.SETTINGS$NAV_API_KEYS),
|
|
@@ -52,164 +54,174 @@ function j({ testId: n }) {
|
|
|
52
54
|
suffix: ` ${r(t.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`
|
|
53
55
|
});
|
|
54
56
|
}
|
|
55
|
-
function
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
57
|
+
function F() {
|
|
58
|
+
return /* @__PURE__ */ D("p", {
|
|
59
|
+
"data-testid": "openhands-free-models-note",
|
|
60
|
+
className: "flex items-start gap-2 text-xs text-warning",
|
|
61
|
+
children: [/* @__PURE__ */ E(n, {
|
|
62
|
+
className: "mt-0.5 size-4 shrink-0 text-warning",
|
|
63
|
+
"aria-hidden": !0
|
|
64
|
+
}), /* @__PURE__ */ E("span", { children: u })]
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
function I({ scope: n = "personal", onSaveSuccess: u, initialValueOverrides: h, embedded: j, hideSaveButton: M, suppressSuccessToast: I, onSaveControlChange: L }) {
|
|
68
|
+
let { t: R } = e("openhands"), { data: z } = f(n), { data: B } = v(z?.agent_settings_schema), V = z?.agent_settings?.llm, H = l(h?.["llm.auth_type"] ?? V?.auth_type), [U, W] = w.useState(H === o), { data: G, isLoading: K, isFetching: q } = C({ enabled: U }), J = U && !G && (K || q), Y = w.useRef(null), X = w.useRef(null);
|
|
69
|
+
w.useEffect(() => {
|
|
70
|
+
H === "subscription" && W(!0);
|
|
71
|
+
}, [H]);
|
|
72
|
+
let Z = String(r.agent_settings?.llm?.model ?? ""), Q = w.useCallback((e, t) => {
|
|
73
|
+
let n = x(e, t);
|
|
62
74
|
if (n !== "basic") return n;
|
|
63
75
|
let r = e.llm_model ?? "", i = e.llm_base_url?.trim() ?? "";
|
|
64
|
-
return i.length > 0 && !
|
|
65
|
-
}, []),
|
|
66
|
-
let
|
|
76
|
+
return i.length > 0 && !N(r, i) ? "all" : "basic";
|
|
77
|
+
}, []), $ = w.useCallback(({ values: e, isDisabled: n, view: r, onChange: u }) => {
|
|
78
|
+
let f = typeof e["llm.model"] == "string" ? e["llm.model"] : "", h = typeof e["llm.base_url"] == "string" ? e["llm.base_url"] : "", v = f.startsWith("openhands/"), x = l(e[a]), S = x === o, C = n || S && J, w = G?.includes(f) ? f : G?.[0] ?? "", O = typeof e["llm.api_key"] == "string" ? e["llm.api_key"] : "", A = j ? O.length > 0 : !!z?.llm_api_key_set, M = (e, r) => /* @__PURE__ */ D(T, { children: [/* @__PURE__ */ E(p, {
|
|
67
79
|
testId: e,
|
|
68
|
-
label:
|
|
80
|
+
label: R(t.SETTINGS_FORM$API_KEY),
|
|
69
81
|
type: "password",
|
|
70
82
|
className: "w-full",
|
|
71
83
|
value: O,
|
|
72
|
-
placeholder:
|
|
73
|
-
onChange: (e) =>
|
|
84
|
+
placeholder: A ? "<hidden>" : "",
|
|
85
|
+
onChange: (e) => u("llm.api_key", e),
|
|
74
86
|
isDisabled: n,
|
|
75
|
-
startContent:
|
|
76
|
-
}), /* @__PURE__ */
|
|
87
|
+
startContent: A ? /* @__PURE__ */ E(b, { isSet: A }) : void 0
|
|
88
|
+
}), /* @__PURE__ */ E(g, {
|
|
77
89
|
testId: r,
|
|
78
|
-
text:
|
|
79
|
-
linkText:
|
|
90
|
+
text: R(t.SETTINGS$DONT_KNOW_API_KEY),
|
|
91
|
+
linkText: R(t.SETTINGS$CLICK_FOR_INSTRUCTIONS),
|
|
80
92
|
href: "https://docs.openhands.dev/usage/local-setup#getting-an-api-key"
|
|
81
93
|
})] }), N = (e) => {
|
|
82
|
-
let t = e === "subscription" ?
|
|
83
|
-
if (
|
|
84
|
-
|
|
85
|
-
let e =
|
|
86
|
-
|
|
94
|
+
let t = e === "subscription" ? o : i;
|
|
95
|
+
if (u(a, t), t === "subscription") {
|
|
96
|
+
W(!0), f && !G?.includes(f) && (Y.current = f);
|
|
97
|
+
let e = X.current && G?.includes(X.current) ? X.current : G?.[0];
|
|
98
|
+
u(s, c), !G?.includes(f) && e && u("llm.model", e);
|
|
87
99
|
return;
|
|
88
100
|
}
|
|
89
|
-
|
|
90
|
-
},
|
|
101
|
+
f && G?.includes(f) && (X.current = f, u("llm.model", Y.current ?? Z));
|
|
102
|
+
}, I = () => /* @__PURE__ */ E(m, {
|
|
91
103
|
testId: "llm-auth-type-input",
|
|
92
|
-
name:
|
|
93
|
-
label:
|
|
104
|
+
name: a,
|
|
105
|
+
label: R(t.SETTINGS$LLM_AUTH_TYPE),
|
|
94
106
|
items: [{
|
|
95
|
-
key:
|
|
96
|
-
label:
|
|
107
|
+
key: i,
|
|
108
|
+
label: R(t.SETTINGS$LLM_AUTH_TYPE_API_KEY)
|
|
97
109
|
}, {
|
|
98
|
-
key:
|
|
99
|
-
label:
|
|
110
|
+
key: o,
|
|
111
|
+
label: R(t.SETTINGS$LLM_AUTH_TYPE_SUBSCRIPTION)
|
|
100
112
|
}],
|
|
101
|
-
selectedKey:
|
|
113
|
+
selectedKey: x,
|
|
102
114
|
isClearable: !1,
|
|
103
115
|
required: !0,
|
|
104
|
-
isDisabled:
|
|
116
|
+
isDisabled: C,
|
|
105
117
|
onSelectionChange: N
|
|
106
|
-
}),
|
|
118
|
+
}), L = () => /* @__PURE__ */ D("div", {
|
|
107
119
|
className: "flex flex-col gap-6",
|
|
108
120
|
"data-testid": "llm-subscription-settings",
|
|
109
|
-
children: [/* @__PURE__ */
|
|
121
|
+
children: [/* @__PURE__ */ E(m, {
|
|
110
122
|
testId: "llm-subscription-model-input",
|
|
111
123
|
name: "llm.subscription_model",
|
|
112
|
-
label:
|
|
113
|
-
items: (
|
|
124
|
+
label: R(t.SETTINGS$SUBSCRIPTION_MODEL),
|
|
125
|
+
items: (G ?? []).map((e) => ({
|
|
114
126
|
key: e,
|
|
115
127
|
label: e
|
|
116
128
|
})),
|
|
117
|
-
selectedKey:
|
|
129
|
+
selectedKey: w,
|
|
118
130
|
isClearable: !1,
|
|
119
131
|
required: !0,
|
|
120
|
-
isDisabled:
|
|
132
|
+
isDisabled: C || !G?.length,
|
|
121
133
|
onSelectionChange: (e) => {
|
|
122
|
-
let t = typeof e == "string" ? e :
|
|
123
|
-
t &&
|
|
134
|
+
let t = typeof e == "string" ? e : G?.[0];
|
|
135
|
+
t && u("llm.model", t);
|
|
124
136
|
}
|
|
125
|
-
}), /* @__PURE__ */
|
|
137
|
+
}), /* @__PURE__ */ E(y, { isDisabled: n })]
|
|
126
138
|
});
|
|
127
|
-
return /* @__PURE__ */
|
|
139
|
+
return /* @__PURE__ */ E("div", {
|
|
128
140
|
className: "flex flex-col gap-6",
|
|
129
|
-
children:
|
|
141
|
+
children: r === "basic" ? /* @__PURE__ */ D("div", {
|
|
130
142
|
className: "flex flex-col gap-6",
|
|
131
143
|
"data-testid": "llm-settings-form-basic",
|
|
132
|
-
children: [
|
|
133
|
-
/* @__PURE__ */
|
|
134
|
-
currentModel:
|
|
144
|
+
children: [I(), S ? L() : /* @__PURE__ */ D(T, { children: [
|
|
145
|
+
/* @__PURE__ */ E(_, {
|
|
146
|
+
currentModel: f || void 0,
|
|
135
147
|
onChange: (e, t) => {
|
|
136
|
-
let n =
|
|
137
|
-
n &&
|
|
148
|
+
let n = k(e, t);
|
|
149
|
+
n && u("llm.model", n);
|
|
138
150
|
},
|
|
139
151
|
wrapperClassName: "!flex-col !gap-6",
|
|
140
152
|
isDisabled: n
|
|
141
153
|
}),
|
|
142
|
-
|
|
143
|
-
|
|
154
|
+
v ? /* @__PURE__ */ E(P, { testId: "openhands-api-key-help" }) : null,
|
|
155
|
+
M("llm-api-key-input", "llm-api-key-help-anchor")
|
|
144
156
|
] })]
|
|
145
|
-
}) : /* @__PURE__ */
|
|
157
|
+
}) : /* @__PURE__ */ D("div", {
|
|
146
158
|
className: "flex flex-col gap-6",
|
|
147
159
|
"data-testid": "llm-settings-form-advanced",
|
|
148
|
-
children: [
|
|
149
|
-
/* @__PURE__ */
|
|
160
|
+
children: [I(), S ? L() : /* @__PURE__ */ D(T, { children: [
|
|
161
|
+
/* @__PURE__ */ E(p, {
|
|
150
162
|
testId: "llm-custom-model-input",
|
|
151
|
-
label:
|
|
163
|
+
label: R(t.SETTINGS$CUSTOM_MODEL),
|
|
152
164
|
type: "text",
|
|
153
165
|
className: "w-full",
|
|
154
|
-
value:
|
|
155
|
-
placeholder:
|
|
156
|
-
onChange: (e) =>
|
|
166
|
+
value: f,
|
|
167
|
+
placeholder: Z,
|
|
168
|
+
onChange: (e) => u("llm.model", e),
|
|
157
169
|
isDisabled: n
|
|
158
170
|
}),
|
|
159
|
-
|
|
160
|
-
/* @__PURE__ */
|
|
171
|
+
v ? /* @__PURE__ */ D(T, { children: [d(f) ? /* @__PURE__ */ E(F, {}) : null, /* @__PURE__ */ E(P, { testId: "openhands-api-key-help-2" })] }) : null,
|
|
172
|
+
/* @__PURE__ */ E(p, {
|
|
161
173
|
testId: "base-url-input",
|
|
162
|
-
label:
|
|
174
|
+
label: R(t.SETTINGS$BASE_URL),
|
|
163
175
|
type: "text",
|
|
164
176
|
className: "w-full",
|
|
165
|
-
value:
|
|
177
|
+
value: h,
|
|
166
178
|
placeholder: "https://api.openai.com",
|
|
167
|
-
onChange: (e) =>
|
|
179
|
+
onChange: (e) => u("llm.base_url", e),
|
|
168
180
|
isDisabled: n
|
|
169
181
|
}),
|
|
170
|
-
|
|
182
|
+
M("llm-api-key-input", "llm-api-key-help-anchor-advanced")
|
|
171
183
|
] })]
|
|
172
184
|
})
|
|
173
185
|
});
|
|
174
186
|
}, [
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
]),
|
|
182
|
-
let n = structuredClone(e.agent_settings_diff ?? {}),
|
|
183
|
-
if (
|
|
184
|
-
|
|
185
|
-
let e = typeof
|
|
186
|
-
if (!
|
|
187
|
-
|
|
188
|
-
} else t.dirty["llm.auth_type"] && (
|
|
189
|
-
return n.llm =
|
|
190
|
-
}, [
|
|
191
|
-
return /* @__PURE__ */
|
|
192
|
-
scope:
|
|
187
|
+
Z,
|
|
188
|
+
j,
|
|
189
|
+
J,
|
|
190
|
+
z?.llm_api_key_set,
|
|
191
|
+
G,
|
|
192
|
+
R
|
|
193
|
+
]), ee = w.useCallback((e, t) => {
|
|
194
|
+
let n = structuredClone(e.agent_settings_diff ?? {}), r = n.llm ?? {};
|
|
195
|
+
if (l(t.values["llm.auth_type"]) === "subscription") {
|
|
196
|
+
r.auth_type = o, r.subscription_vendor = c;
|
|
197
|
+
let e = typeof r.model == "string" ? r.model : String(t.values["llm.model"] ?? ""), n = G?.[0];
|
|
198
|
+
if (!G?.includes(e) && !n) throw Error("Subscription models are not loaded yet.");
|
|
199
|
+
r.model = G?.includes(e) ? e : n, delete r.api_key, delete r.base_url;
|
|
200
|
+
} else t.dirty["llm.auth_type"] && (r.auth_type = i, r.subscription_vendor = null), t.view === "basic" && r.model !== void 0 && (r.base_url = A(B, "llm.base_url"));
|
|
201
|
+
return n.llm = r, { agent_settings_diff: n };
|
|
202
|
+
}, [B, G]);
|
|
203
|
+
return /* @__PURE__ */ E(S, {
|
|
204
|
+
scope: n,
|
|
193
205
|
settingsSources: [{
|
|
194
206
|
settingsSource: "agent_settings",
|
|
195
207
|
sectionKeys: ["llm"],
|
|
196
|
-
excludeKeys:
|
|
208
|
+
excludeKeys: O
|
|
197
209
|
}],
|
|
198
|
-
header:
|
|
199
|
-
buildPayload:
|
|
200
|
-
getInitialView:
|
|
210
|
+
header: $,
|
|
211
|
+
buildPayload: ee,
|
|
212
|
+
getInitialView: Q,
|
|
201
213
|
forceShowAdvancedView: !0,
|
|
202
214
|
allowAllView: !0,
|
|
203
|
-
onSaveSuccess:
|
|
204
|
-
initialValueOverrides:
|
|
205
|
-
embedded:
|
|
206
|
-
hideSaveButton:
|
|
207
|
-
suppressSuccessToast:
|
|
208
|
-
onSaveControlChange:
|
|
215
|
+
onSaveSuccess: u,
|
|
216
|
+
initialValueOverrides: h,
|
|
217
|
+
embedded: j,
|
|
218
|
+
hideSaveButton: M,
|
|
219
|
+
suppressSuccessToast: I,
|
|
220
|
+
onSaveControlChange: L,
|
|
209
221
|
testId: "llm-settings-screen"
|
|
210
222
|
});
|
|
211
223
|
}
|
|
212
224
|
//#endregion
|
|
213
|
-
export {
|
|
225
|
+
export { I as LlmSettingsScreen };
|
|
214
226
|
|
|
215
227
|
//# sourceMappingURL=llm-settings.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"llm-settings.js","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { SettingsDropdownInput } from \"#/components/features/settings/settings-dropdown-input\";\nimport { OpenAISubscriptionAuthCard } from \"#/components/features/settings/llm-settings/openai-subscription-auth-card\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n LLM_AUTH_TYPE_API_KEY,\n LLM_AUTH_TYPE_KEY,\n LLM_AUTH_TYPE_SUBSCRIPTION,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n OPENAI_SUBSCRIPTION_VENDOR,\n resolveLlmAuthType,\n} from \"#/constants/llm-subscription\";\nimport { useOpenAISubscriptionModels } from \"#/hooks/query/use-llm-subscription-models\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\n \"llm.model\",\n \"llm.api_key\",\n \"llm.base_url\",\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n moonshot: new Set([\"https://api.kimi.com/coding/v1\"]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n const persistedLlmSettings = settings?.agent_settings?.llm as\n | Record<string, unknown>\n | undefined;\n const initialAuthType = resolveLlmAuthType(\n initialValueOverrides?.[LLM_AUTH_TYPE_KEY] ??\n persistedLlmSettings?.auth_type,\n );\n const [enableSubscriptionModels, setEnableSubscriptionModels] =\n React.useState(initialAuthType === LLM_AUTH_TYPE_SUBSCRIPTION);\n const {\n data: subscriptionModels,\n isLoading: isSubscriptionModelsLoading,\n isFetching: isSubscriptionModelsFetching,\n } = useOpenAISubscriptionModels({ enabled: enableSubscriptionModels });\n const isWaitingForSubscriptionModels =\n enableSubscriptionModels &&\n !subscriptionModels &&\n (isSubscriptionModelsLoading || isSubscriptionModelsFetching);\n const lastApiKeyModelRef = React.useRef<string | null>(null);\n const lastSubscriptionModelRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n if (initialAuthType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n setEnableSubscriptionModels(true);\n }\n }, [initialAuthType]);\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n const authType = resolveLlmAuthType(values[LLM_AUTH_TYPE_KEY]);\n const isSubscriptionAuth = authType === LLM_AUTH_TYPE_SUBSCRIPTION;\n const shouldDisableSubscriptionControls =\n isDisabled || (isSubscriptionAuth && isWaitingForSubscriptionModels);\n const subscriptionModelValue = subscriptionModels?.includes(modelValue)\n ? modelValue\n : (subscriptionModels?.[0] ?? \"\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n // eslint-disable-next-line i18next/no-literal-string -- masked-key sentinel, not translatable\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n const handleAuthTypeChange = (selectedKey: React.Key | null) => {\n const nextAuthType =\n selectedKey === LLM_AUTH_TYPE_SUBSCRIPTION\n ? LLM_AUTH_TYPE_SUBSCRIPTION\n : LLM_AUTH_TYPE_API_KEY;\n onChange(LLM_AUTH_TYPE_KEY, nextAuthType);\n\n if (nextAuthType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n setEnableSubscriptionModels(true);\n if (modelValue && !subscriptionModels?.includes(modelValue)) {\n lastApiKeyModelRef.current = modelValue;\n }\n const restoredSubscriptionModel =\n lastSubscriptionModelRef.current &&\n subscriptionModels?.includes(lastSubscriptionModelRef.current)\n ? lastSubscriptionModelRef.current\n : subscriptionModels?.[0];\n onChange(LLM_SUBSCRIPTION_VENDOR_KEY, OPENAI_SUBSCRIPTION_VENDOR);\n if (\n !subscriptionModels?.includes(modelValue) &&\n restoredSubscriptionModel\n ) {\n onChange(\"llm.model\", restoredSubscriptionModel);\n }\n return;\n }\n\n if (modelValue && subscriptionModels?.includes(modelValue)) {\n lastSubscriptionModelRef.current = modelValue;\n onChange(\"llm.model\", lastApiKeyModelRef.current ?? defaultModel);\n }\n };\n\n const renderAuthTypeInput = () => (\n <SettingsDropdownInput\n testId=\"llm-auth-type-input\"\n name={LLM_AUTH_TYPE_KEY}\n label={t(I18nKey.SETTINGS$LLM_AUTH_TYPE)}\n items={[\n {\n key: LLM_AUTH_TYPE_API_KEY,\n label: t(I18nKey.SETTINGS$LLM_AUTH_TYPE_API_KEY),\n },\n {\n key: LLM_AUTH_TYPE_SUBSCRIPTION,\n label: t(I18nKey.SETTINGS$LLM_AUTH_TYPE_SUBSCRIPTION),\n },\n ]}\n selectedKey={authType}\n isClearable={false}\n required\n isDisabled={shouldDisableSubscriptionControls}\n onSelectionChange={handleAuthTypeChange}\n />\n );\n\n const renderSubscriptionSettings = () => (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-subscription-settings\"\n >\n <SettingsDropdownInput\n testId=\"llm-subscription-model-input\"\n name=\"llm.subscription_model\"\n label={t(I18nKey.SETTINGS$SUBSCRIPTION_MODEL)}\n items={(subscriptionModels ?? []).map((model) => ({\n key: model,\n label: model,\n }))}\n selectedKey={subscriptionModelValue}\n isClearable={false}\n required\n isDisabled={\n shouldDisableSubscriptionControls || !subscriptionModels?.length\n }\n onSelectionChange={(selectedKey) => {\n const nextModel =\n typeof selectedKey === \"string\"\n ? selectedKey\n : subscriptionModels?.[0];\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n />\n <OpenAISubscriptionAuthCard isDisabled={isDisabled} />\n </div>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n {renderAuthTypeInput()}\n\n {isSubscriptionAuth ? (\n renderSubscriptionSettings()\n ) : (\n <>\n <ModelSelector\n currentModel={modelValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-input\",\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-help-anchor\",\n )}\n </>\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n {renderAuthTypeInput()}\n\n {isSubscriptionAuth ? (\n renderSubscriptionSettings()\n ) : (\n <>\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-input\",\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-help-anchor-advanced\",\n )}\n </>\n )}\n </div>\n )}\n </div>\n );\n },\n [\n defaultModel,\n embedded,\n isWaitingForSubscriptionModels,\n settings?.llm_api_key_set,\n subscriptionModels,\n t,\n ],\n );\n\n const buildPayload = React.useCallback(\n (\n defaultPayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n dirty: Record<string, boolean>;\n view: SettingsView;\n },\n ) => {\n // defaultPayload is the wrapped diff (e.g.\n // `{ agent_settings_diff: { llm: { model: \"gpt-4\" } } }`); we only\n // mutate the inner `llm` object below.\n const agentSettings = structuredClone(\n (defaultPayload.agent_settings_diff as Record<string, unknown>) ?? {},\n );\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n const authType = resolveLlmAuthType(context.values[LLM_AUTH_TYPE_KEY]);\n\n if (authType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n llm.auth_type = LLM_AUTH_TYPE_SUBSCRIPTION;\n llm.subscription_vendor = OPENAI_SUBSCRIPTION_VENDOR;\n const model =\n typeof llm.model === \"string\"\n ? llm.model\n : String(context.values[\"llm.model\"] ?? \"\");\n const fallbackSubscriptionModel = subscriptionModels?.[0];\n if (\n !subscriptionModels?.includes(model) &&\n !fallbackSubscriptionModel\n ) {\n throw new Error(\"Subscription models are not loaded yet.\");\n }\n llm.model = subscriptionModels?.includes(model)\n ? model\n : fallbackSubscriptionModel;\n delete llm.api_key;\n delete llm.base_url;\n } else {\n if (context.dirty[LLM_AUTH_TYPE_KEY]) {\n llm.auth_type = LLM_AUTH_TYPE_API_KEY;\n llm.subscription_vendor = null;\n }\n if (context.view === \"basic\" && llm.model !== undefined) {\n llm.base_url = getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n }\n }\n\n agentSettings.llm = llm;\n return { agent_settings_diff: agentSettings };\n },\n [schema, subscriptionModels],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n settingsSources={[\n {\n settingsSource: \"agent_settings\",\n sectionKeys: [\"llm\"],\n excludeKeys: LLM_EXCLUDED_KEYS,\n },\n ]}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders the LLM-profile management view for both\n * backend types. Both manage the LLM through named profiles — local via the\n * agent-server (`/api/profiles`), cloud via the app-server\n * (`/api/v1/settings/profiles`) — and the view is backend-agnostic because it\n * goes through ProfilesService, which routes per active backend.\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, the profile create/edit form).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"llm-settings.js","names":[],"sources":["../../src/routes/llm-settings.tsx"],"sourcesContent":["import React from \"react\";\nimport { Info } from \"lucide-react\";\nimport { useTranslation } from \"react-i18next\";\nimport { ModelSelector } from \"#/components/shared/modals/settings/model-selector\";\nimport { useAgentSettingsSchema } from \"#/hooks/query/use-agent-settings-schema\";\nimport { useSettings } from \"#/hooks/query/use-settings\";\nimport { SettingsInput } from \"#/components/features/settings/settings-input\";\nimport { SettingsDropdownInput } from \"#/components/features/settings/settings-dropdown-input\";\nimport { OpenAISubscriptionAuthCard } from \"#/components/features/settings/llm-settings/openai-subscription-auth-card\";\nimport { HelpLink } from \"#/ui/help-link\";\nimport { KeyStatusIcon } from \"#/components/features/settings/key-status-icon\";\nimport {\n SdkSectionHeaderProps,\n SdkSectionPage,\n SdkSectionSaveControl,\n} from \"#/components/features/settings/sdk-settings/sdk-section-page\";\nimport { LlmSettingsLocalView } from \"#/components/features/settings/llm-profiles\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport { Settings, SettingsSchema, SettingsScope } from \"#/types/settings\";\nimport { extractModelAndProvider } from \"#/utils/extract-model-and-provider\";\nimport {\n inferInitialView,\n type SettingsFormValues,\n type SettingsView,\n} from \"#/utils/sdk-settings-schema\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport {\n LLM_AUTH_TYPE_API_KEY,\n LLM_AUTH_TYPE_KEY,\n LLM_AUTH_TYPE_SUBSCRIPTION,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n OPENAI_SUBSCRIPTION_VENDOR,\n resolveLlmAuthType,\n} from \"#/constants/llm-subscription\";\nimport { useOpenAISubscriptionModels } from \"#/hooks/query/use-llm-subscription-models\";\nimport {\n FREE_OPENHANDS_MODEL_NOTE,\n isFreeOpenHandsModel,\n} from \"#/utils/format-model-name\";\n\nconst LLM_EXCLUDED_KEYS = new Set([\n \"llm.model\",\n \"llm.api_key\",\n \"llm.base_url\",\n LLM_AUTH_TYPE_KEY,\n LLM_SUBSCRIPTION_VENDOR_KEY,\n]);\n\nconst buildModelId = (provider: string | null, model: string | null) => {\n if (!provider || !model) return null;\n return `${provider}/${model}`;\n};\n\nconst getSchemaFieldDefaultValue = (\n schema: SettingsSchema | null | undefined,\n fieldKey: string,\n) =>\n schema?.sections\n .flatMap((section) => section.fields)\n .find((field) => field.key === fieldKey)?.default ?? null;\n\nconst KNOWN_PROVIDER_DEFAULT_BASE_URLS: Partial<Record<string, Set<string>>> = {\n openai: new Set([\"https://api.openai.com\", \"https://api.openai.com/v1\"]),\n moonshot: new Set([\"https://api.kimi.com/coding/v1\"]),\n};\n\nconst normalizeBaseUrl = (baseUrl: string) => {\n try {\n const parsedUrl = new URL(baseUrl);\n const normalizedPath = parsedUrl.pathname.replace(/\\/+$/, \"\") || \"\";\n return `${parsedUrl.origin}${normalizedPath}`;\n } catch {\n return baseUrl.trim().replace(/\\/+$/, \"\");\n }\n};\n\nconst isProviderDefaultBaseUrl = (model: string, baseUrl: string) => {\n const normalizedBaseUrl = normalizeBaseUrl(baseUrl);\n const { provider } = extractModelAndProvider(model);\n\n if (provider) {\n const knownDefaults = KNOWN_PROVIDER_DEFAULT_BASE_URLS[provider];\n if (knownDefaults) {\n return knownDefaults.has(normalizedBaseUrl);\n }\n }\n\n return Object.values(KNOWN_PROVIDER_DEFAULT_BASE_URLS).some((knownDefaults) =>\n knownDefaults?.has(normalizedBaseUrl),\n );\n};\n\ninterface OpenHandsApiKeyHelpProps {\n testId: string;\n}\n\nfunction OpenHandsApiKeyHelp({ testId }: OpenHandsApiKeyHelpProps) {\n const { t } = useTranslation(\"openhands\");\n\n return (\n <HelpLink\n testId={testId}\n text={t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_TEXT)}\n linkText={t(I18nKey.SETTINGS$NAV_API_KEYS)}\n href=\"https://app.all-hands.dev/settings/api-keys\"\n suffix={` ${t(I18nKey.SETTINGS$OPENHANDS_API_KEY_HELP_SUFFIX)}`}\n />\n );\n}\n\nfunction OpenHandsFreeModelsNote() {\n return (\n <p\n data-testid=\"openhands-free-models-note\"\n className=\"flex items-start gap-2 text-xs text-warning\"\n >\n <Info className=\"mt-0.5 size-4 shrink-0 text-warning\" aria-hidden />\n <span>{FREE_OPENHANDS_MODEL_NOTE}</span>\n </p>\n );\n}\n\nexport function LlmSettingsScreen({\n scope = \"personal\",\n onSaveSuccess,\n initialValueOverrides,\n embedded,\n hideSaveButton,\n suppressSuccessToast,\n onSaveControlChange,\n}: {\n scope?: SettingsScope;\n /** Optional hook fired after a successful save (e.g. advance an onboarding step). */\n onSaveSuccess?: () => void;\n /** Forwarded to {@link SdkSectionPage}. */\n initialValueOverrides?: SettingsFormValues;\n /** Forwarded to {@link SdkSectionPage}. */\n embedded?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n hideSaveButton?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n suppressSuccessToast?: boolean;\n /** Forwarded to {@link SdkSectionPage}. */\n onSaveControlChange?: (control: SdkSectionSaveControl) => void;\n}) {\n const { t } = useTranslation(\"openhands\");\n\n const { data: settings } = useSettings(scope);\n const { data: schema } = useAgentSettingsSchema(\n settings?.agent_settings_schema,\n );\n const persistedLlmSettings = settings?.agent_settings?.llm as\n | Record<string, unknown>\n | undefined;\n const initialAuthType = resolveLlmAuthType(\n initialValueOverrides?.[LLM_AUTH_TYPE_KEY] ??\n persistedLlmSettings?.auth_type,\n );\n const [enableSubscriptionModels, setEnableSubscriptionModels] =\n React.useState(initialAuthType === LLM_AUTH_TYPE_SUBSCRIPTION);\n const {\n data: subscriptionModels,\n isLoading: isSubscriptionModelsLoading,\n isFetching: isSubscriptionModelsFetching,\n } = useOpenAISubscriptionModels({ enabled: enableSubscriptionModels });\n const isWaitingForSubscriptionModels =\n enableSubscriptionModels &&\n !subscriptionModels &&\n (isSubscriptionModelsLoading || isSubscriptionModelsFetching);\n const lastApiKeyModelRef = React.useRef<string | null>(null);\n const lastSubscriptionModelRef = React.useRef<string | null>(null);\n\n React.useEffect(() => {\n if (initialAuthType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n setEnableSubscriptionModels(true);\n }\n }, [initialAuthType]);\n\n const defaultModel = String(\n (DEFAULT_SETTINGS.agent_settings?.llm as Record<string, unknown>)?.model ??\n \"\",\n );\n\n const getInitialView = React.useCallback(\n (\n currentSettings: Settings,\n filteredSchema: SettingsSchema,\n ): SettingsView => {\n const schemaView = inferInitialView(currentSettings, filteredSchema);\n if (schemaView !== \"basic\") {\n return schemaView;\n }\n\n const currentModel = currentSettings.llm_model ?? \"\";\n const trimmedBaseUrl = currentSettings.llm_base_url?.trim() ?? \"\";\n const hasCustomBaseUrl =\n trimmedBaseUrl.length > 0 &&\n !isProviderDefaultBaseUrl(currentModel, trimmedBaseUrl);\n\n return hasCustomBaseUrl ? \"all\" : \"basic\";\n },\n [],\n );\n\n const buildHeader = React.useCallback(\n ({ values, isDisabled, view, onChange }: SdkSectionHeaderProps) => {\n const modelValue =\n typeof values[\"llm.model\"] === \"string\" ? values[\"llm.model\"] : \"\";\n const baseUrlValue =\n typeof values[\"llm.base_url\"] === \"string\"\n ? values[\"llm.base_url\"]\n : \"\";\n const showOpenHandsApiKeyHelp = modelValue.startsWith(\"openhands/\");\n const authType = resolveLlmAuthType(values[LLM_AUTH_TYPE_KEY]);\n const isSubscriptionAuth = authType === LLM_AUTH_TYPE_SUBSCRIPTION;\n const shouldDisableSubscriptionControls =\n isDisabled || (isSubscriptionAuth && isWaitingForSubscriptionModels);\n const subscriptionModelValue = subscriptionModels?.includes(modelValue)\n ? modelValue\n : (subscriptionModels?.[0] ?? \"\");\n\n const apiKeyValue =\n typeof values[\"llm.api_key\"] === \"string\" ? values[\"llm.api_key\"] : \"\";\n // For embedded profile forms (create/edit) the global\n // `llm_api_key_set` flag is misleading: a brand-new profile would show a\n // \"key set\" indicator just because some other profile has a key. Reflect\n // the form's own key state instead so create mode starts visibly unset.\n const apiKeyIsSet = embedded\n ? apiKeyValue.length > 0\n : Boolean(settings?.llm_api_key_set);\n\n const renderApiKeyInput = (testId: string, helpTestId: string) => (\n <>\n <SettingsInput\n testId={testId}\n label={t(I18nKey.SETTINGS_FORM$API_KEY)}\n type=\"password\"\n className=\"w-full\"\n value={apiKeyValue}\n // eslint-disable-next-line i18next/no-literal-string -- masked-key sentinel, not translatable\n placeholder={apiKeyIsSet ? \"<hidden>\" : \"\"}\n onChange={(value) => onChange(\"llm.api_key\", value)}\n isDisabled={isDisabled}\n startContent={\n apiKeyIsSet ? <KeyStatusIcon isSet={apiKeyIsSet} /> : undefined\n }\n />\n\n <HelpLink\n testId={helpTestId}\n text={t(I18nKey.SETTINGS$DONT_KNOW_API_KEY)}\n linkText={t(I18nKey.SETTINGS$CLICK_FOR_INSTRUCTIONS)}\n href=\"https://docs.openhands.dev/usage/local-setup#getting-an-api-key\"\n />\n </>\n );\n\n const handleAuthTypeChange = (selectedKey: React.Key | null) => {\n const nextAuthType =\n selectedKey === LLM_AUTH_TYPE_SUBSCRIPTION\n ? LLM_AUTH_TYPE_SUBSCRIPTION\n : LLM_AUTH_TYPE_API_KEY;\n onChange(LLM_AUTH_TYPE_KEY, nextAuthType);\n\n if (nextAuthType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n setEnableSubscriptionModels(true);\n if (modelValue && !subscriptionModels?.includes(modelValue)) {\n lastApiKeyModelRef.current = modelValue;\n }\n const restoredSubscriptionModel =\n lastSubscriptionModelRef.current &&\n subscriptionModels?.includes(lastSubscriptionModelRef.current)\n ? lastSubscriptionModelRef.current\n : subscriptionModels?.[0];\n onChange(LLM_SUBSCRIPTION_VENDOR_KEY, OPENAI_SUBSCRIPTION_VENDOR);\n if (\n !subscriptionModels?.includes(modelValue) &&\n restoredSubscriptionModel\n ) {\n onChange(\"llm.model\", restoredSubscriptionModel);\n }\n return;\n }\n\n if (modelValue && subscriptionModels?.includes(modelValue)) {\n lastSubscriptionModelRef.current = modelValue;\n onChange(\"llm.model\", lastApiKeyModelRef.current ?? defaultModel);\n }\n };\n\n const renderAuthTypeInput = () => (\n <SettingsDropdownInput\n testId=\"llm-auth-type-input\"\n name={LLM_AUTH_TYPE_KEY}\n label={t(I18nKey.SETTINGS$LLM_AUTH_TYPE)}\n items={[\n {\n key: LLM_AUTH_TYPE_API_KEY,\n label: t(I18nKey.SETTINGS$LLM_AUTH_TYPE_API_KEY),\n },\n {\n key: LLM_AUTH_TYPE_SUBSCRIPTION,\n label: t(I18nKey.SETTINGS$LLM_AUTH_TYPE_SUBSCRIPTION),\n },\n ]}\n selectedKey={authType}\n isClearable={false}\n required\n isDisabled={shouldDisableSubscriptionControls}\n onSelectionChange={handleAuthTypeChange}\n />\n );\n\n const renderSubscriptionSettings = () => (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-subscription-settings\"\n >\n <SettingsDropdownInput\n testId=\"llm-subscription-model-input\"\n name=\"llm.subscription_model\"\n label={t(I18nKey.SETTINGS$SUBSCRIPTION_MODEL)}\n items={(subscriptionModels ?? []).map((model) => ({\n key: model,\n label: model,\n }))}\n selectedKey={subscriptionModelValue}\n isClearable={false}\n required\n isDisabled={\n shouldDisableSubscriptionControls || !subscriptionModels?.length\n }\n onSelectionChange={(selectedKey) => {\n const nextModel =\n typeof selectedKey === \"string\"\n ? selectedKey\n : subscriptionModels?.[0];\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n />\n <OpenAISubscriptionAuthCard isDisabled={isDisabled} />\n </div>\n );\n\n return (\n <div className=\"flex flex-col gap-6\">\n {view === \"basic\" ? (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-basic\"\n >\n {renderAuthTypeInput()}\n\n {isSubscriptionAuth ? (\n renderSubscriptionSettings()\n ) : (\n <>\n <ModelSelector\n currentModel={modelValue || undefined}\n onChange={(provider, model) => {\n const nextModel = buildModelId(provider, model);\n if (nextModel) {\n onChange(\"llm.model\", nextModel);\n }\n }}\n wrapperClassName=\"!flex-col !gap-6\"\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help\" />\n ) : null}\n\n {renderApiKeyInput(\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-input\",\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-help-anchor\",\n )}\n </>\n )}\n </div>\n ) : (\n <div\n className=\"flex flex-col gap-6\"\n data-testid=\"llm-settings-form-advanced\"\n >\n {renderAuthTypeInput()}\n\n {isSubscriptionAuth ? (\n renderSubscriptionSettings()\n ) : (\n <>\n <SettingsInput\n testId=\"llm-custom-model-input\"\n label={t(I18nKey.SETTINGS$CUSTOM_MODEL)}\n type=\"text\"\n className=\"w-full\"\n value={modelValue}\n placeholder={defaultModel}\n onChange={(value) => onChange(\"llm.model\", value)}\n isDisabled={isDisabled}\n />\n\n {showOpenHandsApiKeyHelp ? (\n <>\n {isFreeOpenHandsModel(modelValue) ? (\n <OpenHandsFreeModelsNote />\n ) : null}\n <OpenHandsApiKeyHelp testId=\"openhands-api-key-help-2\" />\n </>\n ) : null}\n\n <SettingsInput\n testId=\"base-url-input\"\n label={t(I18nKey.SETTINGS$BASE_URL)}\n type=\"text\"\n className=\"w-full\"\n value={baseUrlValue}\n // eslint-disable-next-line i18next/no-literal-string -- example value, not translatable\n placeholder=\"https://api.openai.com\"\n onChange={(value) => onChange(\"llm.base_url\", value)}\n isDisabled={isDisabled}\n />\n\n {renderApiKeyInput(\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-input\",\n // eslint-disable-next-line i18next/no-literal-string -- DOM id, not user-facing\n \"llm-api-key-help-anchor-advanced\",\n )}\n </>\n )}\n </div>\n )}\n </div>\n );\n },\n [\n defaultModel,\n embedded,\n isWaitingForSubscriptionModels,\n settings?.llm_api_key_set,\n subscriptionModels,\n t,\n ],\n );\n\n const buildPayload = React.useCallback(\n (\n defaultPayload: Record<string, unknown>,\n context: {\n values: Record<string, string | boolean>;\n dirty: Record<string, boolean>;\n view: SettingsView;\n },\n ) => {\n // defaultPayload is the wrapped diff (e.g.\n // `{ agent_settings_diff: { llm: { model: \"gpt-4\" } } }`); we only\n // mutate the inner `llm` object below.\n const agentSettings = structuredClone(\n (defaultPayload.agent_settings_diff as Record<string, unknown>) ?? {},\n );\n const llm = (agentSettings.llm ?? {}) as Record<string, unknown>;\n const authType = resolveLlmAuthType(context.values[LLM_AUTH_TYPE_KEY]);\n\n if (authType === LLM_AUTH_TYPE_SUBSCRIPTION) {\n llm.auth_type = LLM_AUTH_TYPE_SUBSCRIPTION;\n llm.subscription_vendor = OPENAI_SUBSCRIPTION_VENDOR;\n const model =\n typeof llm.model === \"string\"\n ? llm.model\n : String(context.values[\"llm.model\"] ?? \"\");\n const fallbackSubscriptionModel = subscriptionModels?.[0];\n if (\n !subscriptionModels?.includes(model) &&\n !fallbackSubscriptionModel\n ) {\n throw new Error(\"Subscription models are not loaded yet.\");\n }\n llm.model = subscriptionModels?.includes(model)\n ? model\n : fallbackSubscriptionModel;\n delete llm.api_key;\n delete llm.base_url;\n } else {\n if (context.dirty[LLM_AUTH_TYPE_KEY]) {\n llm.auth_type = LLM_AUTH_TYPE_API_KEY;\n llm.subscription_vendor = null;\n }\n if (context.view === \"basic\" && llm.model !== undefined) {\n llm.base_url = getSchemaFieldDefaultValue(schema, \"llm.base_url\");\n }\n }\n\n agentSettings.llm = llm;\n return { agent_settings_diff: agentSettings };\n },\n [schema, subscriptionModels],\n );\n\n return (\n <SdkSectionPage\n scope={scope}\n settingsSources={[\n {\n settingsSource: \"agent_settings\",\n sectionKeys: [\"llm\"],\n excludeKeys: LLM_EXCLUDED_KEYS,\n },\n ]}\n header={buildHeader}\n buildPayload={buildPayload}\n getInitialView={getInitialView}\n forceShowAdvancedView\n allowAllView\n onSaveSuccess={onSaveSuccess}\n initialValueOverrides={initialValueOverrides}\n embedded={embedded}\n hideSaveButton={hideSaveButton}\n suppressSuccessToast={suppressSuccessToast}\n onSaveControlChange={onSaveControlChange}\n testId=\"llm-settings-screen\"\n />\n );\n}\n\n/**\n * Default export for the route renders the LLM-profile management view for both\n * backend types. Both manage the LLM through named profiles — local via the\n * agent-server (`/api/profiles`), cloud via the app-server\n * (`/api/v1/settings/profiles`) — and the view is backend-agnostic because it\n * goes through ProfilesService, which routes per active backend.\n *\n * The LlmSettingsScreen component is also exported for embedded use cases\n * (e.g., onboarding, the profile create/edit form).\n *\n * Note: This is a route file, only the router should import the default export.\n * Other consumers should use the named export `LlmSettingsScreen` for embedded\n * use cases.\n */\nexport default function LlmSettingsRoute() {\n return <LlmSettingsLocalView />;\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require("../_virtual/_rolldown/runtime.cjs");const e=require("../i18n/declaration.cjs"),t=require("../api/agent-server-compatibility.cjs"),n=require("../utils/base-path.cjs"),r=require("../i18n/index.cjs"),i=require("../utils/custom-toast-handlers.cjs"),a=require("../constants/child-conversation.cjs"),o=require("../stores/goal-store.cjs"),s=require("../api/conversation-metadata-store.cjs"),c=require("../api/cloud/conversation-service.api.cjs"),l=require("../api/conversation-service/agent-server-conversation-service.api.cjs");var u=3e3,d=18e4,f=`openhands-child-conversation-launches:`,p=(e,t)=>({status:`error`,error:e,guidance:t}),m=e=>e.map(e=>`"${e}"`).join(` or `),h=e=>e?.trim()||null;function g(e){let t=e.target;if(!a.CHILD_CONVERSATION_TARGETS.includes(t))return{ok:!1,failure:p(`Unknown target ${JSON.stringify(e.target)}.`,`\`target\` must be exactly ${m(a.CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${a.LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`)};let n=h(e.task);if(!n)return{ok:!1,failure:p("`task` is empty.","`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.")};let r=h(e.repository),i=h(e.branch),o=h(e.isolation);return t===`local`&&(r||i)?{ok:!1,failure:p('`repository`/`branch` were passed with target="local".','A local child always runs in this conversation\'s workspace. Drop `repository` and `branch`, or use target="cloud" to run against a repository in a Cloud sandbox.')}:t===`cloud`&&o?{ok:!1,failure:p('`isolation` was passed with target="cloud".','Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target="local" to choose between "worktree" and "shared".')}:i&&!r?{ok:!1,failure:p("`branch` was passed without `repository`.",'Pass `repository` as "owner/repo" alongside `branch`, or drop `branch` to use the repository\'s default branch.')}:o&&!a.CHILD_CONVERSATION_ISOLATIONS.includes(o)?{ok:!1,failure:p(`Unknown isolation ${JSON.stringify(e.isolation)}.`,`\`isolation\` must be exactly ${m(a.CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${a.LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`)}:{ok:!0,params:{target:t,task:n,title:h(e.title),repository:r,branch:i,isolation:o??`worktree`}}}function _(e,t){let n=`${f}${e}`,r=[];try{let e=window.localStorage.getItem(n),t=e?JSON.parse(e):null;Array.isArray(t)&&(r=t.filter(e=>typeof e==`string`))}catch{}if(r.includes(t))return!1;try{window.localStorage.setItem(n,JSON.stringify([...r,t]))}catch{}return!0}function v(e){let t=n.buildAgentCanvasPath(e);return typeof window>`u`?t:new URL(t,window.location.origin).toString()}function y(){let e=t.getCachedAgentServerVersion();if(!e)return null;let n=t.compareAgentServerVersions(e,a.MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK);return n===null||n>=0?null:`Agent server ${e} does not persist parent/child conversation links (needs ${a.MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`}var b=e=>!!(e?.selected_repository||e?.selected_workspace),x=`The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.`;async function S(e,t){let n=await l.default.resolveConversationWorkingDir(t),r=s.getStoredConversationMetadata(t),i=i=>l.default.createConversation(e.task,void 0,void 0,r?{selected_repository:r.selected_repository,selected_branch:r.selected_branch,git_provider:r.git_provider}:null,n,i===`shared`?`local_repo`:`new_worktree`,t),a=e.isolation,o=null;a===`worktree`&&!b(r)&&(a=`shared`,o=`This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${x}`);let c;try{c=await i(a)}catch(e){if(a!==`worktree`)throw e;try{c=await i(`shared`)}catch{throw e}a=`shared`,o=`Creating a git worktree in this conversation's workspace failed (${e instanceof Error?e.message:String(e)}). ${x}`}let u=c.app_conversation_id??c.id;e.title&&await l.default.updateConversationTitle(u,e.title).catch(()=>void 0);let d=y();return{status:`launched`,target:`local`,conversation_id:u,url:v(`/conversations/${u}`),initial_status:c.status,title:e.title,workspace:n,isolation:a,...o?{isolation_note:o}:{},parent_link:!d,...d?{parent_link_note:d}:{}}}var C=e=>new Promise(t=>{setTimeout(t,e)});async function w(e,t){let n=Date.now()+d,r=e;for(;!r.app_conversation_id&&r.status!==`ERROR`&&!(Date.now()>=n);){await C(u);let e=await c.getCloudAppConversationStartTask(r.id,t).catch(()=>null);if(!e)break;r=e}return r}async function T(e,t){let n=c.pickCloudBackendForLaunch();if(!n)return p(`No OpenHands Cloud backend is connected in Agent Canvas.`,`Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target="local".`);let r=s.getStoredConversationMetadata(t),i=await w(await c.createCloudAppConversation({initial_message:{role:`user`,content:[{type:`text`,text:e.task}]},title:e.title,selected_repository:e.repository??r?.selected_repository??null,selected_branch:e.branch??null,git_provider:e.repository?null:r?.git_provider??null,parent_conversation_id:null},n),n);if(i.status===`ERROR`)return p(i.detail||`The Cloud conversation failed to start.`,`The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target="local".`);let a=i.app_conversation_id;return{status:`launched`,target:`cloud`,conversation_id:a,url:a?`${n.host.replace(/\/$/,``)}/conversations/${a}`:null,initial_status:i.status,title:e.title,start_task_id:i.id,backend:n.name,parent_link:!1,parent_link_note:`This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.`}}async function E(t,n){n.status===`error`?i.displayErrorToast(r.default.t(e.I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED,{error:n.error})):n.url&&i.displaySuccessToastWithLink(r.default.t(n.target===`cloud`?e.I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD:e.I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL),r.default.t(e.I18nKey.CHILD_CONVERSATION$OPEN),n.url),!o.useGoalStore.getState().statusByConversation[t]?.active&&await l.default.sendMessage(t,{role:`user`,content:[{type:`text`,text:`${a.CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(n)}`}]})}async function D(e,t,n){if(!_(t,n))return;let r=g(e),i;if(!r.ok)i=r.failure;else try{i=r.params.target===`cloud`?await T(r.params,t):await S(r.params,t)}catch(e){i=p(e instanceof Error?e.message:String(e),`The launch request failed. Report the error to the user; retry only if the cause looks transient.`)}await E(t,i).catch(e=>{console.warn(`[${a.LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,e)})}exports.handleLaunchChildConversationAction=D;
|
|
2
|
+
//# sourceMappingURL=child-conversation-launch.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"child-conversation-launch.cjs","names":[],"sources":["../../src/services/child-conversation-launch.ts"],"sourcesContent":["import i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\nimport {\n compareAgentServerVersions,\n getCachedAgentServerVersion,\n} from \"#/api/agent-server-compatibility\";\nimport {\n createCloudAppConversation,\n getCloudAppConversationStartTask,\n pickCloudBackendForLaunch,\n} from \"#/api/cloud/conversation-service.api\";\nimport type { Backend } from \"#/api/backend-registry/types\";\nimport {\n getStoredConversationMetadata,\n type ConversationMetadata,\n} from \"#/api/conversation-metadata-store\";\nimport AgentServerConversationService from \"#/api/conversation-service/agent-server-conversation-service.api\";\nimport type { AppConversationStartTask } from \"#/api/conversation-service/agent-server-conversation-service.types\";\nimport {\n CHILD_CONVERSATION_ISOLATIONS,\n CHILD_CONVERSATION_RESULT_PREFIX,\n CHILD_CONVERSATION_TARGETS,\n LAUNCH_CHILD_CONVERSATION_TOOL_NAME,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n type ChildConversationIsolation,\n type ChildConversationTarget,\n} from \"#/constants/child-conversation\";\nimport { useGoalStore } from \"#/stores/goal-store\";\nimport type { LaunchChildConversationAction } from \"#/types/agent-server/core\";\nimport { buildAgentCanvasPath } from \"#/utils/base-path\";\nimport {\n displayErrorToast,\n displaySuccessToastWithLink,\n} from \"#/utils/custom-toast-handlers\";\n\n/** Cadence and ceiling for waiting on a Cloud sandbox to expose its id. */\nconst CLOUD_START_POLL_INTERVAL_MS = 3_000;\nconst CLOUD_START_POLL_TIMEOUT_MS = 180_000;\n\nconst LEDGER_STORAGE_KEY_PREFIX = \"openhands-child-conversation-launches:\";\n\ninterface LaunchSuccess {\n status: \"launched\";\n target: ChildConversationTarget;\n conversation_id: string | null;\n url: string | null;\n initial_status: string;\n title: string | null;\n /** Local only: the directory the child inherited from this conversation. */\n workspace?: string;\n /** Local only: which isolation mode was actually applied. */\n isolation?: ChildConversationIsolation;\n /**\n * Present when the applied isolation is not the one that was asked for,\n * explaining why and what it means for the child.\n */\n isolation_note?: string;\n /** Cloud only: poll this if `conversation_id` is still null. */\n start_task_id?: string;\n /** Cloud only: which connected Cloud backend the child was launched on. */\n backend?: string;\n /** Whether the parent/child link was persisted server-side. */\n parent_link: boolean;\n /** Present when `parent_link` is false, explaining why. */\n parent_link_note?: string;\n}\n\ninterface LaunchFailure {\n status: \"error\";\n error: string;\n guidance: string;\n}\n\nexport type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;\n\ninterface ValidatedParams {\n target: ChildConversationTarget;\n task: string;\n title: string | null;\n repository: string | null;\n branch: string | null;\n isolation: ChildConversationIsolation;\n}\n\nconst failure = (error: string, guidance: string): LaunchFailure => ({\n status: \"error\",\n error,\n guidance,\n});\n\nconst quoted = (values: readonly string[]) =>\n values.map((value) => `\"${value}\"`).join(\" or \");\n\nconst blankToNull = (value: string | null | undefined) => {\n const trimmed = value?.trim();\n return trimmed ? trimmed : null;\n};\n\n/**\n * Validate what the agent-server cannot.\n *\n * The tool's JSON Schema is turned into a pydantic model with `extra=\"forbid\"`,\n * so unknown/misspelled parameter names, missing required parameters and wrong\n * types already fail server-side with a corrective message. `enum` is the gap:\n * the SDK advertises it to the LLM but drops it when building the model, so a\n * misspelled `target` reaches us intact and must be rejected here. The\n * cross-target rules (cloud-only / local-only parameters) are ours to enforce\n * too, since a schema cannot express them.\n */\nfunction validateLaunchParams(\n action: LaunchChildConversationAction,\n):\n | { ok: true; params: ValidatedParams }\n | { ok: false; failure: LaunchFailure } {\n const target = action.target as ChildConversationTarget;\n if (!CHILD_CONVERSATION_TARGETS.includes(target)) {\n return {\n ok: false,\n failure: failure(\n `Unknown target ${JSON.stringify(action.target)}.`,\n `\\`target\\` must be exactly ${quoted(CHILD_CONVERSATION_TARGETS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid target.`,\n ),\n };\n }\n\n const task = blankToNull(action.task);\n if (!task) {\n return {\n ok: false,\n failure: failure(\n \"`task` is empty.\",\n \"`task` must be a self-contained brief: the child conversation cannot see this one, so state the goal, constraints and expected output.\",\n ),\n };\n }\n\n const repository = blankToNull(action.repository);\n const branch = blankToNull(action.branch);\n const isolation = blankToNull(\n action.isolation,\n ) as ChildConversationIsolation | null;\n\n if (target === \"local\" && (repository || branch)) {\n return {\n ok: false,\n failure: failure(\n '`repository`/`branch` were passed with target=\"local\".',\n 'A local child always runs in this conversation\\'s workspace. Drop `repository` and `branch`, or use target=\"cloud\" to run against a repository in a Cloud sandbox.',\n ),\n };\n }\n\n if (target === \"cloud\" && isolation) {\n return {\n ok: false,\n failure: failure(\n '`isolation` was passed with target=\"cloud\".',\n 'Cloud children always run in their own isolated sandbox. Drop `isolation`, or use target=\"local\" to choose between \"worktree\" and \"shared\".',\n ),\n };\n }\n\n if (branch && !repository) {\n return {\n ok: false,\n failure: failure(\n \"`branch` was passed without `repository`.\",\n 'Pass `repository` as \"owner/repo\" alongside `branch`, or drop `branch` to use the repository\\'s default branch.',\n ),\n };\n }\n\n if (isolation && !CHILD_CONVERSATION_ISOLATIONS.includes(isolation)) {\n return {\n ok: false,\n failure: failure(\n `Unknown isolation ${JSON.stringify(action.isolation)}.`,\n `\\`isolation\\` must be exactly ${quoted(CHILD_CONVERSATION_ISOLATIONS)}. Nothing was launched — call ${LAUNCH_CHILD_CONVERSATION_TOOL_NAME} again with a valid isolation.`,\n ),\n };\n }\n\n return {\n ok: true,\n params: {\n target,\n task,\n title: blankToNull(action.title),\n repository,\n branch,\n isolation: isolation ?? \"worktree\",\n },\n };\n}\n\n/**\n * Remember which tool calls have already been acted on.\n *\n * Unlike the Canvas UI tool, a launch is not idempotent: replaying its\n * ActionEvent (a socket reconnect that falls back to `resend_mode: \"all\"`, or a\n * REST/WebSocket race after a reload) would start a second — on Cloud, billable\n * — conversation. Claim the tool call before any network work so a replay that\n * arrives mid-flight is dropped too.\n */\nfunction claimToolCall(parentConversationId: string, toolCallId: string) {\n const key = `${LEDGER_STORAGE_KEY_PREFIX}${parentConversationId}`;\n let handled: string[] = [];\n try {\n const raw = window.localStorage.getItem(key);\n const parsed: unknown = raw ? JSON.parse(raw) : null;\n if (Array.isArray(parsed)) {\n handled = parsed.filter((id): id is string => typeof id === \"string\");\n }\n } catch {\n // A corrupt ledger must not block the launch; start a fresh one.\n }\n\n if (handled.includes(toolCallId)) return false;\n\n try {\n window.localStorage.setItem(key, JSON.stringify([...handled, toolCallId]));\n } catch {\n // Storage full or unavailable — proceed, accepting replay risk over never\n // launching at all.\n }\n return true;\n}\n\nfunction absoluteCanvasUrl(path: string) {\n const canvasPath = buildAgentCanvasPath(path);\n if (typeof window === \"undefined\") return canvasPath;\n return new URL(canvasPath, window.location.origin).toString();\n}\n\n/**\n * `parent_conversation_id` on the local start request landed in agent-server\n * 1.37.1. Older servers ignore unknown fields, so the child is created without\n * the link — report that rather than letting the agent assume a relationship\n * that does not exist.\n */\nfunction localParentLinkNote(): string | null {\n const version = getCachedAgentServerVersion();\n if (!version) return null;\n const comparison = compareAgentServerVersions(\n version,\n MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK,\n );\n if (comparison === null || comparison >= 0) return null;\n return `Agent server ${version} does not persist parent/child conversation links (needs ${MIN_AGENT_SERVER_VERSION_FOR_PARENT_LINK}); the child was created but is not linked to this conversation.`;\n}\n\n/**\n * Whether the parent's workspace can host a git worktree.\n *\n * A conversation started without a repository or an attached workspace runs in\n * a scratch directory that is `git init`-ed but never committed to. `git\n * worktree add` cannot branch from an unborn HEAD, and the agent-server raises\n * the resulting error straight out of its start-conversation handler as a 500,\n * so asking for a worktree there fails the whole launch. Conversation metadata\n * is only persisted when a repository or an explicit workspace was chosen (see\n * `createConversation`), so its absence pinpoints exactly those scratch\n * workspaces. Anything else is attempted and falls back on failure, because the\n * frontend cannot inspect the workspace's git state directly.\n */\nconst parentSupportsWorktree = (\n metadata: ConversationMetadata | null,\n): boolean => !!(metadata?.selected_repository || metadata?.selected_workspace);\n\nconst SHARED_FALLBACK_CONSEQUENCE =\n \"The child was launched in this conversation's directory instead, so it can see work in progress here and the two agents may conflict over the same files.\";\n\nasync function launchLocalChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n // The agent-server rejects a parent whose workspace differs from the child's,\n // so the child requests the parent's own directory. `worktree` then carves an\n // isolated worktree out of it, which is what keeps siblings from colliding.\n const workspace =\n await AgentServerConversationService.resolveConversationWorkingDir(\n parentConversationId,\n );\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n\n const createChild = (isolation: ChildConversationIsolation) =>\n AgentServerConversationService.createConversation(\n params.task,\n undefined,\n undefined,\n parentMetadata\n ? {\n selected_repository: parentMetadata.selected_repository,\n selected_branch: parentMetadata.selected_branch,\n git_provider: parentMetadata.git_provider,\n }\n : null,\n workspace,\n isolation === \"shared\" ? \"local_repo\" : \"new_worktree\",\n parentConversationId,\n );\n\n let isolation = params.isolation;\n let isolationNote: string | null = null;\n\n if (isolation === \"worktree\" && !parentSupportsWorktree(parentMetadata)) {\n isolation = \"shared\";\n isolationNote = `This conversation's workspace is a scratch directory with no commits, which git cannot cut a worktree from. ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n let startTask;\n try {\n startTask = await createChild(isolation);\n } catch (worktreeError) {\n if (isolation !== \"worktree\") throw worktreeError;\n // Creating the worktree is the one part of a local launch that can fail on\n // an otherwise healthy workspace, so retry without it rather than losing\n // the launch. Anything else fails both attempts and is reported as-is.\n try {\n startTask = await createChild(\"shared\");\n } catch {\n throw worktreeError;\n }\n isolation = \"shared\";\n isolationNote = `Creating a git worktree in this conversation's workspace failed (${worktreeError instanceof Error ? worktreeError.message : String(worktreeError)}). ${SHARED_FALLBACK_CONSEQUENCE}`;\n }\n\n const conversationId = startTask.app_conversation_id ?? startTask.id;\n\n if (params.title) {\n // Local start requests carry no title — only `autotitle`. Best effort: a\n // failed rename must not fail the launch.\n await AgentServerConversationService.updateConversationTitle(\n conversationId,\n params.title,\n ).catch(() => undefined);\n }\n\n const parentLinkNote = localParentLinkNote();\n return {\n status: \"launched\",\n target: \"local\",\n conversation_id: conversationId,\n url: absoluteCanvasUrl(`/conversations/${conversationId}`),\n initial_status: startTask.status,\n title: params.title,\n workspace,\n isolation,\n ...(isolationNote ? { isolation_note: isolationNote } : {}),\n parent_link: !parentLinkNote,\n ...(parentLinkNote ? { parent_link_note: parentLinkNote } : {}),\n };\n}\n\nconst delay = (ms: number) =>\n new Promise((resolve) => {\n setTimeout(resolve, ms);\n });\n\n/**\n * Wait for the Cloud start task to expose its conversation id.\n *\n * The Cloud API provisions a sandbox asynchronously and only fills in\n * `app_conversation_id` once the task reaches READY, so without this the agent\n * would get a result with no conversation to open. Bounded: on timeout the\n * caller reports the still-provisioning task instead of hanging.\n */\nasync function waitForCloudConversationId(\n task: AppConversationStartTask,\n backend: Backend,\n): Promise<AppConversationStartTask> {\n const deadline = Date.now() + CLOUD_START_POLL_TIMEOUT_MS;\n let latest = task;\n while (!latest.app_conversation_id && latest.status !== \"ERROR\") {\n if (Date.now() >= deadline) break;\n\n await delay(CLOUD_START_POLL_INTERVAL_MS);\n\n const next = await getCloudAppConversationStartTask(\n latest.id,\n backend,\n ).catch(() => null);\n if (!next) break;\n latest = next;\n }\n return latest;\n}\n\nasync function launchCloudChild(\n params: ValidatedParams,\n parentConversationId: string,\n): Promise<LaunchChildConversationResult> {\n const backend = pickCloudBackendForLaunch();\n if (!backend) {\n return failure(\n \"No OpenHands Cloud backend is connected in Agent Canvas.\",\n 'Ask the user to connect OpenHands Cloud from the backend picker, then call this tool again — or relaunch now with target=\"local\".',\n );\n }\n\n const parentMetadata = getStoredConversationMetadata(parentConversationId);\n const startTask = await createCloudAppConversation(\n {\n initial_message: {\n role: \"user\",\n content: [{ type: \"text\", text: params.task }],\n },\n title: params.title,\n // Fall back to this conversation's repository so a bare `target: \"cloud\"`\n // call still lands the child on the code the user is working on. The\n // provider only travels with the inherited repository — it may not apply\n // to one the agent named itself.\n selected_repository:\n params.repository ?? parentMetadata?.selected_repository ?? null,\n selected_branch: params.branch ?? null,\n git_provider: params.repository\n ? null\n : (parentMetadata?.git_provider ?? null),\n // The parent runs on the local agent-server, so its id means nothing to\n // Cloud. Sending it would only hide the child from the Cloud\n // conversation list, which filters out anything with a parent.\n parent_conversation_id: null,\n },\n backend,\n );\n\n const settled = await waitForCloudConversationId(startTask, backend);\n if (settled.status === \"ERROR\") {\n return failure(\n settled.detail || \"The Cloud conversation failed to start.\",\n 'The Cloud sandbox could not be provisioned. Report this to the user; you can retry, or fall back to target=\"local\".',\n );\n }\n\n const conversationId = settled.app_conversation_id;\n return {\n status: \"launched\",\n target: \"cloud\",\n conversation_id: conversationId,\n url: conversationId\n ? `${backend.host.replace(/\\/$/, \"\")}/conversations/${conversationId}`\n : null,\n initial_status: settled.status,\n title: params.title,\n start_task_id: settled.id,\n backend: backend.name,\n // Cloud children of a local parent carry no server-side link; see above.\n parent_link: false,\n parent_link_note:\n \"This conversation runs on the local agent server, so OpenHands Cloud has no parent to link the child to.\",\n };\n}\n\n/**\n * Hand the outcome back to the agent and the user.\n *\n * Client tools are acknowledged by the agent-server before the browser does any\n * work, so a message is the only way to give the agent the child's id or tell\n * it how to fix a malformed call. `sendMessage` runs the agent, which is what\n * makes it relay the result to the user in its next turn.\n */\nasync function reportLaunchResult(\n parentConversationId: string,\n result: LaunchChildConversationResult,\n) {\n if (result.status === \"error\") {\n displayErrorToast(\n i18n.t(I18nKey.CHILD_CONVERSATION$LAUNCH_FAILED, {\n error: result.error,\n }),\n );\n } else if (result.url) {\n displaySuccessToastWithLink(\n i18n.t(\n result.target === \"cloud\"\n ? I18nKey.CHILD_CONVERSATION$LAUNCHED_CLOUD\n : I18nKey.CHILD_CONVERSATION$LAUNCHED_LOCAL,\n ),\n i18n.t(I18nKey.CHILD_CONVERSATION$OPEN),\n result.url,\n );\n }\n\n // The agent-server cancels an active `/goal` loop on any inbound message, so\n // a launch must not silently end one. The toast above still tells the user\n // what happened.\n const goalStatus =\n useGoalStore.getState().statusByConversation[parentConversationId];\n if (goalStatus?.active) return;\n\n await AgentServerConversationService.sendMessage(parentConversationId, {\n role: \"user\",\n content: [\n {\n type: \"text\",\n text: `${CHILD_CONVERSATION_RESULT_PREFIX}${JSON.stringify(result)}`,\n },\n ],\n });\n}\n\n/**\n * Execute a `launch_child_conversation` tool call.\n *\n * Never rejects: every failure is turned into corrective guidance for the\n * agent, because the agent-server has already told it the call succeeded.\n */\nexport async function handleLaunchChildConversationAction(\n action: LaunchChildConversationAction,\n parentConversationId: string,\n toolCallId: string,\n): Promise<void> {\n if (!claimToolCall(parentConversationId, toolCallId)) return;\n\n const validation = validateLaunchParams(action);\n let result: LaunchChildConversationResult;\n if (!validation.ok) {\n result = validation.failure;\n } else {\n try {\n result =\n validation.params.target === \"cloud\"\n ? await launchCloudChild(validation.params, parentConversationId)\n : await launchLocalChild(validation.params, parentConversationId);\n } catch (error) {\n result = failure(\n error instanceof Error ? error.message : String(error),\n \"The launch request failed. Report the error to the user; retry only if the cause looks transient.\",\n );\n }\n }\n\n await reportLaunchResult(parentConversationId, result).catch((error) => {\n console.warn(\n `[${LAUNCH_CHILD_CONVERSATION_TOOL_NAME}] Failed to report the launch result:`,\n error,\n );\n });\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type ChildConversationIsolation, type ChildConversationTarget } from "#/constants/child-conversation";
|
|
2
|
+
import type { LaunchChildConversationAction } from "#/types/agent-server/core";
|
|
3
|
+
interface LaunchSuccess {
|
|
4
|
+
status: "launched";
|
|
5
|
+
target: ChildConversationTarget;
|
|
6
|
+
conversation_id: string | null;
|
|
7
|
+
url: string | null;
|
|
8
|
+
initial_status: string;
|
|
9
|
+
title: string | null;
|
|
10
|
+
/** Local only: the directory the child inherited from this conversation. */
|
|
11
|
+
workspace?: string;
|
|
12
|
+
/** Local only: which isolation mode was actually applied. */
|
|
13
|
+
isolation?: ChildConversationIsolation;
|
|
14
|
+
/**
|
|
15
|
+
* Present when the applied isolation is not the one that was asked for,
|
|
16
|
+
* explaining why and what it means for the child.
|
|
17
|
+
*/
|
|
18
|
+
isolation_note?: string;
|
|
19
|
+
/** Cloud only: poll this if `conversation_id` is still null. */
|
|
20
|
+
start_task_id?: string;
|
|
21
|
+
/** Cloud only: which connected Cloud backend the child was launched on. */
|
|
22
|
+
backend?: string;
|
|
23
|
+
/** Whether the parent/child link was persisted server-side. */
|
|
24
|
+
parent_link: boolean;
|
|
25
|
+
/** Present when `parent_link` is false, explaining why. */
|
|
26
|
+
parent_link_note?: string;
|
|
27
|
+
}
|
|
28
|
+
interface LaunchFailure {
|
|
29
|
+
status: "error";
|
|
30
|
+
error: string;
|
|
31
|
+
guidance: string;
|
|
32
|
+
}
|
|
33
|
+
export type LaunchChildConversationResult = LaunchSuccess | LaunchFailure;
|
|
34
|
+
/**
|
|
35
|
+
* Execute a `launch_child_conversation` tool call.
|
|
36
|
+
*
|
|
37
|
+
* Never rejects: every failure is turned into corrective guidance for the
|
|
38
|
+
* agent, because the agent-server has already told it the call succeeded.
|
|
39
|
+
*/
|
|
40
|
+
export declare function handleLaunchChildConversationAction(action: LaunchChildConversationAction, parentConversationId: string, toolCallId: string): Promise<void>;
|
|
41
|
+
export {};
|