@openhands/agent-canvas 1.0.0-rc.2 → 1.0.0-rc.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/README.windows.md +2 -2
- package/build/assets/acp-providers-C55k29rf.js +1 -0
- package/build/assets/{add-backend-modal-CIfhseZn.js → add-backend-modal-BdYxoUdL.js} +1 -1
- package/build/assets/agent-server-conversation-service.api-js3oYcdU.js +5 -0
- package/build/assets/{agent-settings-B6htMeS2.js → agent-settings-BNrffu3I.js} +1 -1
- package/build/assets/{alert-banner-D41ZKDZT.js → alert-banner-BzU93oDh.js} +1 -1
- package/build/assets/{analytics-consent-form-modal-TINgM_jV.js → analytics-consent-form-modal-Ce-_Skcd.js} +1 -1
- package/build/assets/{api-key-entry-screen-Di1vHgIU.js → api-key-entry-screen-CGyS-Cna.js} +1 -1
- package/build/assets/{app-settings-CjCa1MOB.js → app-settings-DmOs4oik.js} +1 -1
- package/build/assets/{automation-detail-DPoxzTdV.js → automation-detail-DLbCVJCw.js} +1 -1
- package/build/assets/{automations-list-DwUEe2Ea.js → automations-list-Ces5shz5.js} +1 -1
- package/build/assets/{backend-form-modal-B6q897ZX.js → backend-form-modal-BUy-PzZN.js} +1 -1
- package/build/assets/{backend-synced-settings-badge-BwTawSCE.js → backend-synced-settings-badge-1A63yxGs.js} +1 -1
- package/build/assets/browser-B8bp1IqT.js +5 -0
- package/build/assets/{browser-tab-DiRTKik8.js → browser-tab-CiBRWB4X.js} +1 -1
- package/build/assets/chat-send-button-qKFZuB9E.js +1 -0
- package/build/assets/{circle-plus-check-toggle-B3_W6-nt.js → circle-plus-check-toggle-CboDp7XB.js} +1 -1
- package/build/assets/{condenser-settings-DczjwkIp.js → condenser-settings-FCBjvqSo.js} +1 -1
- package/build/assets/conversation-CT8AvxEH.js +1 -0
- package/build/assets/conversation-panel-DqDDs4WZ.js +1 -0
- package/build/assets/conversation-service.api-y_eB_43m.js +1 -0
- package/build/assets/conversation-store-CiYGBud3.js +6 -0
- package/build/assets/conversation-vNuLKgz6.js +19 -0
- package/build/assets/conversation-websocket-context-Bb4XBXoc.js +3 -0
- package/build/assets/{device-verify-BVl4GEvt.js → device-verify-BOQz2LJQ.js} +1 -1
- package/build/assets/{edit-automation-modal-kc_FzbzK.js → edit-automation-modal-C-oC5tis.js} +1 -1
- package/build/assets/{entry.client-CWkbusD1.js → entry.client-Db3BpFL_.js} +2 -2
- package/build/assets/{enum-filter-dropdown-DlY0Q3fj.js → enum-filter-dropdown-DFwoVtOw.js} +1 -1
- package/build/assets/{extensions-hub-NbQnt-cn.js → extensions-hub-BxZbAtjP.js} +1 -1
- package/build/assets/extensions-navigation-BIb-vAa2.js +1 -0
- package/build/assets/{files-tab-mK7Mdyuf.js → files-tab-DpulQlIo.js} +1 -1
- package/build/assets/{git-control-bar-branch-button-C1qmab0K.js → git-control-bar-branch-button-D1uam-nI.js} +1 -1
- package/build/assets/home-sJAFzI6v.js +1 -0
- package/build/assets/install-server-modal-BjEzlLF5.js +1 -0
- package/build/assets/{launch-BADsYeGp.js → launch-DgtB1JTX.js} +1 -1
- package/build/assets/llm-settings-B8kPJ0Of.js +1 -0
- package/build/assets/llm-settings-CjUpPsvA.js +1 -0
- package/build/assets/{manage-backends-modal-sH7l5NgI.js → manage-backends-modal-CTA-2x4F.js} +1 -1
- package/build/assets/manifest-d3e8504d.js +1 -0
- package/build/assets/{markdown-renderer-CZq_UdmE.js → markdown-renderer-oOUs3tw5.js} +1 -1
- package/build/assets/{mcp-DdQ72_AO.js → mcp-kJYdM8aJ.js} +1 -1
- package/build/assets/messages-BFJXB6MW.js +36 -0
- package/build/assets/model-selector-z_QOzaRV.js +1 -0
- package/build/assets/{onboarding-DCL9stdH.js → onboarding-CZ_7nd-M.js} +1 -1
- package/build/assets/{path-utils-CNd_jqv_.js → path-utils-0KWEiRs9.js} +1 -1
- package/build/assets/{planner-tab-QBnZgIXd.js → planner-tab-CNmJiZ22.js} +1 -1
- package/build/assets/{recommended-automations-launcher-B01jchhe.js → recommended-automations-launcher-B1kfmFTQ.js} +8 -10
- package/build/assets/{root-BBV8Ew9E.js → root-CIZ7Rv1X.js} +2 -2
- package/build/assets/root-layout-B5ijp9At.js +2 -0
- package/build/assets/{sdk-section-page-BQKe3asw.js → sdk-section-page-DTyvCc52.js} +1 -1
- package/build/assets/{sdk-settings-schema-DoRnefvb.js → sdk-settings-schema-B0KrqqPX.js} +1 -1
- package/build/assets/{secrets-settings-D2EfzdpC.js → secrets-settings-BQNSDLKS.js} +1 -1
- package/build/assets/{settings-B7jVceyu.js → settings-BUlJrO_h.js} +1 -1
- package/build/assets/{settings-dropdown-input-BD7ziSoR.js → settings-dropdown-input-BUk4m23x.js} +1 -1
- package/build/assets/{settings-index-Xj0v9Oas.js → settings-index-DE91Eahc.js} +1 -1
- package/build/assets/{settings-modal-CDBy1S9S.js → settings-modal-CCaPYVqW.js} +1 -1
- package/build/assets/{settings-service.api-CTQ-LpAA.js → settings-service.api-4u2RKC8k.js} +1 -1
- package/build/assets/{settings-switch-CSHSqH99.js → settings-switch-BQiAS9ga.js} +1 -1
- package/build/assets/{settings-utils-BCbzc6-u.js → settings-utils-chxTa1vn.js} +1 -1
- package/build/assets/{shared-conversation-BHEbOdHj.js → shared-conversation-sBPLAawM.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-YYPXGikp.js → sidebar-mobile-menu-toggle-BDXWzhyB.js} +1 -1
- package/build/assets/{sidebar-nav-link-CiXbBMQx.js → sidebar-nav-link-Bhlzlhp8.js} +1 -1
- package/build/assets/{skill-card-pill-row-BXILn-GK.js → skill-card-pill-row-Bg5joQf6.js} +1 -1
- package/build/assets/{skills-plugins-Bs5HiF1O.js → skills-plugins-fihjo1KZ.js} +1 -1
- package/build/assets/{skills-settings-CQYxMmWf.js → skills-settings-NQnuMwZP.js} +1 -1
- package/build/assets/{styled-tooltip-DEr7oa0m.js → styled-tooltip-GXy1qxsO.js} +1 -1
- package/build/assets/{task-list-tab-R9N3Wd-U.js → task-list-tab-Hl9BuVfq.js} +1 -1
- package/build/assets/telemetry-j9SLrtY7.js +2 -0
- package/build/assets/{terminal-BTM3UFcQ.js → terminal-DRwe8--D.js} +1 -1
- package/build/assets/{use-active-conversation-BY5F6A1G.js → use-active-conversation-DJGoI9Mc.js} +1 -1
- package/build/assets/{use-agent-settings-schema-CsuMq16G.js → use-agent-settings-schema-DtusObuC.js} +1 -1
- package/build/assets/{use-agent-state-DX5NKEa_.js → use-agent-state-BXgWhFh6.js} +1 -1
- package/build/assets/{use-create-conversation-4iJytCT1.js → use-create-conversation-CzvaVA5A.js} +1 -1
- package/build/assets/use-event-store-DSpvASbC.js +1 -0
- package/build/assets/{use-get-secrets-BMnFFBUx.js → use-get-secrets-DiLgP147.js} +1 -1
- package/build/assets/use-handle-plan-click-CUZwmKIk.js +1 -0
- package/build/assets/{use-launch-skill-in-chat-BoqKmEHC.js → use-launch-skill-in-chat-ClRJlIx7.js} +1 -1
- package/build/assets/use-runtime-is-ready-CWkGQFsb.js +1 -0
- package/build/assets/{use-save-settings-hK6LYt0s.js → use-save-settings-CP23emUY.js} +1 -1
- package/build/assets/use-settings-0qFqC-r6.js +1 -0
- package/build/assets/{use-settings-nav-items-oZ-BlOWX.js → use-settings-nav-items-C6YxUn4h.js} +1 -1
- package/build/assets/{use-task-list-DydbuRFM.js → use-task-list-JPudBRv3.js} +1 -1
- package/build/assets/{use-tracking-DQU60djN.js → use-tracking-CjLZgh8X.js} +1 -1
- package/build/assets/{use-unified-vscode-url-D2Buvmxj.js → use-unified-vscode-url-DSn2tT08.js} +1 -1
- package/build/assets/{use-user-conversation-DwOGM1lc.js → use-user-conversation-CQ5IwnZk.js} +1 -1
- package/build/assets/{vendor~browser-3J6WDaAB.js → vendor~browser-Dwwc84Zf.js} +1 -1
- package/build/assets/{vendor~browser-tab-DYZ-OmbT.js → vendor~browser-tab-Bhohe29F.js} +1 -1
- package/build/assets/{vendor~files-tab-Diy4WrQz.js → vendor~files-tab-B447_Zns.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CWwn0K2j.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-CQQAW8hY.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-dZ3D8RVL.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DFsI4CLy.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-BighOCzm.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DcvlNgbn.js} +1 -1
- package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D13hiNGV.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-ojk_J4bB.js} +1 -1
- package/build/assets/{vendor~launch-D65Vw0VZ.js → vendor~launch-BdXJCmwc.js} +1 -1
- package/build/assets/{vendor~root-layout~conversation-panel~conversation~shared-conversation-BJPgfJoU.js → vendor~root-layout~conversation-panel~conversation~shared-conversation-DToubnIU.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~jpfhx3ls-BkicN-14.js → vendor~root-layout~home~conversation-panel~conversation~extensions-hub~skills-settings~mcp~~jpfhx3ls-zdl_Rltz.js} +2 -2
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~jxrvuot9-Cwz6a1DC.js → vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~jxrvuot9-BaCzvZac.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-C37jLHRk.js → vendor~root-layout~home~conversation-panel~conversation~shared-conversation~alert-banner~pl~rqjteh0a-BwbkftxT.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~automations-list-JQ-neDIa.js → vendor~root-layout~home~mcp~automations-list-CtdIEhjQ.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~automations-list-BTTZ58Mb.js → vendor~root-layout~home~mcp~automations-list-DGOI7kQz.js} +1 -1
- package/build/assets/{vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-UYEKKX0Y.js → vendor~root-layout~home~mcp~llm-settings~agent-settings~condenser-settings~verification-set~o7tv66sg-iPAfRWNQ.js} +2 -2
- package/build/assets/{verification-settings-Rabe5TpK.js → verification-settings-Cm02KmeI.js} +1 -1
- package/build/assets/{vscode-tab-zLD5Z-PP.js → vscode-tab-AF70Yke0.js} +1 -1
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +1 -1
- package/build/locales/ca/openhands.json +1 -1
- package/build/locales/de/openhands.json +1 -1
- package/build/locales/en/openhands.json +1 -1
- package/build/locales/es/openhands.json +1 -1
- package/build/locales/fr/openhands.json +1 -1
- package/build/locales/it/openhands.json +1 -1
- package/build/locales/ja/openhands.json +1 -1
- package/build/locales/ko-KR/openhands.json +1 -1
- package/build/locales/no/openhands.json +1 -1
- package/build/locales/pt/openhands.json +1 -1
- package/build/locales/tr/openhands.json +1 -1
- package/build/locales/uk/openhands.json +1 -1
- package/build/locales/zh-CN/openhands.json +1 -1
- package/build/locales/zh-TW/openhands.json +1 -1
- 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.js +90 -85
- package/dist/api/agent-server-adapter.js.map +1 -1
- package/dist/api/app-preferences-store.cjs.map +1 -1
- package/dist/api/app-preferences-store.d.ts +6 -2
- package/dist/api/app-preferences-store.js.map +1 -1
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs +1 -1
- 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 +9 -9
- package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map +1 -1
- package/dist/components/conversation-events/chat/event-message.cjs +1 -1
- package/dist/components/conversation-events/chat/event-message.cjs.map +1 -1
- package/dist/components/conversation-events/chat/event-message.js +80 -71
- package/dist/components/conversation-events/chat/event-message.js.map +1 -1
- package/dist/components/features/automations/recommended-automations-launcher.d.ts +7 -1
- package/dist/components/features/home/workspace-selection-form.d.ts +1 -0
- package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +8 -0
- package/dist/components/features/sidebar/sidebar.cjs +1 -1
- package/dist/components/features/sidebar/sidebar.js +6 -6
- 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.d.ts +1 -1
- package/dist/components/features/skills/extensions-navigation.js +27 -29
- package/dist/components/features/skills/extensions-navigation.js.map +1 -1
- package/dist/i18n/translation.cjs +1 -1
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +15 -15
- package/dist/i18n/translation.js.map +1 -1
- package/dist/locales/ar/openhands.json +1 -1
- package/dist/locales/ca/openhands.json +1 -1
- package/dist/locales/de/openhands.json +1 -1
- package/dist/locales/en/openhands.json +1 -1
- package/dist/locales/es/openhands.json +1 -1
- package/dist/locales/fr/openhands.json +1 -1
- package/dist/locales/it/openhands.json +1 -1
- package/dist/locales/ja/openhands.json +1 -1
- package/dist/locales/ko-KR/openhands.json +1 -1
- package/dist/locales/no/openhands.json +1 -1
- package/dist/locales/pt/openhands.json +1 -1
- package/dist/locales/tr/openhands.json +1 -1
- package/dist/locales/uk/openhands.json +1 -1
- package/dist/locales/zh-CN/openhands.json +1 -1
- package/dist/locales/zh-TW/openhands.json +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.cjs.map +1 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js +10 -1
- package/dist/node_modules/@openhands/typescript-client/dist/models/acp.js.map +1 -1
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +2 -2
- package/dist/package.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 +21 -21
- package/dist/routes/llm-settings.js.map +1 -1
- package/dist/types/agent-server/core/events/index.d.ts +1 -0
- package/dist/types/agent-server/core/events/streaming-delta-event.d.ts +7 -0
- package/dist/types/agent-server/core/openhands-event.d.ts +2 -2
- 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 +2 -0
- package/dist/types/agent-server/type-guards.js +3 -3
- package/dist/types/agent-server/type-guards.js.map +1 -1
- package/dist/utils/handle-event-for-ui.cjs +1 -1
- package/dist/utils/handle-event-for-ui.cjs.map +1 -1
- package/dist/utils/handle-event-for-ui.js +69 -13
- package/dist/utils/handle-event-for-ui.js.map +1 -1
- package/dist/utils/mcp-config.cjs +1 -1
- package/dist/utils/mcp-config.cjs.map +1 -1
- package/dist/utils/mcp-config.js +27 -19
- package/dist/utils/mcp-config.js.map +1 -1
- package/dist/utils/mcp-marketplace-utils.cjs +1 -1
- package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
- package/dist/utils/mcp-marketplace-utils.js +38 -18
- package/dist/utils/mcp-marketplace-utils.js.map +1 -1
- package/dist/utils/normalize-display-model.cjs +1 -1
- package/dist/utils/normalize-display-model.cjs.map +1 -1
- package/dist/utils/normalize-display-model.js +8 -7
- package/dist/utils/normalize-display-model.js.map +1 -1
- package/dist/utils/openhands-llm.cjs +1 -1
- package/dist/utils/openhands-llm.cjs.map +1 -1
- package/dist/utils/openhands-llm.d.ts +13 -0
- package/dist/utils/openhands-llm.js +9 -3
- package/dist/utils/openhands-llm.js.map +1 -1
- package/package.json +2 -2
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/dev-safe.mjs +53 -118
- package/scripts/dev-with-automation.mjs +58 -4
- package/scripts/runtime-services-info.mjs +199 -0
- package/scripts/static-server.mjs +42 -4
- package/build/assets/acp-providers-DZEi8wDz.js +0 -1
- package/build/assets/agent-server-conversation-service.api-B82pNNTm.js +0 -5
- package/build/assets/browser-DTei6kki.js +0 -5
- package/build/assets/chat-send-button-06dIuWXm.js +0 -1
- package/build/assets/conversation-CBlJiDaV.js +0 -19
- package/build/assets/conversation-Dss8XCN_.js +0 -1
- package/build/assets/conversation-panel-B5sVpsz5.js +0 -1
- package/build/assets/conversation-service.api-B4s-xIOK.js +0 -1
- package/build/assets/conversation-store-kHcewy1E.js +0 -1
- package/build/assets/conversation-websocket-context-C2yKCqvj.js +0 -3
- package/build/assets/extensions-navigation-oOk5yl8X.js +0 -1
- package/build/assets/home-ChuA06Hv.js +0 -1
- package/build/assets/install-server-modal--lZ1HSHB.js +0 -1
- package/build/assets/llm-settings-D477P0Lg.js +0 -1
- package/build/assets/llm-settings-DtlQ7i4C.js +0 -1
- package/build/assets/manifest-17af0b17.js +0 -1
- package/build/assets/messages-luW9zf1w.js +0 -36
- package/build/assets/middleware-B5rcobhi.js +0 -6
- package/build/assets/model-selector-DzQRgNGZ.js +0 -1
- package/build/assets/root-layout-pQASEqtQ.js +0 -2
- package/build/assets/sidebar-store-B76R2gP8.js +0 -1
- package/build/assets/telemetry-DCrd7gnV.js +0 -2
- package/build/assets/use-event-store-Rw1YbvLm.js +0 -1
- package/build/assets/use-handle-plan-click-DYd5a6zN.js +0 -1
- package/build/assets/use-runtime-is-ready-DP-KKHO6.js +0 -1
- package/build/assets/use-settings-Clf0Y_P3.js +0 -1
- /package/build/assets/{automation-LZB0MjdV.js → automation-BzmydDjr.js} +0 -0
- /package/build/assets/{browser-store-BjhV_9wS.js → browser-store-C5uQbbIi.js} +0 -0
- /package/build/assets/{checkmark-BB7QCG5T.js → checkmark-SEZPnU2I.js} +0 -0
- /package/build/assets/{chevron-left-small-CuuwpRi9.js → chevron-left-small-x9_-ucHG.js} +0 -0
- /package/build/assets/{close-Cz0OAgTy.js → close-CbOsKMRg.js} +0 -0
- /package/build/assets/{code-tag-Cz9AIz-X.js → code-tag-C0vR_IQH.js} +0 -0
- /package/build/assets/{command-store-DAd3K0d_.js → command-store-BUWgE-vZ.js} +0 -0
- /package/build/assets/{confirmation-modal-C_lds1Tb.js → confirmation-modal-BPgWqWRI.js} +0 -0
- /package/build/assets/{context-menu-list-item-DhG1Ln5m.js → context-menu-list-item-CHWCx1Mc.js} +0 -0
- /package/build/assets/{conversation-state-store-BUU90dVq.js → conversation-state-store-DpgQaK_O.js} +0 -0
- /package/build/assets/{conversation-tab-empty-state-5bW9CQke.js → conversation-tab-empty-state-Ba5hbJhn.js} +0 -0
- /package/build/assets/{copy-CA1Dblua.js → copy-CAxUvM-U.js} +0 -0
- /package/build/assets/{dist-CBUfAk0Z.js → dist-CeJSjcAI.js} +0 -0
- /package/build/assets/{ellipsis-button-DRRmCrWi.js → ellipsis-button-CiLx8dqc.js} +0 -0
- /package/build/assets/{environment-switch-overlay-BrHKX6_Z.js → environment-switch-overlay-Cow6h62p.js} +0 -0
- /package/build/assets/{file-DM0ihEsO.js → file-BJCSojij.js} +0 -0
- /package/build/assets/{files-tab-store-QlUCuW8b.js → files-tab-store-CZAEwv3x.js} +0 -0
- /package/build/assets/{folder-UGYUKpqb.js → folder-wShAU0y7.js} +0 -0
- /package/build/assets/{git-status-mapper-N4-7eaeC.js → git-status-mapper-C3rfzUex.js} +0 -0
- /package/build/assets/{globe-2otpEmVh.js → globe-CQ_5xwpo.js} +0 -0
- /package/build/assets/{handle-capture-consent-OitMkDHc.js → handle-capture-consent-DDnXMC9Y.js} +0 -0
- /package/build/assets/{iconBase-B_5IRYeZ.js → iconBase-1A_WRQ4E.js} +0 -0
- /package/build/assets/{lesson-plan-5O2tVbD1.js → lesson-plan-B607buca.js} +0 -0
- /package/build/assets/{link-external-kU6aFXU6.js → link-external-DCsHkAj4.js} +0 -0
- /package/build/assets/{map-provider-BHow6ugd.js → map-provider-XSFHmdDs.js} +0 -0
- /package/build/assets/{modal-close-button-BCvw9IUN.js → modal-close-button-DY-rVmld.js} +0 -0
- /package/build/assets/{plan-components-BRiIX8Wn.js → plan-components-DsiDjcDi.js} +0 -0
- /package/build/assets/{sdk-settings-field-metadata-CtO73dY6.js → sdk-settings-field-metadata-CETNItbo.js} +0 -0
- /package/build/assets/{search-Cbh-hHBu.js → search-BqXT2Ied.js} +0 -0
- /package/build/assets/{secrets-service-DEIB-Cfk.js → secrets-service-rJqZtDmI.js} +0 -0
- /package/build/assets/{settings-KgLvVrSm.js → settings-BgL2HUsU.js} +0 -0
- /package/build/assets/{settings-gear-xGs_SPgZ.js → settings-gear-DFmoMp-6.js} +0 -0
- /package/build/assets/{settings-input-CPr7vX81.js → settings-input-DsoZj8Dm.js} +0 -0
- /package/build/assets/{settings-like-page-layout-classes-GknosJgv.js → settings-like-page-layout-classes-DENKlZpD.js} +0 -0
- /package/build/assets/{settings-list-classes-CYDn4jUg.js → settings-list-classes-D8VAVZmi.js} +0 -0
- /package/build/assets/{settings-section-header-context-aD2iq1gD.js → settings-section-header-context-D61smD-W.js} +0 -0
- /package/build/assets/{skills-CCaEu1KQ.js → skills-BDOWVle-.js} +0 -0
- /package/build/assets/{switch-skeleton-C87Tx3Tc.js → switch-skeleton-QpdcdxRP.js} +0 -0
- /package/build/assets/{terminal-DQJ6IJUd.js → terminal-BUww3Ssl.js} +0 -0
- /package/build/assets/{toggle-switch-C-7juZ1u.js → toggle-switch-DDr-DnEc.js} +0 -0
- /package/build/assets/{typography-U1gkzkXo.js → typography-Cx7uw7z3.js} +0 -0
- /package/build/assets/{u-check-circle-lbkXL2z4.js → u-check-circle-CftRwky1.js} +0 -0
- /package/build/assets/{u-check-circle-half-CirnoxXG.js → u-check-circle-half-sGVk0BtL.js} +0 -0
- /package/build/assets/{u-circuit-Danff2ks.js → u-circuit-Cg9tH5qb.js} +0 -0
- /package/build/assets/{u-edit-CH5nNya1.js → u-edit-foF02hwH.js} +0 -0
- /package/build/assets/{use-breakpoint-2sN462wJ.js → use-breakpoint-Dt2knceC.js} +0 -0
- /package/build/assets/{use-click-outside-element-_vianyPb.js → use-click-outside-element-Bu2A3s59.js} +0 -0
- /package/build/assets/{use-is-authed-DrocXcet.js → use-is-authed-fNsj-Adj.js} +0 -0
- /package/build/assets/{use-llm-profiles-BhZRf-NX.js → use-llm-profiles-CyNVoVqm.js} +0 -0
- /package/build/assets/{use-skills-v8pQ02ze.js → use-skills-BWHATXK-.js} +0 -0
- /package/build/assets/{v4-BMWDcIWQ.js → v4-BygpdDmc.js} +0 -0
- /package/build/assets/{vendor~browser-C3GKF4mj.js → vendor~browser-BYEwwJqV.js} +0 -0
- /package/build/assets/{vendor~conversation-panel~conversation~alert-banner-DMcFTqbt.js → vendor~conversation-panel~conversation~alert-banner-BnHToS5O.js} +0 -0
- /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-Cntsv2EN.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-B4oeCCli.js} +0 -0
- /package/build/assets/{vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-DTosGXG7.js → vendor~home~conversation-panel~conversation~shared-conversation~planner-tab~files-tab-D8cqyq4k.js} +0 -0
- /package/build/assets/{vendor~terminal-CnKZILnC.js → vendor~terminal-aeP3PnKf.js} +0 -0
- /package/build/assets/{vscode-url-helper-CwQPl6QN.js → vscode-url-helper-DOCkV_-G.js} +0 -0
- /package/build/assets/{waiting-for-runtime-message-B1Dzhtj5.js → waiting-for-runtime-message-Bg27u9JB.js} +0 -0
- /package/build/assets/{x-mark-CmcVOTk2.js → x-mark-Cn-YJVbN.js} +0 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-server-adapter.js","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of `VITE_RUNTIME_SERVICES_INFO` (set by the dev launchers in\n * scripts/dev-*.mjs). All URLs are written from the agent's point of view,\n * not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured dev env var.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n // X-Session-API-Key is the local convention shared by the agent-server\n // and automation backend (see openhands-automation auth.py).\n lines.push(\n ` Auth: header 'X-Session-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n active_profile: metadata?.active_profile ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nconst ACP_SETTINGS_KEYS = [\n \"acp_command\",\n \"acp_args\",\n \"acp_model\",\n \"acp_session_mode\",\n \"acp_prompt_timeout\",\n] as const;\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // ``mcp_config`` is a *shared* field (not in ACP_SETTINGS_KEYS): forward it\n // so the ACP subprocess connects to the configured MCP servers at session\n // creation. Only include it when it actually carries servers — an empty or\n // malformed value is dropped rather than sending ``mcp_config: {}``.\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length > 0 && \"mcpServers\" in mcpConfig) {\n payload.mcp_config = mcpConfig;\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n // ``acp_env`` is no longer a forwarded ACP setting (provider creds ride the\n // Secrets panel), but a legacy value may linger on persisted settings —\n // scrub it so it never leaks onto the OpenHands payload.\n delete agentSettings.acp_env;\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = backend ? buildAuthHeaders(backend) : {};\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"agent-server-adapter.js","names":[],"sources":["../../src/api/agent-server-adapter.ts"],"sourcesContent":["import { ACP_SETTINGS_KEYS } from \"@openhands/typescript-client\";\nimport { DEFAULT_SETTINGS } from \"#/services/settings\";\nimport { ExecutionStatus } from \"#/types/agent-server/core\";\nimport { Settings, SettingsValue } from \"#/types/settings\";\nimport {\n getAcpProvider,\n resolveEffectiveAcpModel,\n} from \"#/constants/acp-providers\";\nimport { getAgentServerClientOptions } from \"./agent-server-client-options\";\nimport { isAgentServerToolAvailable } from \"./agent-server-compatibility\";\nimport {\n getAgentServerWorkingDir,\n shouldLoadPublicSkills,\n} from \"./agent-server-config\";\nimport { getEffectiveLocalBackend } from \"./backend-registry/active-store\";\nimport { buildAuthHeaders } from \"./backend-registry/auth\";\nimport {\n GetHooksResponse,\n PluginSpec,\n AppConversation,\n AppConversationPage,\n SandboxStatus,\n} from \"./conversation-service/agent-server-conversation-service.types\";\nimport SettingsService from \"./settings-service/settings-service.api\";\nimport { getStoredConversationMetadata } from \"./conversation-metadata-store\";\n\nexport interface DirectConversationInfo {\n id: string;\n title?: string | null;\n created_at: string;\n updated_at: string;\n execution_status?: string | null;\n /** Cloud-only sandbox lifecycle state. Omitted / null for local agent-server conversations. */\n sandbox_status?: string | null;\n metrics?: {\n accumulated_cost?: number | null;\n max_budget_per_task?: number | null;\n accumulated_token_usage?: {\n prompt_tokens?: number;\n completion_tokens?: number;\n cache_read_tokens?: number;\n cache_write_tokens?: number;\n context_window?: number;\n per_turn_token?: number;\n } | null;\n } | null;\n agent?: {\n /**\n * Pydantic discriminator from the SDK union: ``\"ACPAgent\"`` for ACP CLI\n * subprocesses (model lives on the subprocess via ``acp_model``),\n * ``\"Agent\"`` for direct litellm. Read by {@link toAppConversation}.\n */\n kind?: string | null;\n acp_model?: string | null;\n llm?: {\n model?: string | null;\n } | null;\n } | null;\n current_model_id?: string | null;\n current_model_name?: string | null;\n workspace?: {\n working_dir?: string | null;\n } | null;\n /**\n * Arbitrary string-keyed conversation tags surfaced by the agent-server\n * (see ``ConversationInfo.tags``). Canvas only consumes one key today —\n * ``ACP_SERVER_TAG_KEY`` (\"acpserver\") — but the field is typed as a\n * generic record so future readers don't need another wire-shape change.\n * Keys are constrained to ``^[a-z0-9]+$`` by the agent-server validator;\n * values are opaque strings.\n */\n tags?: Record<string, string> | null;\n}\n\n// Module qualname for the Canvas-UI tool. The agent-server imports this via\n// tool_module_qualnames; the host directory is exposed via OH_EXTRA_PYTHON_PATH\n// (see scripts/dev-safe.mjs).\nconst CANVAS_UI_TOOL_NAME = \"canvas_ui\";\nconst CANVAS_UI_TOOL_MODULE = \"canvas_ui_tool\";\n\nconst DEFAULT_TOOL_NAMES = [\n \"terminal\",\n \"file_editor\",\n \"task_tracker\",\n CANVAS_UI_TOOL_NAME,\n];\nconst BROWSER_TOOL_SET_NAME = \"browser_tool_set\";\nconst TASK_TOOL_SET_NAME = \"task_tool_set\";\n\nfunction browserToolsEnabled() {\n return import.meta.env.VITE_ENABLE_BROWSER_TOOLS !== \"false\";\n}\n\n/**\n * Shape of the runtime services info (set by the dev launchers in\n * scripts/dev-*.mjs as `VITE_RUNTIME_SERVICES_INFO`, or injected at serve time\n * by `scripts/static-server.mjs` for static builds — see\n * `getRawRuntimeServicesInfo`). All URLs are written from the agent's point of\n * view, not the browser's. The block is rendered into the agent's system prompt\n * via `AgentContext.system_message_suffix` so the agent knows what's\n * reachable from inside its sandbox without having to probe.\n */\ninterface RuntimeServicesInfo {\n mode?: string;\n agent_host_alias?: string;\n services?: {\n agent_server?: { description?: string; url_from_agent?: string };\n ingress?: { description?: string; url_from_agent?: string };\n frontend?: {\n kind?: \"vite\" | \"static\";\n description?: string;\n url_from_agent?: string;\n };\n // `vite` is the legacy key name for the frontend entry, accepted for\n // one release while older dev-stack launchers may still emit it.\n vite?: { description?: string; url_from_agent?: string };\n automation?: {\n description?: string;\n url_from_agent?: string;\n api_prefix?: string;\n docs_url?: string;\n openapi_url?: string;\n auth_env_var?: string;\n };\n };\n}\n\n/**\n * Return the raw runtime-services JSON string, consulting two sources in order\n * (mirrors `getBakedSessionApiKey` in agent-server-config.ts):\n * 1. `VITE_RUNTIME_SERVICES_INFO` — baked into the bundle at build time by\n * the dev launchers (`npm run dev`, dev:static).\n * 2. `window.__AGENT_CANVAS_RUNTIME_SERVICES_INFO__` — injected into\n * index.html at serve time by `scripts/static-server.mjs\n * --runtime-services-info <json>`. This is the path used by static builds\n * (the Docker image and the published binary), where the env var is empty\n * in the prebuilt bundle. Without it the `<RUNTIME_SERVICES>` block is\n * missing and the agent cannot reach the local automation backend.\n */\nfunction getRawRuntimeServicesInfo(): string | null {\n const envRaw = import.meta.env.VITE_RUNTIME_SERVICES_INFO?.trim();\n if (envRaw) return envRaw;\n\n if (typeof window !== \"undefined\") {\n const injected = (window as unknown as Record<string, unknown>)\n .__AGENT_CANVAS_RUNTIME_SERVICES_INFO__;\n if (typeof injected === \"string\") {\n return injected.trim() || null;\n }\n }\n\n return null;\n}\n\nfunction parseRuntimeServicesInfo(): RuntimeServicesInfo | null {\n const raw = getRawRuntimeServicesInfo();\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RuntimeServicesInfo;\n if (!parsed || typeof parsed !== \"object\") return null;\n return parsed;\n } catch {\n // Malformed JSON: ignore and fall back to no runtime info, rather than\n // tearing down conversation creation over a misconfigured env var or\n // injected value.\n return null;\n }\n}\n\n/**\n * Render the runtime services info into a markdown block suitable for\n * appending to the system prompt via `AgentContext.system_message_suffix`.\n *\n * Returns `undefined` when no runtime info is configured, so callers can\n * safely omit the field on production builds (where the launcher doesn't\n * set `VITE_RUNTIME_SERVICES_INFO`).\n */\nexport function buildRuntimeServicesSystemSuffix(): string | undefined {\n const info = parseRuntimeServicesInfo();\n if (!info?.services) return undefined;\n\n const lines: string[] = [];\n lines.push(\"<RUNTIME_SERVICES>\");\n if (info.mode) {\n lines.push(\n `You are running inside an agent-canvas dev stack started in '${info.mode}' mode.`,\n );\n } else {\n lines.push(\"You are running inside an agent-canvas dev stack.\");\n }\n lines.push(\n \"The following services are reachable from your sandbox. URLs are written\",\n \"from your point of view (i.e., as you should curl/fetch them).\",\n \"\",\n );\n\n const { agent_server, ingress, automation } = info.services;\n // Accept `frontend` (current key) or `vite` (legacy key) for the\n // frontend service entry. The legacy fallback can be removed once all\n // launchers in this repo emit `frontend`.\n const frontend = info.services.frontend ?? info.services.vite;\n\n if (agent_server?.url_from_agent) {\n lines.push(\n `* Agent Server (you): ${agent_server.url_from_agent}`,\n ` ${agent_server.description ?? \"The agent-server hosting your tool calls.\"}`,\n );\n }\n if (ingress?.url_from_agent) {\n lines.push(\n `* Ingress: ${ingress.url_from_agent}`,\n ` ${ingress.description ?? \"Unified entry point for browser-facing traffic.\"}`,\n );\n }\n if (frontend?.url_from_agent) {\n lines.push(\n `* Frontend: ${frontend.url_from_agent}`,\n ` ${frontend.description ?? \"Frontend dev server.\"}`,\n );\n }\n if (automation?.url_from_agent) {\n lines.push(\n `* Automation backend: ${automation.url_from_agent}`,\n ` ${automation.description ?? \"OpenHands Automations service.\"}`,\n );\n if (automation.docs_url) {\n lines.push(` Docs: ${automation.docs_url}`);\n }\n if (automation.openapi_url) {\n lines.push(` OpenAPI: ${automation.openapi_url}`);\n }\n if (automation.auth_env_var) {\n // X-Session-API-Key is the local convention shared by the agent-server\n // and automation backend (see openhands-automation auth.py).\n lines.push(\n ` Auth: header 'X-Session-API-Key: $${automation.auth_env_var}'`,\n );\n }\n } else {\n lines.push(\n \"* Automation backend: not running in this dev mode (skip /api/automation calls).\",\n );\n }\n\n // Anchor the \"don't guess\" warning to the actual agent-server URL for\n // this stack instead of a hardcoded port. The agent-server listens on\n // different ports across dev modes, and baking the wrong port into the\n // system prompt is exactly the kind of confusion this block is meant to\n // prevent.\n const agentServerUrl = agent_server?.url_from_agent;\n lines.push(\n \"\",\n \"Trust this block over guessing: do not assume any other URLs are running.\",\n );\n if (agentServerUrl) {\n lines.push(\n `In particular, ${agentServerUrl} inside your sandbox is the Agent Server`,\n \"you are running inside of — NOT the automation backend.\",\n );\n }\n lines.push(\"</RUNTIME_SERVICES>\");\n\n return lines.join(\"\\n\");\n}\n\nexport function toConversationUrl(conversationId: string): string {\n // Local-format conversation URL — points at whichever local agent-server\n // is actually serving the conversation (the bundled one when the active\n // selection is cloud).\n const { host } = getAgentServerClientOptions();\n return `${host}/api/conversations/${conversationId}`;\n}\n\n// TODO(i18n): extract \"Conversation\" once we add CONVERSATION$DEFAULT_TITLE\n// with `{{shortId}}` interpolation. Kept as a literal for now to keep the\n// fallback inside this pure adapter rather than fanning out to display sites.\nexport function getDefaultConversationTitle(conversationId: string): string {\n return `Conversation ${conversationId.slice(0, 5)}`;\n}\n\nexport function toAppConversation(\n info: DirectConversationInfo,\n): AppConversation {\n const metadata = getStoredConversationMetadata(info.id);\n // ACPAgent conversations carry a sentinel ``llm`` on older SDKs. Prefer the\n // runtime model fields when available, then the configured ``acp_model`` that\n // Canvas saves for built-in providers. ``agent_kind`` still gates model\n // switching, so surfacing this string is display-only.\n const isAcp = info.agent?.kind === \"ACPAgent\";\n // Only surface ``acp_server`` for ACP conversations even if the wire\n // payload accidentally carries an ``acpserver`` tag on an OpenHands\n // conversation — the chip is identity info for the ACP CLI subprocess,\n // and showing it on a non-ACP conversation would be a lie.\n const acpServer = isAcp ? (info.tags?.[ACP_SERVER_TAG_KEY] ?? null) : null;\n return {\n id: info.id,\n created_by_user_id: null,\n selected_repository: metadata?.selected_repository ?? null,\n selected_branch: metadata?.selected_branch ?? null,\n git_provider: metadata?.git_provider ?? null,\n selected_workspace: metadata?.selected_workspace ?? null,\n active_profile: metadata?.active_profile ?? null,\n title: info.title?.trim()\n ? info.title\n : getDefaultConversationTitle(info.id),\n trigger: null,\n pr_number: [],\n agent_kind: isAcp ? \"acp\" : \"openhands\",\n acp_server: acpServer,\n // Chip path: no ``providerDefault`` — the chip must distinguish\n // \"no concrete model\" (fall back to the provider display name in\n // ConversationCardFooter) from \"default\" (would lie about what's\n // running on the subprocess).\n llm_model: isAcp\n ? resolveEffectiveAcpModel({\n runtimeName: info.current_model_name,\n runtimeId: info.current_model_id,\n configured: info.agent?.acp_model,\n sdkLlm: info.agent?.llm?.model,\n })\n : (info.agent?.llm?.model ?? DEFAULT_SETTINGS.llm_model),\n metrics: info.metrics\n ? {\n accumulated_cost: info.metrics.accumulated_cost ?? null,\n max_budget_per_task: info.metrics.max_budget_per_task ?? null,\n accumulated_token_usage: info.metrics.accumulated_token_usage\n ? {\n prompt_tokens:\n info.metrics.accumulated_token_usage.prompt_tokens ?? 0,\n completion_tokens:\n info.metrics.accumulated_token_usage.completion_tokens ?? 0,\n cache_read_tokens:\n info.metrics.accumulated_token_usage.cache_read_tokens ?? 0,\n cache_write_tokens:\n info.metrics.accumulated_token_usage.cache_write_tokens ?? 0,\n context_window:\n info.metrics.accumulated_token_usage.context_window ?? 0,\n per_turn_token:\n info.metrics.accumulated_token_usage.per_turn_token ?? 0,\n }\n : null,\n }\n : null,\n created_at: info.created_at,\n updated_at: info.updated_at,\n execution_status:\n (info.execution_status as AppConversation[\"execution_status\"]) ??\n ExecutionStatus.IDLE,\n sandbox_status: (info.sandbox_status as SandboxStatus | null) ?? null,\n conversation_url: toConversationUrl(info.id),\n session_api_key: getAgentServerClientOptions().apiKey ?? null,\n sandbox_id: null,\n workspace: {\n working_dir: info.workspace?.working_dir ?? getAgentServerWorkingDir(),\n },\n public: false,\n sub_conversation_ids: [],\n };\n}\n\nexport function toConversationPage(data: {\n items: DirectConversationInfo[];\n next_page_id?: string | null;\n}): AppConversationPage {\n return {\n items: data.items.map(toAppConversation),\n next_page_id: data.next_page_id ?? null,\n };\n}\n\ntype SettingsRecord = Record<string, unknown>;\n\ninterface AgentToolSpec {\n name: string;\n params: SettingsRecord;\n}\n\ntype AgentSettingsPayload = SettingsRecord & {\n llm?: SettingsRecord;\n agent_context: SettingsRecord;\n tools?: AgentToolSpec[];\n};\n\ninterface LocalWorkspacePayload {\n kind: \"LocalWorkspace\";\n working_dir: string;\n}\n\ninterface InitialMessagePayload {\n role: \"user\";\n content: Array<{ type: \"text\"; text: string }>;\n run: true;\n}\n\ntype ConversationSettingsPayload = SettingsRecord & {\n workspace: LocalWorkspacePayload;\n initial_message?: InitialMessagePayload;\n};\n\nexport const ACP_SERVER_TAG_KEY = \"acpserver\";\n\nconst CONVERSATION_SETTINGS_METADATA_KEYS = new Set([\n \"schema_version\",\n \"agent_settings\",\n \"workspace\",\n \"conversation_id\",\n \"initial_message\",\n \"plugins\",\n]);\n\nfunction toRecord(value: unknown): SettingsRecord {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) {\n return {};\n }\n\n return structuredClone(value as SettingsRecord);\n}\n\nfunction normalizeSecretString(value: unknown): string | undefined {\n if (typeof value !== \"string\") {\n return undefined;\n }\n\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction getConversationConfirmationPolicy(\n conversationSettings: SettingsRecord,\n) {\n if (conversationSettings.confirmation_mode !== true) {\n return { kind: \"NeverConfirm\" };\n }\n\n if (conversationSettings.security_analyzer === \"llm\") {\n return { kind: \"ConfirmRisky\", threshold: \"HIGH\", confirm_unknown: true };\n }\n\n return { kind: \"AlwaysConfirm\" };\n}\n\nfunction getConversationSecurityAnalyzer(conversationSettings: SettingsRecord) {\n switch (conversationSettings.security_analyzer) {\n case \"llm\":\n return { kind: \"LLMSecurityAnalyzer\" };\n case \"pattern\":\n return { kind: \"PatternSecurityAnalyzer\" };\n case \"policy_rail\":\n return { kind: \"PolicyRailSecurityAnalyzer\" };\n default:\n return undefined;\n }\n}\n\nfunction isToolRecord(\n value: unknown,\n): value is { name: string; params?: unknown } {\n return (\n !!value &&\n typeof value === \"object\" &&\n !Array.isArray(value) &&\n typeof (value as { name?: unknown }).name === \"string\"\n );\n}\n\nfunction shouldIncludeTool(name: string, agentSettings: SettingsRecord) {\n if (name === BROWSER_TOOL_SET_NAME) {\n return browserToolsEnabled() && isAgentServerToolAvailable(name);\n }\n\n if (name === TASK_TOOL_SET_NAME) {\n return (\n agentSettings.enable_sub_agents === true &&\n isAgentServerToolAvailable(name)\n );\n }\n\n return true;\n}\n\nfunction getAgentTools(agentSettings: SettingsRecord): AgentToolSpec[] {\n const tools = new Map<string, AgentToolSpec>();\n\n for (const name of DEFAULT_TOOL_NAMES) {\n tools.set(name, { name, params: {} });\n }\n\n for (const name of [BROWSER_TOOL_SET_NAME, TASK_TOOL_SET_NAME]) {\n if (shouldIncludeTool(name, agentSettings)) {\n tools.set(name, { name, params: {} });\n }\n }\n\n const configuredTools = agentSettings.tools;\n if (\n Array.isArray(configuredTools) &&\n configuredTools.every((tool) => isToolRecord(tool))\n ) {\n for (const tool of configuredTools) {\n if (shouldIncludeTool(tool.name, agentSettings)) {\n tools.set(tool.name, {\n name: tool.name,\n params: toRecord(tool.params),\n });\n }\n }\n }\n\n return Array.from(tools.values());\n}\n\nfunction buildInitialMessage(\n query?: string,\n conversationInstructions?: string,\n): InitialMessagePayload | null {\n const parts = [query?.trim(), conversationInstructions?.trim()].filter(\n Boolean,\n );\n if (parts.length === 0) {\n return null;\n }\n\n return {\n role: \"user\",\n content: [{ type: \"text\", text: parts.join(\"\\n\\n\") }],\n run: true,\n };\n}\n\nfunction buildAgentContext(agentSettings: SettingsRecord): SettingsRecord {\n const runtimeServicesSuffix = buildRuntimeServicesSystemSuffix();\n return {\n ...toRecord(agentSettings.agent_context),\n load_public_skills: shouldLoadPublicSkills(),\n load_user_skills: true,\n load_project_skills: true,\n ...(runtimeServicesSuffix\n ? { system_message_suffix: runtimeServicesSuffix }\n : {}),\n };\n}\n\nfunction isAcpAgent(settings: Settings): boolean {\n const agentSettings = toRecord(settings.agent_settings);\n return agentSettings.agent_kind === \"acp\";\n}\n\nfunction getAcpServerTag(settings: Settings): string | undefined {\n const agentSettings = toRecord(settings.agent_settings);\n const value = agentSettings.acp_server;\n return typeof value === \"string\" && value.length > 0 ? value : undefined;\n}\n\nfunction resolveAcpCommand(agentSettings: SettingsRecord): unknown {\n const cmd = agentSettings.acp_command;\n const isEmpty = Array.isArray(cmd) && cmd.length === 0;\n const noCommand = cmd === undefined;\n if (!isEmpty && !noCommand) {\n return cmd;\n }\n\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n return provider ? [...provider.default_command] : cmd;\n}\n\nfunction buildConfiguredAcpAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const payload: AgentSettingsPayload = {\n agent_kind: \"acp\",\n agent_context: buildAgentContext(agentSettings),\n };\n\n for (const key of ACP_SETTINGS_KEYS) {\n // ``acp_model`` is resolved separately below so a saved ``null`` still\n // falls back to the provider's default rather than being dropped.\n if (key === \"acp_model\") continue;\n // ``acp_env`` is deprecated — provider creds now route via agent_context.secrets.\n if (key === \"acp_env\") continue;\n const value =\n key === \"acp_command\"\n ? resolveAcpCommand(agentSettings)\n : agentSettings[key];\n if (value !== undefined && value !== null) {\n payload[key] = value;\n }\n }\n\n // ``mcp_config`` is a *shared* field (not in ACP_SETTINGS_KEYS): forward it\n // so the ACP subprocess connects to the configured MCP servers at session\n // creation. Only include it when it actually carries servers — an empty or\n // malformed value is dropped rather than sending ``mcp_config: {}``.\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length > 0 && \"mcpServers\" in mcpConfig) {\n payload.mcp_config = mcpConfig;\n }\n\n // Saved settings may carry ``acp_model: null`` (existing users predating\n // the default-model registry, or saved fields the agent-server stripped).\n // Fall back to the provider's ``default_model`` so the conversation starts\n // with whatever the Settings → Agent UI shows — without that, the form's\n // displayed default would silently not take effect at runtime until the\n // user re-saved the page.\n const serverKey =\n typeof agentSettings.acp_server === \"string\"\n ? agentSettings.acp_server\n : undefined;\n const provider = getAcpProvider(serverKey);\n const effectiveModel = resolveEffectiveAcpModel({\n configured: agentSettings.acp_model as string | null | undefined,\n providerDefault: provider?.default_model,\n });\n if (effectiveModel) {\n payload.acp_model = effectiveModel;\n }\n\n return payload;\n}\n\nfunction buildConfiguredOpenHandsAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n const agentSettings = toRecord(settings.agent_settings);\n const llm = toRecord(agentSettings.llm);\n\n llm.model =\n typeof llm.model === \"string\" && llm.model.trim().length > 0\n ? llm.model\n : DEFAULT_SETTINGS.llm_model;\n\n const apiKey = normalizeSecretString(llm.api_key);\n if (apiKey) {\n llm.api_key = apiKey;\n } else {\n delete llm.api_key;\n }\n\n const baseUrl = normalizeSecretString(llm.base_url);\n if (baseUrl) {\n llm.base_url = baseUrl;\n } else {\n delete llm.base_url;\n }\n\n const mcpConfig = toRecord(agentSettings.mcp_config);\n if (Object.keys(mcpConfig).length === 0 || !(\"mcpServers\" in mcpConfig)) {\n delete agentSettings.mcp_config;\n }\n\n delete agentSettings.acp_server;\n for (const key of ACP_SETTINGS_KEYS) {\n delete agentSettings[key];\n }\n // ``acp_env`` is no longer a forwarded ACP setting (provider creds ride the\n // Secrets panel), but a legacy value may linger on persisted settings —\n // scrub it so it never leaks onto the OpenHands payload.\n delete agentSettings.acp_env;\n\n return {\n ...agentSettings,\n llm,\n agent_context: buildAgentContext(agentSettings),\n tools: getAgentTools(agentSettings),\n };\n}\n\nfunction buildConfiguredAgentSettings(\n settings: Settings,\n): AgentSettingsPayload {\n return isAcpAgent(settings)\n ? buildConfiguredAcpAgentSettings(settings)\n : buildConfiguredOpenHandsAgentSettings(settings);\n}\n\nfunction buildConfiguredConversationSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n workingDir?: string;\n}): ConversationSettingsPayload {\n const { settings, query, conversationInstructions, plugins, workingDir } =\n options;\n const conversationSettings = toRecord(settings.conversation_settings);\n const initialMessage = buildInitialMessage(query, conversationInstructions);\n\n CONVERSATION_SETTINGS_METADATA_KEYS.forEach(\n (key) => delete conversationSettings[key],\n );\n\n const payload: ConversationSettingsPayload = {\n ...conversationSettings,\n workspace: {\n kind: \"LocalWorkspace\",\n working_dir: workingDir ?? getAgentServerWorkingDir(),\n },\n ...(initialMessage ? { initial_message: initialMessage } : {}),\n ...(plugins?.length\n ? {\n plugins: plugins.map((plugin) => ({\n source: plugin.source,\n ...(plugin.ref ? { ref: plugin.ref } : {}),\n ...(plugin.repo_path ? { repo_path: plugin.repo_path } : {}),\n })),\n }\n : {}),\n };\n\n return payload;\n}\n\ninterface LookupSecret {\n kind: \"LookupSecret\";\n url: string;\n headers?: Record<string, string>;\n description?: string;\n}\n\ntype StartConversationPayload = Record<string, unknown> & {\n agent_settings: AgentSettingsPayload;\n workspace: LocalWorkspacePayload;\n confirmation_policy: SettingsRecord;\n security_analyzer?: SettingsRecord;\n initial_message?: InitialMessagePayload;\n max_iterations: number;\n stuck_detection: true;\n autotitle: true;\n worktree: true;\n secrets_encrypted?: true;\n conversation_id?: string;\n secrets?: Record<string, LookupSecret>;\n tags?: Record<string, string>;\n tool_module_qualnames?: Record<string, string>;\n};\n\nexport interface StartConversationOptions {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n encryptedAgentSettings?: Record<string, SettingsValue>;\n encryptedConversationSettings?: Record<string, SettingsValue>;\n secretsEncrypted?: boolean;\n customSecrets?: Array<{ name: string; description?: string }>;\n}\n\nexport function buildStartConversationRequest(\n options: StartConversationOptions,\n): StartConversationPayload {\n const sourceAgentSettings = options.encryptedAgentSettings\n ? { ...options.settings, agent_settings: options.encryptedAgentSettings }\n : options.settings;\n\n const acpMode = isAcpAgent(sourceAgentSettings);\n const agentSettings = buildConfiguredAgentSettings(sourceAgentSettings);\n const acpServerTag = acpMode\n ? getAcpServerTag(sourceAgentSettings)\n : undefined;\n\n const sourceConversationOptions = options.encryptedConversationSettings\n ? {\n ...options,\n settings: {\n ...options.settings,\n conversation_settings: options.encryptedConversationSettings,\n },\n }\n : options;\n\n const conversationSettings = buildConfiguredConversationSettings(\n sourceConversationOptions,\n );\n\n const payload: StartConversationPayload = {\n agent_settings: agentSettings,\n workspace: conversationSettings.workspace,\n confirmation_policy:\n getConversationConfirmationPolicy(conversationSettings),\n max_iterations:\n typeof conversationSettings.max_iterations === \"number\"\n ? conversationSettings.max_iterations\n : 500,\n stuck_detection: true,\n autotitle: true,\n worktree: true,\n };\n\n if (acpServerTag) {\n payload.tags = { [ACP_SERVER_TAG_KEY]: acpServerTag };\n }\n\n if (options.secretsEncrypted) {\n payload.secrets_encrypted = true;\n }\n\n if (options.conversationId) {\n payload.conversation_id = options.conversationId;\n }\n\n const securityAnalyzer =\n getConversationSecurityAnalyzer(conversationSettings);\n if (securityAnalyzer) {\n payload.security_analyzer = securityAnalyzer;\n }\n\n if (conversationSettings.initial_message) {\n payload.initial_message = conversationSettings.initial_message;\n }\n\n if (conversationSettings.plugins) {\n payload.plugins = conversationSettings.plugins;\n }\n\n if (conversationSettings.hook_config) {\n payload.hook_config = conversationSettings.hook_config;\n }\n\n payload.tool_module_qualnames = {\n [CANVAS_UI_TOOL_NAME]: CANVAS_UI_TOOL_MODULE,\n ...((conversationSettings.tool_module_qualnames as\n | Record<string, string>\n | undefined) ?? {}),\n };\n\n if (conversationSettings.agent_definitions) {\n payload.agent_definitions = conversationSettings.agent_definitions;\n }\n\n if (options.customSecrets && options.customSecrets.length > 0) {\n const backend = getEffectiveLocalBackend();\n const headers = backend ? buildAuthHeaders(backend) : {};\n\n const secrets: Record<string, LookupSecret> = {};\n for (const secret of options.customSecrets) {\n const lookupSecret: LookupSecret = {\n kind: \"LookupSecret\",\n url: `/api/settings/secrets/${encodeURIComponent(secret.name)}`,\n description: secret.description,\n };\n\n if (Object.keys(headers).length > 0) {\n lookupSecret.headers = headers;\n }\n\n secrets[secret.name] = lookupSecret;\n }\n\n payload.secrets = secrets;\n\n if (acpMode) {\n payload.agent_settings.agent_context = {\n ...payload.agent_settings.agent_context,\n secrets,\n };\n }\n }\n\n return payload;\n}\n\nexport async function buildStartConversationRequestWithEncryptedSettings(options: {\n settings: Settings;\n query?: string;\n conversationInstructions?: string;\n plugins?: PluginSpec[];\n conversationId?: string;\n workingDir?: string;\n}): Promise<Record<string, unknown>> {\n const { SecretsService } = await import(\"./secrets-service\");\n\n const [settingsResult, customSecrets] = await Promise.all([\n SettingsService.getSettingsForConversation(),\n SecretsService.getSecrets(),\n ]);\n\n const { agentSettings, conversationSettings, secretsEncrypted } =\n settingsResult;\n\n return buildStartConversationRequest({\n ...options,\n encryptedAgentSettings: agentSettings,\n encryptedConversationSettings: conversationSettings,\n secretsEncrypted,\n customSecrets,\n });\n}\n\nexport function emptyHooksResponse(): GetHooksResponse {\n return { hooks: [] };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-preferences-store.cjs","names":[],"sources":["../../src/api/app-preferences-store.ts"],"sourcesContent":["import { Settings } from \"#/types/settings\";\n\n/**\n * The local agent-server's PATCH /api/settings only persists\n * `agent_settings_diff` and `conversation_settings_diff`. App-level user\n * preferences (language, sound notifications, analytics consent, git\n * identity) have no native home
|
|
1
|
+
{"version":3,"file":"app-preferences-store.cjs","names":[],"sources":["../../src/api/app-preferences-store.ts"],"sourcesContent":["import { Settings } from \"#/types/settings\";\n\n/**\n * The local agent-server's PATCH /api/settings only persists\n * `agent_settings_diff` and `conversation_settings_diff`. App-level user\n * preferences (language, sound notifications, analytics consent, git\n * identity) have no native home in that schema, so we persist them in\n * localStorage as a workaround. This is the same fallback pattern used\n * by `DISABLED_SKILLS_STORAGE_KEY` in `settings-service.api.ts`.\n *\n * In cloud mode the cloud backend accepts these fields as flat top-level\n * keys at POST /api/v1/settings, and the cloud fetch returns them. We\n * still write through to localStorage so the values survive momentary\n * fetch failures and so the merge logic in `syncDerivedSettings` has a\n * single source for both modes.\n *\n * Long-term goal: extend the local agent-server settings schema to cover\n * these fields and delete this module entirely.\n */\nexport const APP_PREFERENCES_STORAGE_KEY =\n \"openhands-agent-server-app-preferences\";\n\nexport const APP_PREFERENCE_FIELDS = [\n \"language\",\n \"user_consents_to_analytics\",\n \"enable_sound_notifications\",\n \"git_user_name\",\n \"git_user_email\",\n] as const;\n\nexport type AppPreferenceField = (typeof APP_PREFERENCE_FIELDS)[number];\n\nexport type StoredAppPreferences = Partial<Pick<Settings, AppPreferenceField>>;\n\nconst APP_PREFERENCE_FIELD_SET: ReadonlySet<string> = new Set(\n APP_PREFERENCE_FIELDS,\n);\n\nconst isAppPreferenceField = (key: string): key is AppPreferenceField =>\n APP_PREFERENCE_FIELD_SET.has(key);\n\nconst coerceValue = (\n key: AppPreferenceField,\n value: unknown,\n): StoredAppPreferences[AppPreferenceField] | undefined => {\n switch (key) {\n case \"language\":\n case \"git_user_name\":\n case \"git_user_email\":\n return typeof value === \"string\" ? value : undefined;\n case \"enable_sound_notifications\":\n return typeof value === \"boolean\" ? value : undefined;\n case \"user_consents_to_analytics\":\n if (value === null) return null;\n return typeof value === \"boolean\" ? value : undefined;\n default:\n return undefined;\n }\n};\n\nconst sanitize = (input: Record<string, unknown>): StoredAppPreferences => {\n const out: StoredAppPreferences = {};\n for (const key of APP_PREFERENCE_FIELDS) {\n if (key in input) {\n const coerced = coerceValue(key, input[key]);\n if (coerced !== undefined) {\n // TS can't narrow per-key writes through a discriminated assignment,\n // so funnel through a single record write.\n (out as Record<string, unknown>)[key] = coerced;\n }\n }\n }\n return out;\n};\n\nexport const readStoredAppPreferences = (): StoredAppPreferences => {\n if (typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(APP_PREFERENCES_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return {};\n }\n return sanitize(parsed as Record<string, unknown>);\n } catch {\n return {};\n }\n};\n\nexport const writeStoredAppPreferences = (\n partial: StoredAppPreferences,\n): void => {\n if (typeof window === \"undefined\") return;\n\n const sanitized = sanitize(partial as Record<string, unknown>);\n const existing = readStoredAppPreferences();\n const merged: StoredAppPreferences = { ...existing, ...sanitized };\n\n if (Object.keys(merged).length === 0) {\n window.localStorage.removeItem(APP_PREFERENCES_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n APP_PREFERENCES_STORAGE_KEY,\n JSON.stringify(merged),\n );\n};\n\nexport const clearStoredAppPreferences = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.removeItem(APP_PREFERENCES_STORAGE_KEY);\n};\n\n/**\n * Split known app-preference keys out of a save payload. Used by\n * `SettingsService.saveSettings` so the local PATCH only ever sees the\n * diff fields the agent-server accepts.\n */\nexport const extractAppPreferences = (\n input: Record<string, unknown>,\n): { extracted: StoredAppPreferences; rest: Record<string, unknown> } => {\n const extracted: StoredAppPreferences = {};\n const rest: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (isAppPreferenceField(key)) {\n const coerced = coerceValue(key, value);\n if (coerced !== undefined) {\n (extracted as Record<string, unknown>)[key] = coerced;\n }\n } else {\n rest[key] = value;\n }\n }\n\n return { extracted, rest };\n};\n"],"mappings":"6CAmBA,IAAa,EACX,yCAEW,EAAwB,CACnC,WACA,6BACA,6BACA,gBACA,iBACD,CAMK,EAAgD,IAAI,IACxD,EACD,CAEK,EAAwB,GAC5B,EAAyB,IAAI,EAAI,CAE7B,GACJ,EACA,IACyD,CACzD,OAAQ,EAAR,CACE,IAAK,WACL,IAAK,gBACL,IAAK,iBACH,OAAO,OAAO,GAAU,SAAW,EAAQ,IAAA,GAC7C,IAAK,6BACH,OAAO,OAAO,GAAU,UAAY,EAAQ,IAAA,GAC9C,IAAK,6BAEH,OADI,IAAU,KAAa,KACpB,OAAO,GAAU,UAAY,EAAQ,IAAA,GAC9C,QACE,SAIA,EAAY,GAAyD,CACzE,IAAM,EAA4B,EAAE,CACpC,IAAK,IAAM,KAAO,EAChB,GAAI,KAAO,EAAO,CAChB,IAAM,EAAU,EAAY,EAAK,EAAM,GAAK,CACxC,IAAY,IAAA,KAGb,EAAgC,GAAO,GAI9C,OAAO,GAGI,MAAuD,CAClE,GAAI,OAAO,OAAW,IACpB,MAAO,EAAE,CAGX,GAAI,CACF,IAAM,EAAM,OAAO,aAAa,QAAQ,EAA4B,CACpE,GAAI,CAAC,EAAK,MAAO,EAAE,CACnB,IAAM,EAAS,KAAK,MAAM,EAAI,CAI9B,MAHI,CAAC,GAAU,OAAO,GAAW,UAAY,MAAM,QAAQ,EAAO,CACzD,EAAE,CAEJ,EAAS,EAAkC,MAC5C,CACN,MAAO,EAAE,GAIA,EACX,GACS,CACT,GAAI,OAAO,OAAW,IAAa,OAEnC,IAAM,EAAY,EAAS,EAAmC,CAExD,EAA+B,CAAE,GADtB,GACyB,CAAU,GAAG,EAAW,CAElE,GAAI,OAAO,KAAK,EAAO,CAAC,SAAW,EAAG,CACpC,OAAO,aAAa,WAAW,EAA4B,CAC3D,OAGF,OAAO,aAAa,QAClB,EACA,KAAK,UAAU,EAAO,CACvB,EAaU,EACX,GACuE,CACvE,IAAM,EAAkC,EAAE,CACpC,EAAgC,EAAE,CAExC,IAAK,GAAM,CAAC,EAAK,KAAU,OAAO,QAAQ,EAAM,CAC9C,GAAI,EAAqB,EAAI,CAAE,CAC7B,IAAM,EAAU,EAAY,EAAK,EAAM,CACnC,IAAY,IAAA,KACb,EAAsC,GAAO,QAGhD,EAAK,GAAO,EAIhB,MAAO,CAAE,YAAW,OAAM"}
|
|
@@ -3,14 +3,18 @@ import { Settings } from "#/types/settings";
|
|
|
3
3
|
* The local agent-server's PATCH /api/settings only persists
|
|
4
4
|
* `agent_settings_diff` and `conversation_settings_diff`. App-level user
|
|
5
5
|
* preferences (language, sound notifications, analytics consent, git
|
|
6
|
-
* identity) have no native home
|
|
7
|
-
*
|
|
6
|
+
* identity) have no native home in that schema, so we persist them in
|
|
7
|
+
* localStorage as a workaround. This is the same fallback pattern used
|
|
8
|
+
* by `DISABLED_SKILLS_STORAGE_KEY` in `settings-service.api.ts`.
|
|
8
9
|
*
|
|
9
10
|
* In cloud mode the cloud backend accepts these fields as flat top-level
|
|
10
11
|
* keys at POST /api/v1/settings, and the cloud fetch returns them. We
|
|
11
12
|
* still write through to localStorage so the values survive momentary
|
|
12
13
|
* fetch failures and so the merge logic in `syncDerivedSettings` has a
|
|
13
14
|
* single source for both modes.
|
|
15
|
+
*
|
|
16
|
+
* Long-term goal: extend the local agent-server settings schema to cover
|
|
17
|
+
* these fields and delete this module entirely.
|
|
14
18
|
*/
|
|
15
19
|
export declare const APP_PREFERENCES_STORAGE_KEY = "openhands-agent-server-app-preferences";
|
|
16
20
|
export declare const APP_PREFERENCE_FIELDS: readonly ["language", "user_consents_to_analytics", "enable_sound_notifications", "git_user_name", "git_user_email"];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app-preferences-store.js","names":[],"sources":["../../src/api/app-preferences-store.ts"],"sourcesContent":["import { Settings } from \"#/types/settings\";\n\n/**\n * The local agent-server's PATCH /api/settings only persists\n * `agent_settings_diff` and `conversation_settings_diff`. App-level user\n * preferences (language, sound notifications, analytics consent, git\n * identity) have no native home
|
|
1
|
+
{"version":3,"file":"app-preferences-store.js","names":[],"sources":["../../src/api/app-preferences-store.ts"],"sourcesContent":["import { Settings } from \"#/types/settings\";\n\n/**\n * The local agent-server's PATCH /api/settings only persists\n * `agent_settings_diff` and `conversation_settings_diff`. App-level user\n * preferences (language, sound notifications, analytics consent, git\n * identity) have no native home in that schema, so we persist them in\n * localStorage as a workaround. This is the same fallback pattern used\n * by `DISABLED_SKILLS_STORAGE_KEY` in `settings-service.api.ts`.\n *\n * In cloud mode the cloud backend accepts these fields as flat top-level\n * keys at POST /api/v1/settings, and the cloud fetch returns them. We\n * still write through to localStorage so the values survive momentary\n * fetch failures and so the merge logic in `syncDerivedSettings` has a\n * single source for both modes.\n *\n * Long-term goal: extend the local agent-server settings schema to cover\n * these fields and delete this module entirely.\n */\nexport const APP_PREFERENCES_STORAGE_KEY =\n \"openhands-agent-server-app-preferences\";\n\nexport const APP_PREFERENCE_FIELDS = [\n \"language\",\n \"user_consents_to_analytics\",\n \"enable_sound_notifications\",\n \"git_user_name\",\n \"git_user_email\",\n] as const;\n\nexport type AppPreferenceField = (typeof APP_PREFERENCE_FIELDS)[number];\n\nexport type StoredAppPreferences = Partial<Pick<Settings, AppPreferenceField>>;\n\nconst APP_PREFERENCE_FIELD_SET: ReadonlySet<string> = new Set(\n APP_PREFERENCE_FIELDS,\n);\n\nconst isAppPreferenceField = (key: string): key is AppPreferenceField =>\n APP_PREFERENCE_FIELD_SET.has(key);\n\nconst coerceValue = (\n key: AppPreferenceField,\n value: unknown,\n): StoredAppPreferences[AppPreferenceField] | undefined => {\n switch (key) {\n case \"language\":\n case \"git_user_name\":\n case \"git_user_email\":\n return typeof value === \"string\" ? value : undefined;\n case \"enable_sound_notifications\":\n return typeof value === \"boolean\" ? value : undefined;\n case \"user_consents_to_analytics\":\n if (value === null) return null;\n return typeof value === \"boolean\" ? value : undefined;\n default:\n return undefined;\n }\n};\n\nconst sanitize = (input: Record<string, unknown>): StoredAppPreferences => {\n const out: StoredAppPreferences = {};\n for (const key of APP_PREFERENCE_FIELDS) {\n if (key in input) {\n const coerced = coerceValue(key, input[key]);\n if (coerced !== undefined) {\n // TS can't narrow per-key writes through a discriminated assignment,\n // so funnel through a single record write.\n (out as Record<string, unknown>)[key] = coerced;\n }\n }\n }\n return out;\n};\n\nexport const readStoredAppPreferences = (): StoredAppPreferences => {\n if (typeof window === \"undefined\") {\n return {};\n }\n\n try {\n const raw = window.localStorage.getItem(APP_PREFERENCES_STORAGE_KEY);\n if (!raw) return {};\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) {\n return {};\n }\n return sanitize(parsed as Record<string, unknown>);\n } catch {\n return {};\n }\n};\n\nexport const writeStoredAppPreferences = (\n partial: StoredAppPreferences,\n): void => {\n if (typeof window === \"undefined\") return;\n\n const sanitized = sanitize(partial as Record<string, unknown>);\n const existing = readStoredAppPreferences();\n const merged: StoredAppPreferences = { ...existing, ...sanitized };\n\n if (Object.keys(merged).length === 0) {\n window.localStorage.removeItem(APP_PREFERENCES_STORAGE_KEY);\n return;\n }\n\n window.localStorage.setItem(\n APP_PREFERENCES_STORAGE_KEY,\n JSON.stringify(merged),\n );\n};\n\nexport const clearStoredAppPreferences = (): void => {\n if (typeof window === \"undefined\") return;\n window.localStorage.removeItem(APP_PREFERENCES_STORAGE_KEY);\n};\n\n/**\n * Split known app-preference keys out of a save payload. Used by\n * `SettingsService.saveSettings` so the local PATCH only ever sees the\n * diff fields the agent-server accepts.\n */\nexport const extractAppPreferences = (\n input: Record<string, unknown>,\n): { extracted: StoredAppPreferences; rest: Record<string, unknown> } => {\n const extracted: StoredAppPreferences = {};\n const rest: Record<string, unknown> = {};\n\n for (const [key, value] of Object.entries(input)) {\n if (isAppPreferenceField(key)) {\n const coerced = coerceValue(key, value);\n if (coerced !== undefined) {\n (extracted as Record<string, unknown>)[key] = coerced;\n }\n } else {\n rest[key] = value;\n }\n }\n\n return { extracted, rest };\n};\n"],"mappings":";AAmBA,IAAa,IACX,0CAEW,IAAwB;CACnC;CACA;CACA;CACA;CACA;CACD,EAMK,IAAgD,IAAI,IACxD,EACD,EAEK,KAAwB,MAC5B,EAAyB,IAAI,EAAI,EAE7B,KACJ,GACA,MACyD;AACzD,SAAQ,GAAR;EACE,KAAK;EACL,KAAK;EACL,KAAK,iBACH,QAAO,OAAO,KAAU,WAAW,IAAQ,KAAA;EAC7C,KAAK,6BACH,QAAO,OAAO,KAAU,YAAY,IAAQ,KAAA;EAC9C,KAAK,6BAEH,QADI,MAAU,OAAa,OACpB,OAAO,KAAU,YAAY,IAAQ,KAAA;EAC9C,QACE;;GAIA,KAAY,MAAyD;CACzE,IAAM,IAA4B,EAAE;AACpC,MAAK,IAAM,KAAO,EAChB,KAAI,KAAO,GAAO;EAChB,IAAM,IAAU,EAAY,GAAK,EAAM,GAAK;AAC5C,EAAI,MAAY,KAAA,MAGb,EAAgC,KAAO;;AAI9C,QAAO;GAGI,UAAuD;AAClE,KAAI,OAAO,SAAW,IACpB,QAAO,EAAE;AAGX,KAAI;EACF,IAAM,IAAM,OAAO,aAAa,QAAQ,EAA4B;AACpE,MAAI,CAAC,EAAK,QAAO,EAAE;EACnB,IAAM,IAAS,KAAK,MAAM,EAAI;AAI9B,SAHI,CAAC,KAAU,OAAO,KAAW,YAAY,MAAM,QAAQ,EAAO,GACzD,EAAE,GAEJ,EAAS,EAAkC;SAC5C;AACN,SAAO,EAAE;;GAIA,KACX,MACS;AACT,KAAI,OAAO,SAAW,IAAa;CAEnC,IAAM,IAAY,EAAS,EAAmC,EAExD,IAA+B;EAAE,GADtB,GACyB;EAAU,GAAG;EAAW;AAElE,KAAI,OAAO,KAAK,EAAO,CAAC,WAAW,GAAG;AACpC,SAAO,aAAa,WAAW,EAA4B;AAC3D;;AAGF,QAAO,aAAa,QAClB,GACA,KAAK,UAAU,EAAO,CACvB;GAaU,KACX,MACuE;CACvE,IAAM,IAAkC,EAAE,EACpC,IAAgC,EAAE;AAExC,MAAK,IAAM,CAAC,GAAK,MAAU,OAAO,QAAQ,EAAM,CAC9C,KAAI,EAAqB,EAAI,EAAE;EAC7B,IAAM,IAAU,EAAY,GAAK,EAAM;AACvC,EAAI,MAAY,KAAA,MACb,EAAsC,KAAO;OAGhD,GAAK,KAAO;AAIhB,QAAO;EAAE;EAAW;EAAM"}
|
package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../types/agent-server/type-guards.cjs`);var t=t=>{if(e.isConversationStateUpdateEvent(t))return!1;if(e.isActionEvent(t)){let e=t.action.kind;return!(!e||e===`ExecuteBashAction`&&t.source===`user`||e===`PlanningFileEditorAction`||e===`SwitchLLMAction`)}return e.isObservationEvent(t)?!(t.observation.kind===`SwitchLLMObservation`&&!t.observation.is_error)
|
|
1
|
+
require(`../../../../_virtual/_rolldown/runtime.cjs`);const e=require(`../../../../types/agent-server/type-guards.cjs`);var t=t=>{if(e.isConversationStateUpdateEvent(t))return!1;if(e.isActionEvent(t)){let e=t.action.kind;return!(!e||e===`ExecuteBashAction`&&t.source===`user`||e===`PlanningFileEditorAction`||e===`SwitchLLMAction`)}return e.isObservationEvent(t)?!(t.observation.kind===`SwitchLLMObservation`&&!t.observation.is_error):e.isMessageEvent(t)||e.isAgentErrorEvent(t)||e.isHookExecutionEvent(t)||e.isACPToolCallEvent(t)?!0:e.isStreamingDeltaEvent(t)?t.content!==null||t.reasoning_content!==null:!1},n=e=>e.some(e=>e.source===`user`);exports.hasUserEvent=n,exports.shouldRenderEvent=t;
|
|
2
2
|
//# sourceMappingURL=should-render-event.cjs.map
|
package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"should-render-event.cjs","names":[],"sources":["../../../../../src/components/conversation-events/chat/event-content-helpers/should-render-event.ts"],"sourcesContent":["import { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isActionEvent,\n isObservationEvent,\n isMessageEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n} from \"#/types/agent-server/type-guards\";\n\nexport const shouldRenderEvent = (event: OpenHandsEvent) => {\n // Explicitly exclude system events that should not be rendered in chat\n if (isConversationStateUpdateEvent(event)) {\n return false;\n }\n\n // Render action events (with filtering)\n if (isActionEvent(event)) {\n // For V1, action is an object with kind property\n const actionType = event.action.kind;\n\n if (!actionType) {\n return false;\n }\n\n // Hide user commands from the chat interface\n if (actionType === \"ExecuteBashAction\" && event.source === \"user\") {\n return false;\n }\n\n // Hide PlanningFileEditorAction - handled separately with PlanPreview component\n if (actionType === \"PlanningFileEditorAction\") {\n return false;\n }\n\n // The model switch tool reuses the same inline model message UI as\n // `/model <profile>` once the observation arrives.\n if (actionType === \"SwitchLLMAction\") {\n return false;\n }\n\n return true;\n }\n\n // Render observation events\n if (isObservationEvent(event)) {\n // Successful model switches are rendered through ModelMessages so they\n // look identical to `/model <profile>` confirmations. Failed switches\n // still render as observations so the error remains visible in chat.\n if (\n event.observation.kind === \"SwitchLLMObservation\" &&\n !event.observation.is_error\n ) {\n return false;\n }\n\n return true;\n }\n\n // Render message events (user and assistant messages)\n if (isMessageEvent(event)) {\n return true;\n }\n\n // Render agent error events\n if (isAgentErrorEvent(event)) {\n return true;\n }\n\n // Render hook execution events\n if (isHookExecutionEvent(event)) {\n return true;\n }\n\n // Render ACP sub-agent tool call events at every lifecycle stage. The SDK\n // now persists exactly two events per ``tool_call_id`` — one early\n // ``started`` event (``pending`` / ``in_progress``) and one terminal\n // (``completed`` / ``failed``) event — the action->observation pair for a\n // tool call. The ``started`` event renders the card as \"running\" (no check\n // mark; see ``getACPToolCallResult``) and ``handleEventForUI`` replaces it\n // in place by ``tool_call_id`` once the terminal event arrives, mirroring\n // how an ObservationEvent supersedes its ActionEvent. The old terminal-only\n // gate existed because the source fanned out one cumulative-output frame per\n // ``ToolCallProgress``, which flashed half-formed cards mid-stream; that\n // fan-out is gone, so the running card is now a single clean event.\n if (isACPToolCallEvent(event)) {\n return true;\n }\n\n // Don't render any other event types (system events, etc.)\n return false;\n};\n\nexport const hasUserEvent = (events: OpenHandsEvent[]) =>\n events.some((event) => event.source === \"user\");\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"should-render-event.cjs","names":[],"sources":["../../../../../src/components/conversation-events/chat/event-content-helpers/should-render-event.ts"],"sourcesContent":["import { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isActionEvent,\n isObservationEvent,\n isMessageEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\n\nexport const shouldRenderEvent = (event: OpenHandsEvent) => {\n // Explicitly exclude system events that should not be rendered in chat\n if (isConversationStateUpdateEvent(event)) {\n return false;\n }\n\n // Render action events (with filtering)\n if (isActionEvent(event)) {\n // For V1, action is an object with kind property\n const actionType = event.action.kind;\n\n if (!actionType) {\n return false;\n }\n\n // Hide user commands from the chat interface\n if (actionType === \"ExecuteBashAction\" && event.source === \"user\") {\n return false;\n }\n\n // Hide PlanningFileEditorAction - handled separately with PlanPreview component\n if (actionType === \"PlanningFileEditorAction\") {\n return false;\n }\n\n // The model switch tool reuses the same inline model message UI as\n // `/model <profile>` once the observation arrives.\n if (actionType === \"SwitchLLMAction\") {\n return false;\n }\n\n return true;\n }\n\n // Render observation events\n if (isObservationEvent(event)) {\n // Successful model switches are rendered through ModelMessages so they\n // look identical to `/model <profile>` confirmations. Failed switches\n // still render as observations so the error remains visible in chat.\n if (\n event.observation.kind === \"SwitchLLMObservation\" &&\n !event.observation.is_error\n ) {\n return false;\n }\n\n return true;\n }\n\n // Render message events (user and assistant messages)\n if (isMessageEvent(event)) {\n return true;\n }\n\n // Render agent error events\n if (isAgentErrorEvent(event)) {\n return true;\n }\n\n // Render hook execution events\n if (isHookExecutionEvent(event)) {\n return true;\n }\n\n // Render ACP sub-agent tool call events at every lifecycle stage. The SDK\n // now persists exactly two events per ``tool_call_id`` — one early\n // ``started`` event (``pending`` / ``in_progress``) and one terminal\n // (``completed`` / ``failed``) event — the action->observation pair for a\n // tool call. The ``started`` event renders the card as \"running\" (no check\n // mark; see ``getACPToolCallResult``) and ``handleEventForUI`` replaces it\n // in place by ``tool_call_id`` once the terminal event arrives, mirroring\n // how an ObservationEvent supersedes its ActionEvent. The old terminal-only\n // gate existed because the source fanned out one cumulative-output frame per\n // ``ToolCallProgress``, which flashed half-formed cards mid-stream; that\n // fan-out is gone, so the running card is now a single clean event.\n if (isACPToolCallEvent(event)) {\n return true;\n }\n\n if (isStreamingDeltaEvent(event)) {\n return event.content !== null || event.reasoning_content !== null;\n }\n\n // Don't render any other event types (system events, etc.)\n return false;\n};\n\nexport const hasUserEvent = (events: OpenHandsEvent[]) =>\n events.some((event) => event.source === \"user\");\n"],"mappings":"wHAYA,IAAa,EAAqB,GAA0B,CAE1D,GAAI,EAAA,+BAA+B,EAAM,CACvC,MAAO,GAIT,GAAI,EAAA,cAAc,EAAM,CAAE,CAExB,IAAM,EAAa,EAAM,OAAO,KAsBhC,MAJA,EAhBI,CAAC,GAKD,IAAe,qBAAuB,EAAM,SAAW,QAKvD,IAAe,4BAMf,IAAe,mBAyDrB,OAjDI,EAAA,mBAAmB,EAAM,CAI3B,EACE,EAAM,YAAY,OAAS,wBAC3B,CAAC,EAAM,YAAY,UASnB,EAAA,eAAe,EAAM,EAKrB,EAAA,kBAAkB,EAAM,EAKxB,EAAA,qBAAqB,EAAM,EAe3B,EAAA,mBAAmB,EAAM,CACpB,GAGL,EAAA,sBAAsB,EAAM,CACvB,EAAM,UAAY,MAAQ,EAAM,oBAAsB,KAIxD,IAGI,EAAgB,GAC3B,EAAO,KAAM,GAAU,EAAM,SAAW,OAAO"}
|
package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { isACPToolCallEvent as e, isActionEvent as t, isAgentErrorEvent as n, isConversationStateUpdateEvent as r, isHookExecutionEvent as i, isMessageEvent as a, isObservationEvent as o } from "../../../../types/agent-server/type-guards.js";
|
|
1
|
+
import { isACPToolCallEvent as e, isActionEvent as t, isAgentErrorEvent as n, isConversationStateUpdateEvent as r, isHookExecutionEvent as i, isMessageEvent as a, isObservationEvent as o, isStreamingDeltaEvent as s } from "../../../../types/agent-server/type-guards.js";
|
|
2
2
|
//#region src/components/conversation-events/chat/event-content-helpers/should-render-event.ts
|
|
3
|
-
var
|
|
4
|
-
if (r(
|
|
5
|
-
if (t(
|
|
6
|
-
let e =
|
|
7
|
-
return !(!e || e === "ExecuteBashAction" &&
|
|
3
|
+
var c = (c) => {
|
|
4
|
+
if (r(c)) return !1;
|
|
5
|
+
if (t(c)) {
|
|
6
|
+
let e = c.action.kind;
|
|
7
|
+
return !(!e || e === "ExecuteBashAction" && c.source === "user" || e === "PlanningFileEditorAction" || e === "SwitchLLMAction");
|
|
8
8
|
}
|
|
9
|
-
return o(
|
|
10
|
-
},
|
|
9
|
+
return o(c) ? !(c.observation.kind === "SwitchLLMObservation" && !c.observation.is_error) : a(c) || n(c) || i(c) || e(c) ? !0 : s(c) ? c.content !== null || c.reasoning_content !== null : !1;
|
|
10
|
+
}, l = (e) => e.some((e) => e.source === "user");
|
|
11
11
|
//#endregion
|
|
12
|
-
export {
|
|
12
|
+
export { l as hasUserEvent, c as shouldRenderEvent };
|
|
13
13
|
|
|
14
14
|
//# sourceMappingURL=should-render-event.js.map
|
package/dist/components/conversation-events/chat/event-content-helpers/should-render-event.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"should-render-event.js","names":[],"sources":["../../../../../src/components/conversation-events/chat/event-content-helpers/should-render-event.ts"],"sourcesContent":["import { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isActionEvent,\n isObservationEvent,\n isMessageEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n} from \"#/types/agent-server/type-guards\";\n\nexport const shouldRenderEvent = (event: OpenHandsEvent) => {\n // Explicitly exclude system events that should not be rendered in chat\n if (isConversationStateUpdateEvent(event)) {\n return false;\n }\n\n // Render action events (with filtering)\n if (isActionEvent(event)) {\n // For V1, action is an object with kind property\n const actionType = event.action.kind;\n\n if (!actionType) {\n return false;\n }\n\n // Hide user commands from the chat interface\n if (actionType === \"ExecuteBashAction\" && event.source === \"user\") {\n return false;\n }\n\n // Hide PlanningFileEditorAction - handled separately with PlanPreview component\n if (actionType === \"PlanningFileEditorAction\") {\n return false;\n }\n\n // The model switch tool reuses the same inline model message UI as\n // `/model <profile>` once the observation arrives.\n if (actionType === \"SwitchLLMAction\") {\n return false;\n }\n\n return true;\n }\n\n // Render observation events\n if (isObservationEvent(event)) {\n // Successful model switches are rendered through ModelMessages so they\n // look identical to `/model <profile>` confirmations. Failed switches\n // still render as observations so the error remains visible in chat.\n if (\n event.observation.kind === \"SwitchLLMObservation\" &&\n !event.observation.is_error\n ) {\n return false;\n }\n\n return true;\n }\n\n // Render message events (user and assistant messages)\n if (isMessageEvent(event)) {\n return true;\n }\n\n // Render agent error events\n if (isAgentErrorEvent(event)) {\n return true;\n }\n\n // Render hook execution events\n if (isHookExecutionEvent(event)) {\n return true;\n }\n\n // Render ACP sub-agent tool call events at every lifecycle stage. The SDK\n // now persists exactly two events per ``tool_call_id`` — one early\n // ``started`` event (``pending`` / ``in_progress``) and one terminal\n // (``completed`` / ``failed``) event — the action->observation pair for a\n // tool call. The ``started`` event renders the card as \"running\" (no check\n // mark; see ``getACPToolCallResult``) and ``handleEventForUI`` replaces it\n // in place by ``tool_call_id`` once the terminal event arrives, mirroring\n // how an ObservationEvent supersedes its ActionEvent. The old terminal-only\n // gate existed because the source fanned out one cumulative-output frame per\n // ``ToolCallProgress``, which flashed half-formed cards mid-stream; that\n // fan-out is gone, so the running card is now a single clean event.\n if (isACPToolCallEvent(event)) {\n return true;\n }\n\n // Don't render any other event types (system events, etc.)\n return false;\n};\n\nexport const hasUserEvent = (events: OpenHandsEvent[]) =>\n events.some((event) => event.source === \"user\");\n"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"should-render-event.js","names":[],"sources":["../../../../../src/components/conversation-events/chat/event-content-helpers/should-render-event.ts"],"sourcesContent":["import { OpenHandsEvent } from \"#/types/agent-server/core\";\nimport {\n isActionEvent,\n isObservationEvent,\n isMessageEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\n\nexport const shouldRenderEvent = (event: OpenHandsEvent) => {\n // Explicitly exclude system events that should not be rendered in chat\n if (isConversationStateUpdateEvent(event)) {\n return false;\n }\n\n // Render action events (with filtering)\n if (isActionEvent(event)) {\n // For V1, action is an object with kind property\n const actionType = event.action.kind;\n\n if (!actionType) {\n return false;\n }\n\n // Hide user commands from the chat interface\n if (actionType === \"ExecuteBashAction\" && event.source === \"user\") {\n return false;\n }\n\n // Hide PlanningFileEditorAction - handled separately with PlanPreview component\n if (actionType === \"PlanningFileEditorAction\") {\n return false;\n }\n\n // The model switch tool reuses the same inline model message UI as\n // `/model <profile>` once the observation arrives.\n if (actionType === \"SwitchLLMAction\") {\n return false;\n }\n\n return true;\n }\n\n // Render observation events\n if (isObservationEvent(event)) {\n // Successful model switches are rendered through ModelMessages so they\n // look identical to `/model <profile>` confirmations. Failed switches\n // still render as observations so the error remains visible in chat.\n if (\n event.observation.kind === \"SwitchLLMObservation\" &&\n !event.observation.is_error\n ) {\n return false;\n }\n\n return true;\n }\n\n // Render message events (user and assistant messages)\n if (isMessageEvent(event)) {\n return true;\n }\n\n // Render agent error events\n if (isAgentErrorEvent(event)) {\n return true;\n }\n\n // Render hook execution events\n if (isHookExecutionEvent(event)) {\n return true;\n }\n\n // Render ACP sub-agent tool call events at every lifecycle stage. The SDK\n // now persists exactly two events per ``tool_call_id`` — one early\n // ``started`` event (``pending`` / ``in_progress``) and one terminal\n // (``completed`` / ``failed``) event — the action->observation pair for a\n // tool call. The ``started`` event renders the card as \"running\" (no check\n // mark; see ``getACPToolCallResult``) and ``handleEventForUI`` replaces it\n // in place by ``tool_call_id`` once the terminal event arrives, mirroring\n // how an ObservationEvent supersedes its ActionEvent. The old terminal-only\n // gate existed because the source fanned out one cumulative-output frame per\n // ``ToolCallProgress``, which flashed half-formed cards mid-stream; that\n // fan-out is gone, so the running card is now a single clean event.\n if (isACPToolCallEvent(event)) {\n return true;\n }\n\n if (isStreamingDeltaEvent(event)) {\n return event.content !== null || event.reasoning_content !== null;\n }\n\n // Don't render any other event types (system events, etc.)\n return false;\n};\n\nexport const hasUserEvent = (events: OpenHandsEvent[]) =>\n events.some((event) => event.source === \"user\");\n"],"mappings":";;AAYA,IAAa,KAAqB,MAA0B;AAE1D,KAAI,EAA+B,EAAM,CACvC,QAAO;AAIT,KAAI,EAAc,EAAM,EAAE;EAExB,IAAM,IAAa,EAAM,OAAO;AAsBhC,SAJA,EAhBI,CAAC,KAKD,MAAe,uBAAuB,EAAM,WAAW,UAKvD,MAAe,8BAMf,MAAe;;AAyDrB,QAjDI,EAAmB,EAAM,GAI3B,EACE,EAAM,YAAY,SAAS,0BAC3B,CAAC,EAAM,YAAY,YASnB,EAAe,EAAM,IAKrB,EAAkB,EAAM,IAKxB,EAAqB,EAAM,IAe3B,EAAmB,EAAM,GACpB,KAGL,EAAsB,EAAM,GACvB,EAAM,YAAY,QAAQ,EAAM,sBAAsB,OAIxD;GAGI,KAAgB,MAC3B,EAAO,MAAM,MAAU,EAAM,WAAW,OAAO"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../types/agent-state.cjs`),n=require(`../../../stores/conversation-store.cjs`),r=require(`../../../types/agent-server/type-guards.cjs`),i=require(`../../../hooks/use-agent-state.cjs`),a=require(`../../../hooks/query/use-config.cjs`),o=require(`../../features/chat/plan-preview.cjs`),
|
|
1
|
+
const e=require(`../../../_virtual/_rolldown/runtime.cjs`),t=require(`../../../types/agent-state.cjs`),n=require(`../../../stores/conversation-store.cjs`),r=require(`../../../types/agent-server/type-guards.cjs`),i=require(`../../../hooks/use-agent-state.cjs`),a=require(`../../../hooks/query/use-config.cjs`),o=require(`../../features/chat/chat-message.cjs`),s=require(`../../features/chat/plan-preview.cjs`),c=require(`./event-message-components/error-event-message.cjs`),l=require(`./event-message-components/user-assistant-event-message.cjs`),u=require(`./event-content-helpers/create-skill-ready-event.cjs`),d=require(`./event-message-components/finish-event-message.cjs`),f=require(`./event-message-components/generic-event-message-wrapper.cjs`),p=require(`./event-message-components/thought-event-message.cjs`),m=require(`./event-message-components/collapsible-thinking.cjs`),h=require(`../../shared/hook-execution-event-message.cjs`);require(`./event-message-components/hook-execution-event-message.cjs`);const g=require(`./hooks/use-plan-preview-events.cjs`),_=require(`./event-thought-helpers.cjs`);let v=require(`react`);v=e.__toESM(v,1);let y=require(`react/jsx-runtime`);var b=e=>e.activated_skills||e.activated_microagents||[],x=e=>!e||e.length===0?!1:e.some(e=>e.type===`text`&&e.text.trim().length>0),S=e=>{let t=b(e).length>0,n=x(e.extended_content);return t&&n},C=(e,t,n)=>{try{let r=u.createSkillReadyEvent(e);return(0,y.jsxs)(y.Fragment,{children:[(0,y.jsx)(l.UserAssistantEventMessage,{event:e,isLastMessage:!1,isFromPlanningAgent:t.isFromPlanningAgent}),(0,y.jsx)(f.GenericEventMessageWrapper,{event:r,isLastMessage:n})]})}catch{return(0,y.jsx)(l.UserAssistantEventMessage,{event:e,isLastMessage:n,isFromPlanningAgent:t.isFromPlanningAgent})}};function w({event:e,messages:u,isLastMessage:v,isInLast10Actions:b,planPreviewEventIds:x,suppressThought:w=!1}){let{data:T}=a.useConfig(),{planContent:E}=n.useConversationStore(),{curAgentState:D}=i.useAgentState(),O=D===t.AgentState.RUNNING||D===t.AgentState.LOADING,k=e.isFromPlanningAgent||!1,A={isLastMessage:v,isInLast10Actions:b,config:T,isFromPlanningAgent:k};if(r.isAgentErrorEvent(e))return(0,y.jsx)(c.ErrorEventMessage,{event:e,...A});if(r.isHookExecutionEvent(e))return(0,y.jsx)(h.HookExecutionEventMessage,{event:e});if(r.isACPToolCallEvent(e))return(0,y.jsx)(f.GenericEventMessageWrapper,{event:e,isLastMessage:v});if(r.isStreamingDeltaEvent(e)){let t=e.content??``,n=e.reasoning_content??``;return(0,y.jsxs)(y.Fragment,{children:[n&&(0,y.jsx)(m.CollapsibleThinking,{content:n}),t&&(0,y.jsx)(o.ChatMessage,{type:`agent`,message:t,isFromPlanningAgent:k})]})}if(r.isActionEvent(e)&&e.action.kind===`FinishAction`)return(0,y.jsx)(d.FinishEventMessage,{event:e,...A});if(r.isActionEvent(e)&&e.action.kind===`ThinkAction`)return(0,y.jsx)(m.CollapsibleThinking,{content:e.action.thought});if(r.isActionEvent(e)){let t=_.getReasoningContent(e);return(0,y.jsxs)(y.Fragment,{children:[t&&(0,y.jsx)(m.CollapsibleThinking,{content:t}),!w&&(0,y.jsx)(p.ThoughtEventMessage,{event:e,isFromPlanningAgent:k}),(0,y.jsx)(f.GenericEventMessageWrapper,{event:e,isLastMessage:v})]})}if(r.isObservationEvent(e)){if(r.isPlanningFileEditorObservationEvent(e))return x&&g.shouldShowPlanPreview(e.id,x)?(0,y.jsx)(s.PlanPreview,{planContent:E,isStreaming:v&&D===t.AgentState.RUNNING,isBuildDisabled:O}):null;let n=u.find(t=>r.isActionEvent(t)&&t.id===e.action_id),i=!w&&n&&r.isActionEvent(n)&&n.action.kind!==`ThinkAction`,a=n&&r.isActionEvent(n)?_.getReasoningContent(n):``;return(0,y.jsxs)(y.Fragment,{children:[a&&(0,y.jsx)(m.CollapsibleThinking,{content:a}),i&&(0,y.jsx)(p.ThoughtEventMessage,{event:n,isFromPlanningAgent:k}),(0,y.jsx)(f.GenericEventMessageWrapper,{event:e,isLastMessage:v,correspondingAction:n&&r.isActionEvent(n)?n:void 0})]})}if(!r.isActionEvent(e)&&!r.isObservationEvent(e)){let t=e;return r.isUserMessageEvent(e)&&S(t)?C(t,A,v):(0,y.jsx)(l.UserAssistantEventMessage,{event:t,...A,isLastMessage:v})}return(0,y.jsx)(f.GenericEventMessageWrapper,{event:e,isLastMessage:v})}exports.EventMessage=w;
|
|
2
2
|
//# sourceMappingURL=event-message.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"event-message.cjs","names":[],"sources":["../../../../src/components/conversation-events/chat/event-message.tsx"],"sourcesContent":["import React from \"react\";\nimport {\n OpenHandsEvent,\n MessageEvent,\n ActionEvent,\n} from \"#/types/agent-server/core\";\nimport {\n FinishAction,\n ThinkAction,\n} from \"#/types/agent-server/core/base/action\";\nimport {\n isActionEvent,\n isObservationEvent,\n isAgentErrorEvent,\n isUserMessageEvent,\n isPlanningFileEditorObservationEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { useConfig } from \"#/hooks/query/use-config\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { PlanPreview } from \"../../features/chat/plan-preview\";\nimport { ErrorEventMessage } from \"./event-message-components/error-event-message\";\nimport { UserAssistantEventMessage } from \"./event-message-components/user-assistant-event-message\";\nimport { FinishEventMessage } from \"./event-message-components/finish-event-message\";\nimport { GenericEventMessageWrapper } from \"./event-message-components/generic-event-message-wrapper\";\nimport { ThoughtEventMessage } from \"./event-message-components/thought-event-message\";\nimport { CollapsibleThinking } from \"./event-message-components/collapsible-thinking\";\nimport { HookExecutionEventMessage } from \"./event-message-components/hook-execution-event-message\";\nimport { createSkillReadyEvent } from \"./event-content-helpers/create-skill-ready-event\";\nimport { shouldShowPlanPreview } from \"./hooks/use-plan-preview-events\";\nimport { getReasoningContent } from \"./event-thought-helpers\";\n\ninterface EventMessageProps {\n event: OpenHandsEvent & { isFromPlanningAgent?: boolean };\n messages: OpenHandsEvent[];\n isLastMessage: boolean;\n isInLast10Actions: boolean;\n /** Set of event IDs that should render PlanPreview (one per user message phase) */\n planPreviewEventIds?: Set<string>;\n /**\n * When true, do not render the inline `ThoughtEventMessage` for action /\n * observation events. The caller is expected to render the thought\n * separately (e.g. via a hoisted \"thought\" rendered item) so the message\n * pane and the in-group rendering don't double up on the same content.\n * Has no effect on `ThinkAction`, whose thought IS its action body.\n */\n suppressThought?: boolean;\n}\n\n/**\n * Extracts activated skills from a MessageEvent, supporting both\n * activated_skills and activated_microagents field names.\n */\nconst getActivatedSkills = (event: MessageEvent): string[] =>\n (event as unknown as { activated_skills?: string[] }).activated_skills ||\n event.activated_microagents ||\n [];\n\n/**\n * Checks if extended content contains valid text content.\n */\nconst hasValidExtendedContent = (\n extendedContent: MessageEvent[\"extended_content\"],\n): boolean => {\n if (!extendedContent || extendedContent.length === 0) {\n return false;\n }\n\n return extendedContent.some(\n (content) => content.type === \"text\" && content.text.trim().length > 0,\n );\n};\n\n/**\n * Determines if a Skill Ready event should be displayed for the given message event.\n */\nconst shouldShowSkillReadyEvent = (messageEvent: MessageEvent): boolean => {\n const activatedSkills = getActivatedSkills(messageEvent);\n const hasActivatedSkills = activatedSkills.length > 0;\n const hasExtendedContent = hasValidExtendedContent(\n messageEvent.extended_content,\n );\n\n return hasActivatedSkills && hasExtendedContent;\n};\n\ninterface CommonProps {\n isLastMessage: boolean;\n isInLast10Actions: boolean;\n config: unknown;\n isFromPlanningAgent: boolean;\n}\n\n/**\n * Renders a user message with its corresponding Skill Ready event.\n */\nconst renderUserMessageWithSkillReady = (\n messageEvent: MessageEvent,\n commonProps: CommonProps,\n isLastMessage: boolean,\n): React.ReactElement => {\n try {\n const skillReadyEvent = createSkillReadyEvent(messageEvent);\n return (\n <>\n <UserAssistantEventMessage\n event={messageEvent}\n isLastMessage={false}\n isFromPlanningAgent={commonProps.isFromPlanningAgent}\n />\n <GenericEventMessageWrapper\n event={skillReadyEvent}\n isLastMessage={isLastMessage}\n />\n </>\n );\n } catch (error) {\n // If skill ready event creation fails, just render the user message\n return (\n <UserAssistantEventMessage\n event={messageEvent}\n isLastMessage={isLastMessage}\n isFromPlanningAgent={commonProps.isFromPlanningAgent}\n />\n );\n }\n};\n\nexport function EventMessage({\n event,\n messages,\n isLastMessage,\n isInLast10Actions,\n planPreviewEventIds,\n suppressThought = false,\n}: EventMessageProps) {\n const { data: config } = useConfig();\n const { planContent } = useConversationStore();\n const { curAgentState } = useAgentState();\n\n // Disable Build button while agent is running (streaming)\n const isAgentRunning =\n curAgentState === AgentState.RUNNING ||\n curAgentState === AgentState.LOADING;\n\n // Read isFromPlanningAgent directly from the event object\n const isFromPlanningAgent = event.isFromPlanningAgent || false;\n\n // Common props for components that need them\n const commonProps = {\n isLastMessage,\n isInLast10Actions,\n config,\n isFromPlanningAgent,\n };\n\n // Agent error events\n if (isAgentErrorEvent(event)) {\n return <ErrorEventMessage event={event} {...commonProps} />;\n }\n\n // Hook execution events\n if (isHookExecutionEvent(event)) {\n return <HookExecutionEventMessage event={event} />;\n }\n\n // ACP sub-agent tool call events (Claude Code, Codex, Gemini CLI, …)\n // render through the same generic wrapper used for observation events so\n // the card shape, success indicator and markdown rendering all match.\n if (isACPToolCallEvent(event)) {\n return (\n <GenericEventMessageWrapper event={event} isLastMessage={isLastMessage} />\n );\n }\n\n // Finish actions\n if (isActionEvent(event) && event.action.kind === \"FinishAction\") {\n return (\n <FinishEventMessage\n event={event as ActionEvent<FinishAction>}\n {...commonProps}\n />\n );\n }\n\n // ThinkAction - render the thought inside a collapsible section so it\n // doesn't dominate the chat, especially when the thinking language\n // (usually English) differs from the conversation language.\n if (isActionEvent(event) && event.action.kind === \"ThinkAction\") {\n const thinkAction = event as ActionEvent<ThinkAction>;\n return <CollapsibleThinking content={thinkAction.action.thought} />;\n }\n\n // Action events - render thought + action (will be replaced by thought + observation)\n if (isActionEvent(event)) {\n const reasoningContent = getReasoningContent(event);\n return (\n <>\n {reasoningContent && <CollapsibleThinking content={reasoningContent} />}\n {!suppressThought && (\n <ThoughtEventMessage\n event={event}\n isFromPlanningAgent={isFromPlanningAgent}\n />\n )}\n <GenericEventMessageWrapper\n event={event}\n isLastMessage={isLastMessage}\n />\n </>\n );\n }\n\n // Observation events - find the corresponding action and render thought + observation\n if (isObservationEvent(event)) {\n // Handle PlanningFileEditorObservation specially\n if (isPlanningFileEditorObservationEvent(event)) {\n // Only show PlanPreview if this event is marked as the one to display\n // (last PlanningFileEditorObservation in its phase)\n if (\n planPreviewEventIds &&\n shouldShowPlanPreview(event.id, planPreviewEventIds)\n ) {\n // Show shine effect only if this is the last message AND agent is running\n const isStreaming =\n isLastMessage && curAgentState === AgentState.RUNNING;\n return (\n <PlanPreview\n planContent={planContent}\n isStreaming={isStreaming}\n isBuildDisabled={isAgentRunning}\n />\n );\n }\n // Not the designated preview event for this phase - render nothing\n // This prevents duplicate previews within the same phase\n return null;\n }\n\n // Find the action that this observation is responding to\n const correspondingAction = messages.find(\n (msg) => isActionEvent(msg) && msg.id === event.action_id,\n );\n\n // Skip ThoughtEventMessage for ThinkAction (thought IS the action)\n const shouldShowThought =\n !suppressThought &&\n correspondingAction &&\n isActionEvent(correspondingAction) &&\n correspondingAction.action.kind !== \"ThinkAction\";\n\n const reasoningContent =\n correspondingAction && isActionEvent(correspondingAction)\n ? getReasoningContent(correspondingAction)\n : \"\";\n\n return (\n <>\n {reasoningContent && <CollapsibleThinking content={reasoningContent} />}\n {shouldShowThought && (\n <ThoughtEventMessage\n event={correspondingAction}\n isFromPlanningAgent={isFromPlanningAgent}\n />\n )}\n <GenericEventMessageWrapper\n event={event}\n isLastMessage={isLastMessage}\n correspondingAction={\n correspondingAction && isActionEvent(correspondingAction)\n ? correspondingAction\n : undefined\n }\n />\n </>\n );\n }\n\n // Message events (user and assistant messages)\n if (!isActionEvent(event) && !isObservationEvent(event)) {\n const messageEvent = event as MessageEvent;\n\n // Check if this is a user message that should display a Skill Ready event\n if (isUserMessageEvent(event) && shouldShowSkillReadyEvent(messageEvent)) {\n return renderUserMessageWithSkillReady(\n messageEvent,\n commonProps,\n isLastMessage,\n );\n }\n\n // Render normal message event (user or assistant)\n return (\n <UserAssistantEventMessage\n event={messageEvent}\n {...commonProps}\n isLastMessage={isLastMessage}\n />\n );\n }\n\n // Generic fallback for all other events\n return (\n <GenericEventMessageWrapper event={event} isLastMessage={isLastMessage} />\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"event-message.cjs","names":[],"sources":["../../../../src/components/conversation-events/chat/event-message.tsx"],"sourcesContent":["import React from \"react\";\nimport {\n OpenHandsEvent,\n MessageEvent,\n ActionEvent,\n} from \"#/types/agent-server/core\";\nimport {\n FinishAction,\n ThinkAction,\n} from \"#/types/agent-server/core/base/action\";\nimport {\n isActionEvent,\n isObservationEvent,\n isAgentErrorEvent,\n isUserMessageEvent,\n isPlanningFileEditorObservationEvent,\n isHookExecutionEvent,\n isACPToolCallEvent,\n isStreamingDeltaEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { useConfig } from \"#/hooks/query/use-config\";\nimport { useConversationStore } from \"#/stores/conversation-store\";\nimport { useAgentState } from \"#/hooks/use-agent-state\";\nimport { AgentState } from \"#/types/agent-state\";\nimport { ChatMessage } from \"#/components/features/chat/chat-message\";\nimport { PlanPreview } from \"../../features/chat/plan-preview\";\nimport { ErrorEventMessage } from \"./event-message-components/error-event-message\";\nimport { UserAssistantEventMessage } from \"./event-message-components/user-assistant-event-message\";\nimport { FinishEventMessage } from \"./event-message-components/finish-event-message\";\nimport { GenericEventMessageWrapper } from \"./event-message-components/generic-event-message-wrapper\";\nimport { ThoughtEventMessage } from \"./event-message-components/thought-event-message\";\nimport { CollapsibleThinking } from \"./event-message-components/collapsible-thinking\";\nimport { HookExecutionEventMessage } from \"./event-message-components/hook-execution-event-message\";\nimport { createSkillReadyEvent } from \"./event-content-helpers/create-skill-ready-event\";\nimport { shouldShowPlanPreview } from \"./hooks/use-plan-preview-events\";\nimport { getReasoningContent } from \"./event-thought-helpers\";\n\ninterface EventMessageProps {\n event: OpenHandsEvent & { isFromPlanningAgent?: boolean };\n messages: OpenHandsEvent[];\n isLastMessage: boolean;\n isInLast10Actions: boolean;\n /** Set of event IDs that should render PlanPreview (one per user message phase) */\n planPreviewEventIds?: Set<string>;\n /**\n * When true, do not render the inline `ThoughtEventMessage` for action /\n * observation events. The caller is expected to render the thought\n * separately (e.g. via a hoisted \"thought\" rendered item) so the message\n * pane and the in-group rendering don't double up on the same content.\n * Has no effect on `ThinkAction`, whose thought IS its action body.\n */\n suppressThought?: boolean;\n}\n\n/**\n * Extracts activated skills from a MessageEvent, supporting both\n * activated_skills and activated_microagents field names.\n */\nconst getActivatedSkills = (event: MessageEvent): string[] =>\n (event as unknown as { activated_skills?: string[] }).activated_skills ||\n event.activated_microagents ||\n [];\n\n/**\n * Checks if extended content contains valid text content.\n */\nconst hasValidExtendedContent = (\n extendedContent: MessageEvent[\"extended_content\"],\n): boolean => {\n if (!extendedContent || extendedContent.length === 0) {\n return false;\n }\n\n return extendedContent.some(\n (content) => content.type === \"text\" && content.text.trim().length > 0,\n );\n};\n\n/**\n * Determines if a Skill Ready event should be displayed for the given message event.\n */\nconst shouldShowSkillReadyEvent = (messageEvent: MessageEvent): boolean => {\n const activatedSkills = getActivatedSkills(messageEvent);\n const hasActivatedSkills = activatedSkills.length > 0;\n const hasExtendedContent = hasValidExtendedContent(\n messageEvent.extended_content,\n );\n\n return hasActivatedSkills && hasExtendedContent;\n};\n\ninterface CommonProps {\n isLastMessage: boolean;\n isInLast10Actions: boolean;\n config: unknown;\n isFromPlanningAgent: boolean;\n}\n\n/**\n * Renders a user message with its corresponding Skill Ready event.\n */\nconst renderUserMessageWithSkillReady = (\n messageEvent: MessageEvent,\n commonProps: CommonProps,\n isLastMessage: boolean,\n): React.ReactElement => {\n try {\n const skillReadyEvent = createSkillReadyEvent(messageEvent);\n return (\n <>\n <UserAssistantEventMessage\n event={messageEvent}\n isLastMessage={false}\n isFromPlanningAgent={commonProps.isFromPlanningAgent}\n />\n <GenericEventMessageWrapper\n event={skillReadyEvent}\n isLastMessage={isLastMessage}\n />\n </>\n );\n } catch (error) {\n // If skill ready event creation fails, just render the user message\n return (\n <UserAssistantEventMessage\n event={messageEvent}\n isLastMessage={isLastMessage}\n isFromPlanningAgent={commonProps.isFromPlanningAgent}\n />\n );\n }\n};\n\nexport function EventMessage({\n event,\n messages,\n isLastMessage,\n isInLast10Actions,\n planPreviewEventIds,\n suppressThought = false,\n}: EventMessageProps) {\n const { data: config } = useConfig();\n const { planContent } = useConversationStore();\n const { curAgentState } = useAgentState();\n\n // Disable Build button while agent is running (streaming)\n const isAgentRunning =\n curAgentState === AgentState.RUNNING ||\n curAgentState === AgentState.LOADING;\n\n // Read isFromPlanningAgent directly from the event object\n const isFromPlanningAgent = event.isFromPlanningAgent || false;\n\n // Common props for components that need them\n const commonProps = {\n isLastMessage,\n isInLast10Actions,\n config,\n isFromPlanningAgent,\n };\n\n // Agent error events\n if (isAgentErrorEvent(event)) {\n return <ErrorEventMessage event={event} {...commonProps} />;\n }\n\n // Hook execution events\n if (isHookExecutionEvent(event)) {\n return <HookExecutionEventMessage event={event} />;\n }\n\n // ACP sub-agent tool call events (Claude Code, Codex, Gemini CLI, …)\n // render through the same generic wrapper used for observation events so\n // the card shape, success indicator and markdown rendering all match.\n if (isACPToolCallEvent(event)) {\n return (\n <GenericEventMessageWrapper event={event} isLastMessage={isLastMessage} />\n );\n }\n\n if (isStreamingDeltaEvent(event)) {\n const content = event.content ?? \"\";\n const reasoningContent = event.reasoning_content ?? \"\";\n return (\n <>\n {reasoningContent && <CollapsibleThinking content={reasoningContent} />}\n {content && (\n <ChatMessage\n type=\"agent\"\n message={content}\n isFromPlanningAgent={isFromPlanningAgent}\n />\n )}\n </>\n );\n }\n\n // Finish actions\n if (isActionEvent(event) && event.action.kind === \"FinishAction\") {\n return (\n <FinishEventMessage\n event={event as ActionEvent<FinishAction>}\n {...commonProps}\n />\n );\n }\n\n // ThinkAction - render the thought inside a collapsible section so it\n // doesn't dominate the chat, especially when the thinking language\n // (usually English) differs from the conversation language.\n if (isActionEvent(event) && event.action.kind === \"ThinkAction\") {\n const thinkAction = event as ActionEvent<ThinkAction>;\n return <CollapsibleThinking content={thinkAction.action.thought} />;\n }\n\n // Action events - render thought + action (will be replaced by thought + observation)\n if (isActionEvent(event)) {\n const reasoningContent = getReasoningContent(event);\n return (\n <>\n {reasoningContent && <CollapsibleThinking content={reasoningContent} />}\n {!suppressThought && (\n <ThoughtEventMessage\n event={event}\n isFromPlanningAgent={isFromPlanningAgent}\n />\n )}\n <GenericEventMessageWrapper\n event={event}\n isLastMessage={isLastMessage}\n />\n </>\n );\n }\n\n // Observation events - find the corresponding action and render thought + observation\n if (isObservationEvent(event)) {\n // Handle PlanningFileEditorObservation specially\n if (isPlanningFileEditorObservationEvent(event)) {\n // Only show PlanPreview if this event is marked as the one to display\n // (last PlanningFileEditorObservation in its phase)\n if (\n planPreviewEventIds &&\n shouldShowPlanPreview(event.id, planPreviewEventIds)\n ) {\n // Show shine effect only if this is the last message AND agent is running\n const isStreaming =\n isLastMessage && curAgentState === AgentState.RUNNING;\n return (\n <PlanPreview\n planContent={planContent}\n isStreaming={isStreaming}\n isBuildDisabled={isAgentRunning}\n />\n );\n }\n // Not the designated preview event for this phase - render nothing\n // This prevents duplicate previews within the same phase\n return null;\n }\n\n // Find the action that this observation is responding to\n const correspondingAction = messages.find(\n (msg) => isActionEvent(msg) && msg.id === event.action_id,\n );\n\n // Skip ThoughtEventMessage for ThinkAction (thought IS the action)\n const shouldShowThought =\n !suppressThought &&\n correspondingAction &&\n isActionEvent(correspondingAction) &&\n correspondingAction.action.kind !== \"ThinkAction\";\n\n const reasoningContent =\n correspondingAction && isActionEvent(correspondingAction)\n ? getReasoningContent(correspondingAction)\n : \"\";\n\n return (\n <>\n {reasoningContent && <CollapsibleThinking content={reasoningContent} />}\n {shouldShowThought && (\n <ThoughtEventMessage\n event={correspondingAction}\n isFromPlanningAgent={isFromPlanningAgent}\n />\n )}\n <GenericEventMessageWrapper\n event={event}\n isLastMessage={isLastMessage}\n correspondingAction={\n correspondingAction && isActionEvent(correspondingAction)\n ? correspondingAction\n : undefined\n }\n />\n </>\n );\n }\n\n // Message events (user and assistant messages)\n if (!isActionEvent(event) && !isObservationEvent(event)) {\n const messageEvent = event as MessageEvent;\n\n // Check if this is a user message that should display a Skill Ready event\n if (isUserMessageEvent(event) && shouldShowSkillReadyEvent(messageEvent)) {\n return renderUserMessageWithSkillReady(\n messageEvent,\n commonProps,\n isLastMessage,\n );\n }\n\n // Render normal message event (user or assistant)\n return (\n <UserAssistantEventMessage\n event={messageEvent}\n {...commonProps}\n isLastMessage={isLastMessage}\n />\n );\n }\n\n // Generic fallback for all other events\n return (\n <GenericEventMessageWrapper event={event} isLastMessage={isLastMessage} />\n );\n}\n"],"mappings":"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"}
|