@openhands/agent-canvas 1.2.1 → 1.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/README.windows.md +2 -2
- package/build/assets/{Trans-De9RwQhA.js → Trans-Ch-OVWEA.js} +1 -1
- package/build/assets/{acp-providers-D_D8VUpP.js → acp-providers-Sy2LKyMa.js} +1 -1
- package/build/assets/active-backend-context-hxq31W25.js +1 -0
- package/build/assets/{add-backend-modal-DIEyAkA4.js → add-backend-modal-Bgy8anoR.js} +1 -1
- package/build/assets/{agent-profiles-settings-BovdV4N8.js → agent-profiles-settings-3mmXljZX.js} +1 -1
- package/build/assets/{agent-server-conversation-service.api-BP73yONA.js → agent-server-conversation-service.api-CuSdr4Qb.js} +4 -4
- package/build/assets/{agent-settings-Ofbh6UTD.js → agent-settings-B-W10B4E.js} +1 -1
- package/build/assets/agent-settings-D_yOy8XB.js +1 -0
- package/build/assets/{alert-banner-BW0wRYw6.js → alert-banner-Bpf2NQJq.js} +1 -1
- package/build/assets/{api-key-entry-screen-CZIdRmUJ.js → api-key-entry-screen-Dl0qVYi4.js} +1 -1
- package/build/assets/{app-settings-BHJ9nKti.js → app-settings-yVizGXox.js} +1 -1
- package/build/assets/automation-detail-dvJfLs5u.js +2 -0
- package/build/assets/automation-export-BPcgLqPD.js +2 -0
- package/build/assets/automations-list-CUXogTe0.js +1 -0
- package/build/assets/{back-nav-button-BYmxFxGt.js → back-nav-button-CUS2mi-U.js} +1 -1
- package/build/assets/backend-form-modal-CQJGmItq.js +1 -0
- package/build/assets/{backend-synced-settings-badge-Bs3FSXsS.js → backend-synced-settings-badge-D9hZx05w.js} +1 -1
- package/build/assets/{base-modal-CC5Pp8CF.js → base-modal-BuF2f0YL.js} +1 -1
- package/build/assets/{brand-button-DKPMEKcW.js → brand-button-vMVuyDXu.js} +1 -1
- package/build/assets/{browser-B39N74jU.js → browser-BifzWIBd.js} +1 -1
- package/build/assets/{browser-tab-aiDg7aEn.js → browser-tab-CkkqJnqw.js} +1 -1
- package/build/assets/{chat-send-button-DxuZIoW8.js → chat-send-button-B_jc5z5C.js} +1 -1
- package/build/assets/{circle-plus-check-toggle-T3eAIuPQ.js → circle-plus-check-toggle-C5YxjxvI.js} +1 -1
- package/build/assets/{combobox-caret-CLGvOs32.js → combobox-caret-DoObzqf4.js} +1 -1
- package/build/assets/{command-menu-DKrLCRla.js → command-menu-D-JV3Wqm.js} +1 -1
- package/build/assets/{condenser-settings-D_5vY4m3.js → condenser-settings-Ca8fYZ_Z.js} +1 -1
- package/build/assets/{confirmation-modal-BK1F0A68.js → confirmation-modal-BGLu6XJN.js} +1 -1
- package/build/assets/{context-menu-list-item-DBFzplWW.js → context-menu-list-item-BD5_EaKN.js} +1 -1
- package/build/assets/conversation-9NBSRoR9.js +1 -0
- package/build/assets/conversation-BXg6Oti2.js +89 -0
- package/build/assets/conversation-panel-CZri9Rup.js +1 -0
- package/build/assets/{conversation-service.api-CqZcHr1c.js → conversation-service.api-DPBQvTKD.js} +1 -1
- package/build/assets/{conversation-tab-empty-state-m8IUkfMj.js → conversation-tab-empty-state-B2dDYEwf.js} +1 -1
- package/build/assets/{copy-to-clipboard-button-Aj9wFmt2.js → copy-to-clipboard-button-CaR52OnA.js} +1 -1
- package/build/assets/declaration-gq8V5eTU.js +1 -0
- package/build/assets/{device-verify-B5YCrvSu.js → device-verify-Dj6I4Iqe.js} +1 -1
- package/build/assets/{dropdown-classes-CgQ45JmQ.js → dropdown-classes-DJSXeLqc.js} +1 -1
- package/build/assets/{ellipsis-button-1BV9Sga8.js → ellipsis-button-C5Z-tPFp.js} +1 -1
- package/build/assets/{entry.client-Dy--3GeA.js → entry.client-2_3ePAm0.js} +2 -2
- package/build/assets/{enum-filter-dropdown-DCITiYVi.js → enum-filter-dropdown-DXbVwiNZ.js} +1 -1
- package/build/assets/{environment-switch-overlay-DUnKILNH.js → environment-switch-overlay-DZQfRGTx.js} +1 -1
- package/build/assets/{extensions-hub-CksKNpP9.js → extensions-hub-CLjmOGqS.js} +1 -1
- package/build/assets/{extensions-navigation-Di9rJ-b5.js → extensions-navigation-B5MeUiMq.js} +1 -1
- package/build/assets/files-tab-UVoray88.js +1 -0
- package/build/assets/{form-control-classes-9rkU4usZ.js → form-control-classes-ChpNBe9n.js} +1 -1
- package/build/assets/{git-provider-icon-aQRcUVI-.js → git-provider-icon-o6hdnJUB.js} +1 -1
- package/build/assets/home-D1IHvGTN.js +1 -0
- package/build/assets/{install-server-modal-BRab9inF.js → install-server-modal-BbuQaA2w.js} +1 -1
- package/build/assets/{launch-1TJRKkkX.js → launch-V_3Rp7WS.js} +1 -1
- package/build/assets/lesson-plan-jC0HBoe6.js +4 -0
- package/build/assets/{llm-not-configured-banner-BrU5uHto.js → llm-not-configured-banner-DoARbcdL.js} +4 -4
- package/build/assets/{llm-settings-BpdU51Jo.js → llm-settings-4JMLV8mq.js} +1 -1
- package/build/assets/llm-settings-D91gMw3U.js +1 -0
- package/build/assets/{loading-spinner-DHAIPZkP.js → loading-spinner-BLdswi6q.js} +1 -1
- package/build/assets/{manage-backends-modal-BREySP-J.js → manage-backends-modal-DkWAMSle.js} +1 -1
- package/build/assets/manifest-aa5c29f6.js +1 -0
- package/build/assets/{markdown-renderer-D8mayGjI.js → markdown-renderer-DMT7Zkpd.js} +1 -1
- package/build/assets/{mcp-XXf8k6mb.js → mcp-BQufNPZ6.js} +1 -1
- package/build/assets/{messages-D2oZb47Z.js → messages-BnJCqnFf.js} +11 -11
- package/build/assets/{modal-body-Dcu5McCa.js → modal-body-DouOE5KH.js} +1 -1
- package/build/assets/{modal-classes-DPqxSyuP.js → modal-classes-Cf-4tbHz.js} +1 -1
- package/build/assets/{modal-close-button-CTCX4nCH.js → modal-close-button-B6CfxxJj.js} +1 -1
- package/build/assets/{model-selector-VDHBytb6.js → model-selector-BfqpMkSJ.js} +1 -1
- package/build/assets/onboarding-Bu-HN1ze.js +1 -0
- package/build/assets/{onboarding-modal-C084vkv7.js → onboarding-modal-CZgjs5Bx.js} +1 -1
- package/build/assets/{option-service.api-Cl5VLGyw.js → option-service.api-BrZ5q87N.js} +1 -1
- package/build/assets/{organization-service.api-CnynNoT-.js → organization-service.api-Bk4pCGU1.js} +1 -1
- package/build/assets/{path-utils-BeOmjIcE.js → path-utils-CSmuZoix.js} +1 -1
- package/build/assets/{plan-components-j9X4AxYM.js → plan-components-CcCBmCzW.js} +1 -1
- package/build/assets/{planner-tab-AZMjJx31.js → planner-tab-Dhyqcqmb.js} +1 -1
- package/build/assets/{plugins-management-service-BAp70KN7.js → plugins-management-service-BRBLxvUl.js} +1 -1
- package/build/assets/{profiles-service.api-CeYEmSM3.js → profiles-service.api-BMXQ9pZC.js} +1 -1
- package/build/assets/providers-BpO967xS.js +1 -0
- package/build/assets/{proxy-CHmFkV16.js → proxy-BgJLtxu7.js} +1 -1
- package/build/assets/{recommended-automations-launcher-Bk8tQ-Cn.js → recommended-automations-launcher-CTcGTl4d.js} +1 -1
- package/build/assets/root-BM5WgQ4_.js +2 -0
- package/build/assets/root-V8fkplMy.css +1 -0
- package/build/assets/root-layout-BdoyoobY.js +2 -0
- package/build/assets/{runtime-waiting-state-DkzLgvMn.js → runtime-waiting-state-BsXLElvN.js} +1 -1
- package/build/assets/{schema-field-nvq_Juo2.js → schema-field-BSHrUMme.js} +1 -1
- package/build/assets/{sdk-section-page-CgT70FaZ.js → sdk-section-page-t82gsOqa.js} +1 -1
- package/build/assets/{sdk-settings-schema-BjBkMHzZ.js → sdk-settings-schema-Cv7M9eSe.js} +1 -1
- package/build/assets/{secrets-service-CZYjNxFw.js → secrets-service-B2GqPV0N.js} +1 -1
- package/build/assets/{secrets-settings-BCxk9p9s.js → secrets-settings-Cx6yJlyb.js} +1 -1
- package/build/assets/{segmented-toggle-Cen5AMUc.js → segmented-toggle-DyQqPhoD.js} +1 -1
- package/build/assets/{settings-CedTdtb5.js → settings-DmPLkgq0.js} +1 -1
- package/build/assets/{settings-dropdown-input-xzoZwgUe.js → settings-dropdown-input-BjunGXJb.js} +1 -1
- package/build/assets/{settings-index-Cct3PJSP.js → settings-index-D6sxWSRk.js} +1 -1
- package/build/assets/{settings-input-DYs7t3oK.js → settings-input-bD6npsDm.js} +1 -1
- package/build/assets/{settings-list-classes-DvQr5jVS.js → settings-list-classes-Dp_OgzN0.js} +1 -1
- package/build/assets/{settings-modal-DwLybV_O.js → settings-modal-CwrZKrTq.js} +1 -1
- package/build/assets/settings-service.api-Ce-ub11I.js +1 -0
- package/build/assets/{settings-switch-C2N2xqEb.js → settings-switch-5cuxl8Dc.js} +1 -1
- package/build/assets/{shared-conversation-DnDwfLgg.js → shared-conversation-BDREQPnE.js} +1 -1
- package/build/assets/{sidebar-layout-BtpUSN3j.js → sidebar-layout-DuzJMMw-.js} +1 -1
- package/build/assets/{sidebar-mobile-menu-toggle-B8k3qu84.js → sidebar-mobile-menu-toggle-VvvX32iF.js} +1 -1
- package/build/assets/{sidebar-nav-link-CIB3NBRW.js → sidebar-nav-link-5AlTT5-j.js} +1 -1
- package/build/assets/{skill-card-pill-row-D2bUe2kB.js → skill-card-pill-row-eTIkE4nz.js} +1 -1
- package/build/assets/{skills-plugins-B68ay6Pu.js → skills-plugins-2Y44YHbO.js} +1 -1
- package/build/assets/{skills-settings-A6sUy0T7.js → skills-settings-BUv04tSc.js} +1 -1
- package/build/assets/{styled-tooltip-DsKsFjuq.js → styled-tooltip-CgX2oRaK.js} +1 -1
- package/build/assets/{suspense-X5dBsJzn.js → suspense-BO1Tl_Vf.js} +1 -1
- package/build/assets/{task-list-tab-BAThr9Ex.js → task-list-tab-0k9PhuqO.js} +1 -1
- package/build/assets/{terminal-BmT0_gIH.js → terminal-B61WrJdS.js} +1 -1
- package/build/assets/{toggle-switch-DBlOOzBr.js → toggle-switch-2j5wwR85.js} +1 -1
- package/build/assets/{typography-YGPBGzOG.js → typography-D8mHDeNG.js} +1 -1
- package/build/assets/{use-acp-credential-form-Bs6s0DTU.js → use-acp-credential-form-MzZM6D7D.js} +1 -1
- package/build/assets/{use-activate-agent-profile-CKtTToSx.js → use-activate-agent-profile-20v1xU0H.js} +1 -1
- package/build/assets/{use-active-agent-profile-CWLPpOtK.js → use-active-agent-profile-BkJN7Xsy.js} +1 -1
- package/build/assets/{use-active-conversation-DI5S5M1w.js → use-active-conversation-DOZVSVtU.js} +1 -1
- package/build/assets/{use-agent-profiles-DPcaYLpm.js → use-agent-profiles-DwZxnwO5.js} +1 -1
- package/build/assets/{use-agent-state-Buh3bQDh.js → use-agent-state-DHZO1obC.js} +1 -1
- package/build/assets/{use-backends-health-CxkvJy2N.js → use-backends-health-CRHzuvxO.js} +1 -1
- package/build/assets/{use-can-manage-org-profiles-DeX2Mfje.js → use-can-manage-org-profiles-C-OhWVBS.js} +1 -1
- package/build/assets/{use-cloud-current-user-id-D_PQGfmT.js → use-cloud-current-user-id-utFreJ0i.js} +1 -1
- package/build/assets/use-config-mJrtZHP1.js +1 -0
- package/build/assets/{use-create-conversation-D9OWYcte.js → use-create-conversation-DdqsP78F.js} +1 -1
- package/build/assets/{use-create-secret-CVFSlhlR.js → use-create-secret-D0HCOZFu.js} +1 -1
- package/build/assets/{use-event-store-YSOxy9FC.js → use-event-store-DjgU4wfO.js} +1 -1
- package/build/assets/{use-handle-plan-click-Dug8egQl.js → use-handle-plan-click-DyRASKMC.js} +1 -1
- package/build/assets/use-is-authed-B6M0mcGL.js +1 -0
- package/build/assets/use-llm-profiles-RC-PkpBl.js +1 -0
- package/build/assets/{use-plugins-marketplace-BGb-KgfD.js → use-plugins-marketplace-FHlbeQ4c.js} +1 -1
- package/build/assets/use-runtime-is-ready-RKj2WmEh.js +1 -0
- package/build/assets/use-save-agent-profile-tvDP15mh.js +1 -0
- package/build/assets/{use-save-settings-CChDyjOB.js → use-save-settings-BGlSfuYn.js} +1 -1
- package/build/assets/{use-settings-dX4fKUPF.js → use-settings-CH5LzXD8.js} +1 -1
- package/build/assets/{use-settings-nav-items-D6pHnvxg.js → use-settings-nav-items-Dwktv8wo.js} +1 -1
- package/build/assets/{use-skills-Dqo-pDDx.js → use-skills-CSzSL6HG.js} +1 -1
- package/build/assets/{use-task-list-CmWGElgb.js → use-task-list-AVBLbbcR.js} +1 -1
- package/build/assets/{use-tracking-GtXCHmTv.js → use-tracking-BIXzagSI.js} +1 -1
- package/build/assets/{use-user-conversation-6uOXFP95.js → use-user-conversation-zpx7I18x.js} +1 -1
- package/build/assets/{useMutation-DCE9RcgK.js → useMutation-DrCAVg5w.js} +1 -1
- package/build/assets/{useMutationState-BUab8Kxr.js → useMutationState-D8QN0ZCy.js} +1 -1
- package/build/assets/{useQueries-CRzaAqzH.js → useQueries-DClgUTc7.js} +1 -1
- package/build/assets/{useQuery-CpjHYtZc.js → useQuery-CRBP8vIS.js} +1 -1
- package/build/assets/{useTranslation-Cgy4JA_g.js → useTranslation-CDRCKC9-.js} +1 -1
- package/build/assets/{utils-CE0lDWtb.js → utils-D3U3rlqr.js} +1 -1
- package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~d1e18dtv-5Q6lQJdE.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~nfpll1wp-BCpa95De.js} +1 -1
- package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~nn5zty47-Zl0G1QCy.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~jewnulst-C57fm7C-.js} +1 -1
- package/build/assets/{vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~enjl5xdm-mMKDm94D.js → vendor~root-layout~home~conversation-panel~conversation~launch~settings~settings-index~agen~enjl5xdm-B0EIDu4M.js} +1 -1
- package/build/assets/{verification-settings-D_zsNB_c.js → verification-settings-D576N2xa.js} +1 -1
- package/build/index.html +4 -4
- package/build/locales/ar/openhands.json +24 -0
- package/build/locales/ca/openhands.json +24 -0
- package/build/locales/de/openhands.json +24 -0
- package/build/locales/en/openhands.json +25 -1
- package/build/locales/es/openhands.json +24 -0
- package/build/locales/fr/openhands.json +24 -0
- package/build/locales/it/openhands.json +24 -0
- package/build/locales/ja/openhands.json +24 -0
- package/build/locales/ko-KR/openhands.json +24 -0
- package/build/locales/no/openhands.json +24 -0
- package/build/locales/pt/openhands.json +24 -0
- package/build/locales/tr/openhands.json +24 -0
- package/build/locales/uk/openhands.json +24 -0
- package/build/locales/zh-CN/openhands.json +24 -0
- package/build/locales/zh-TW/openhands.json +24 -0
- package/config/defaults.json +4 -4
- package/dist/api/automation-service/automation-service.api.d.ts +2 -1
- package/dist/api/event-service/event-service.api.cjs +1 -1
- package/dist/api/event-service/event-service.api.cjs.map +1 -1
- package/dist/api/event-service/event-service.api.js +1 -1
- package/dist/api/event-service/event-service.api.js.map +1 -1
- package/dist/api/event-service/event-service.types.d.ts +6 -0
- package/dist/api/git-service/agent-server-git-service.api.cjs +1 -1
- package/dist/api/git-service/agent-server-git-service.api.cjs.map +1 -1
- package/dist/api/git-service/agent-server-git-service.api.js +2 -2
- package/dist/api/git-service/agent-server-git-service.api.js.map +1 -1
- package/dist/api/git-service/git-service.api.cjs +1 -1
- package/dist/api/git-service/git-service.api.cjs.map +1 -1
- package/dist/api/git-service/git-service.api.js +2 -2
- package/dist/api/git-service/git-service.api.js.map +1 -1
- package/dist/components/conversation-events/chat/event-thought-helpers.cjs +1 -1
- package/dist/components/conversation-events/chat/event-thought-helpers.js +1 -1
- package/dist/components/features/automations/automation-card.d.ts +2 -1
- package/dist/components/features/automations/automation-group.d.ts +2 -1
- package/dist/components/features/automations/automation-list-row.d.ts +2 -1
- package/dist/components/features/automations/build-automation-menu-items.d.ts +2 -1
- package/dist/components/features/automations/detail/detail-header.d.ts +3 -1
- package/dist/components/features/automations/import-automation-modal.d.ts +10 -0
- package/dist/components/features/backends/backend-form-modal.cjs +1 -1
- package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
- package/dist/components/features/backends/backend-form-modal.d.ts +3 -1
- package/dist/components/features/backends/backend-form-modal.js +17 -16
- package/dist/components/features/backends/backend-form-modal.js.map +1 -1
- package/dist/components/features/conversation/conversation-name-context-menu.cjs +1 -1
- package/dist/components/features/conversation/conversation-name-context-menu.cjs.map +1 -1
- package/dist/components/features/conversation/conversation-name-context-menu.d.ts +2 -1
- package/dist/components/features/conversation/conversation-name-context-menu.js +74 -63
- package/dist/components/features/conversation/conversation-name-context-menu.js.map +1 -1
- package/dist/components/features/conversation/conversation-name.cjs +1 -1
- package/dist/components/features/conversation/conversation-name.cjs.map +1 -1
- package/dist/components/features/conversation/conversation-name.js +73 -61
- package/dist/components/features/conversation/conversation-name.js.map +1 -1
- package/dist/components/features/conversation/transcript-export-modal.cjs +2 -0
- package/dist/components/features/conversation/transcript-export-modal.cjs.map +1 -0
- package/dist/components/features/conversation/transcript-export-modal.d.ts +10 -0
- package/dist/components/features/conversation/transcript-export-modal.js +140 -0
- package/dist/components/features/conversation/transcript-export-modal.js.map +1 -0
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +1 -1
- package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
- package/dist/components/features/home/shared/dropdown-item.cjs +1 -1
- package/dist/components/features/home/shared/dropdown-item.cjs.map +1 -1
- package/dist/components/features/home/shared/dropdown-item.d.ts +7 -1
- package/dist/components/features/home/shared/dropdown-item.js +2 -1
- package/dist/components/features/home/shared/dropdown-item.js.map +1 -1
- package/dist/components/features/home/shared/generic-dropdown-menu.cjs +1 -1
- package/dist/components/features/home/shared/generic-dropdown-menu.cjs.map +1 -1
- package/dist/components/features/home/shared/generic-dropdown-menu.d.ts +8 -1
- package/dist/components/features/home/shared/generic-dropdown-menu.js +18 -10
- package/dist/components/features/home/shared/generic-dropdown-menu.js.map +1 -1
- package/dist/components/features/home/workspace-dropdown/workspace-dropdown.d.ts +11 -2
- package/dist/config/defaults.cjs +1 -1
- package/dist/config/defaults.cjs.map +1 -1
- package/dist/config/defaults.js +4 -4
- package/dist/config/defaults.js.map +1 -1
- package/dist/hooks/query/use-automations.d.ts +2 -1
- package/dist/hooks/query/use-resolved-workspaces.cjs +1 -1
- package/dist/hooks/query/use-resolved-workspaces.cjs.map +1 -1
- package/dist/hooks/query/use-resolved-workspaces.d.ts +9 -1
- package/dist/hooks/query/use-resolved-workspaces.js +1 -0
- package/dist/hooks/query/use-resolved-workspaces.js.map +1 -1
- package/dist/hooks/query/use-workspace-file-content.cjs +1 -1
- package/dist/hooks/query/use-workspace-file-content.cjs.map +1 -1
- package/dist/hooks/query/use-workspace-file-content.js +61 -55
- package/dist/hooks/query/use-workspace-file-content.js.map +1 -1
- package/dist/hooks/query/use-workspace-files.cjs +1 -1
- package/dist/hooks/query/use-workspace-files.cjs.map +1 -1
- package/dist/hooks/query/use-workspace-files.d.ts +11 -4
- package/dist/hooks/query/use-workspace-files.js +39 -18
- package/dist/hooks/query/use-workspace-files.js.map +1 -1
- package/dist/hooks/use-auto-refresh-files-on-edit.cjs +1 -1
- package/dist/hooks/use-auto-refresh-files-on-edit.cjs.map +1 -1
- package/dist/hooks/use-auto-refresh-files-on-edit.d.ts +9 -0
- package/dist/hooks/use-auto-refresh-files-on-edit.js +15 -11
- package/dist/hooks/use-auto-refresh-files-on-edit.js.map +1 -1
- package/dist/hooks/use-tracking.cjs +1 -1
- package/dist/hooks/use-tracking.cjs.map +1 -1
- package/dist/hooks/use-tracking.d.ts +7 -0
- package/dist/hooks/use-tracking.js +9 -0
- package/dist/hooks/use-tracking.js.map +1 -1
- package/dist/i18n/declaration.cjs +1 -1
- package/dist/i18n/declaration.cjs.map +1 -1
- package/dist/i18n/declaration.d.ts +24 -0
- package/dist/i18n/declaration.js +1 -1
- package/dist/i18n/declaration.js.map +1 -1
- package/dist/i18n/translation.cjs +2 -2
- package/dist/i18n/translation.cjs.map +1 -1
- package/dist/i18n/translation.js +409 -1
- package/dist/i18n/translation.js.map +1 -1
- package/dist/locales/ar/openhands.json +24 -0
- package/dist/locales/ca/openhands.json +24 -0
- package/dist/locales/de/openhands.json +24 -0
- package/dist/locales/en/openhands.json +25 -1
- package/dist/locales/es/openhands.json +24 -0
- package/dist/locales/fr/openhands.json +24 -0
- package/dist/locales/it/openhands.json +24 -0
- package/dist/locales/ja/openhands.json +24 -0
- package/dist/locales/ko-KR/openhands.json +24 -0
- package/dist/locales/no/openhands.json +24 -0
- package/dist/locales/pt/openhands.json +24 -0
- package/dist/locales/tr/openhands.json +24 -0
- package/dist/locales/uk/openhands.json +24 -0
- package/dist/locales/zh-CN/openhands.json +24 -0
- package/dist/locales/zh-TW/openhands.json +24 -0
- package/dist/package.cjs +1 -1
- package/dist/package.cjs.map +1 -1
- package/dist/package.js +1 -1
- package/dist/package.js.map +1 -1
- package/dist/routes/files-tab.cjs +1 -1
- package/dist/routes/files-tab.js +10 -10
- package/dist/types/automation.d.ts +8 -0
- package/dist/utils/custom-toast-handlers.cjs.map +1 -1
- package/dist/utils/custom-toast-handlers.d.ts +1 -0
- package/dist/utils/custom-toast-handlers.js.map +1 -1
- package/dist/utils/mcp-server-name.cjs +1 -1
- package/dist/utils/mcp-server-name.cjs.map +1 -1
- package/dist/utils/mcp-server-name.js +2 -2
- package/dist/utils/mcp-server-name.js.map +1 -1
- package/dist/utils/transcript-export/index.cjs +72 -0
- package/dist/utils/transcript-export/index.cjs.map +1 -0
- package/dist/utils/transcript-export/index.d.ts +10 -0
- package/dist/utils/transcript-export/index.js +302 -0
- package/dist/utils/transcript-export/index.js.map +1 -0
- package/dist/utils/transcript-export/load-complete-events.cjs +2 -0
- package/dist/utils/transcript-export/load-complete-events.cjs.map +1 -0
- package/dist/utils/transcript-export/load-complete-events.d.ts +22 -0
- package/dist/utils/transcript-export/load-complete-events.js +39 -0
- package/dist/utils/transcript-export/load-complete-events.js.map +1 -0
- package/package.json +1 -1
- package/scripts/check-sdk-version-sync.mjs +6 -6
- package/scripts/dev-safe.mjs +2 -2
- package/build/assets/active-backend-context-u2tdYRgH.js +0 -1
- package/build/assets/agent-settings-FGL3Yvkp.js +0 -1
- package/build/assets/automation-detail-DwYGzLkp.js +0 -2
- package/build/assets/automations-list-CuAfJmMW.js +0 -1
- package/build/assets/backend-form-modal-BiaOB2C4.js +0 -1
- package/build/assets/conversation-DnVC6vRV.js +0 -1
- package/build/assets/conversation-panel-ZEz-z3az.js +0 -1
- package/build/assets/conversation-xkBS3AHC.js +0 -19
- package/build/assets/declaration-B9kpCaYl.js +0 -1
- package/build/assets/edit-automation-modal-B6U96gVV.js +0 -1
- package/build/assets/files-tab-61Vaihc6.js +0 -1
- package/build/assets/home-5gOtUe-n.js +0 -1
- package/build/assets/lesson-plan-BtrZUCuj.js +0 -4
- package/build/assets/llm-settings-DsInf6yD.js +0 -1
- package/build/assets/manifest-a5bb7568.js +0 -1
- package/build/assets/onboarding-D32kiGME.js +0 -1
- package/build/assets/providers-vX9jk9Vy.js +0 -1
- package/build/assets/root-CxWZUXTQ.css +0 -1
- package/build/assets/root-layout-CoWw4kpz.js +0 -2
- package/build/assets/root-tuEPDx-i.js +0 -2
- package/build/assets/settings-service.api-DldkFI1x.js +0 -1
- package/build/assets/use-config-ur0RbjCP.js +0 -1
- package/build/assets/use-is-authed-_dhQ2Xuj.js +0 -1
- package/build/assets/use-llm-profiles-DjackuP1.js +0 -1
- package/build/assets/use-runtime-is-ready-Diiq65Q-.js +0 -1
- package/build/assets/use-save-agent-profile-aKKLqDJM.js +0 -1
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~i3v2mlbw-BrJQ3XZH.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~epa72zsm-B4rXs7Zd.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~ncdxw0bh-ClHnt5lR.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~extensions~lf310vj7-CSVPGId3.js} +0 -0
- /package/build/assets/{vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~k6tszsdc-Bb-xMtbv.js → vendor~entry.client~root~root-layout~home~conversation-panel~conversation~launch~skills-set~ivvrgjsa-BlIZPCXX.js} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../../src/utils/transcript-export/index.ts"],"sourcesContent":["import type {\n ActionEvent,\n OpenHandsEvent,\n ObservationEvent,\n} from \"#/types/agent-server/core\";\nimport type { TaskAction } from \"#/types/agent-server/core/base/action\";\nimport type { TaskObservation } from \"#/types/agent-server/core/base/observation\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isGoalConversationStateUpdateEvent,\n isHookExecutionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n isSwitchLLMObservationEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\nimport { shouldRenderEvent } from \"#/components/conversation-events/chat/event-content-helpers/should-render-event\";\nimport { parseMessageFromEvent } from \"#/components/conversation-events/chat/event-content-helpers/parse-message-from-event\";\nimport { getActionContent } from \"#/components/conversation-events/chat/event-content-helpers/get-action-content\";\nimport { getObservationContent } from \"#/components/conversation-events/chat/event-content-helpers/get-observation-content\";\nimport {\n getACPToolCallContent,\n stripRedundantTitlePrefix,\n} from \"#/components/conversation-events/chat/event-content-helpers/get-acp-tool-call-content\";\nimport { groupEvents } from \"#/components/conversation-events/chat/group-events\";\nimport {\n getActionThoughtText,\n getReasoningContent,\n splitInlineThink,\n} from \"#/components/conversation-events/chat/event-thought-helpers\";\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\n\nexport type TranscriptExportFormat = \"markdown\" | \"html\";\n\nexport interface TranscriptExportOptions {\n includeToolDetails: boolean;\n includeTimestamps: boolean;\n title?: string | null;\n model?: string | null;\n}\n\ntype TranscriptEntry =\n | {\n kind: \"message\";\n author: \"user\" | \"assistant\";\n content: string;\n timestamp: string;\n }\n | {\n kind: \"tool\";\n summary: string;\n details: string;\n timestamp: string;\n }\n | {\n kind: \"error\";\n content: string;\n timestamp: string;\n }\n | {\n kind: \"note\";\n summary: string;\n content: string;\n timestamp: string;\n };\n\nconst SAFE_ACTION_DETAIL_KINDS = new Set([\n \"BrowserClickAction\",\n \"BrowserCloseTabAction\",\n \"BrowserGetContentAction\",\n \"BrowserGetStateAction\",\n \"BrowserGoBackAction\",\n \"BrowserListTabsAction\",\n \"BrowserNavigateAction\",\n \"BrowserScrollAction\",\n \"BrowserSwitchTabAction\",\n \"BrowserTypeAction\",\n \"ExecuteBashAction\",\n \"FileEditorAction\",\n \"GlobAction\",\n \"GrepAction\",\n \"InvokeSkillAction\",\n \"MCPToolAction\",\n \"StrReplaceEditorAction\",\n \"TaskAction\",\n \"TaskTrackerAction\",\n \"TerminalAction\",\n \"ThinkAction\",\n]);\n\nconst SAFE_OBSERVATION_DETAIL_KINDS = new Set([\n \"BrowserObservation\",\n \"CanvasUIObservation\",\n \"ExecuteBashObservation\",\n \"FileEditorObservation\",\n \"GlobObservation\",\n \"GrepObservation\",\n \"InvokeSkillObservation\",\n \"MCPToolObservation\",\n \"StrReplaceEditorObservation\",\n \"SwitchLLMObservation\",\n \"TaskTrackerObservation\",\n \"TaskObservation\",\n \"TerminalObservation\",\n]);\n\nconst cleanInlineText = (value: string): string =>\n value\n .replace(/[\\r\\n]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\nconst truncate = (value: string, maxLength = 100): string =>\n value.length > maxLength ? `${value.slice(0, maxLength)}…` : value;\n\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return Number.isNaN(date.getTime()) ? timestamp : date.toISOString();\n};\n\nconst escapeHtml = (value: string): string =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n\n/**\n * GitHub Markdown permits raw HTML, so dynamic conversation text needs the\n * same inert representation as the chat's sanitized Markdown renderer.\n */\nconst sanitizeMarkdownText = (value: string): string =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replace(\n /(!?)\\[([^\\]]*)\\]\\(\\s*((?:javascript|data|vbscript):[^)]*)\\)/gi,\n \"$1\\\\[$2\\\\]($3)\",\n )\n .replace(\n /^(\\s{0,3})\\[([^\\]]+)\\]:(\\s*<?(?:javascript|data|vbscript):)/gim,\n \"$1\\\\[$2\\\\]:$3\",\n );\n\nconst safeTitle = (title?: string | null): string =>\n cleanInlineText(title || i18n.t(I18nKey.TRANSCRIPT_EXPORT$DEFAULT_TITLE)) ||\n i18n.t(I18nKey.TRANSCRIPT_EXPORT$DEFAULT_TITLE);\n\nconst translatePlain = (\n key: I18nKey,\n values?: Record<string, unknown>,\n): string =>\n cleanInlineText(i18n.t(key, values).replace(/<\\/?(?:cmd|path)>/g, \"\"));\n\nconst isServerFallbackSummary = (summary: string): boolean =>\n /^[a-z][a-z0-9_]*\\s*:\\s*[[{]/i.test(summary);\n\nconst humanizeEventKind = (kind: string): string =>\n kind\n .replace(/(?:Action|Observation)$/, \"\")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim();\n\nconst getActionSummary = (event: ActionEvent): string => {\n const summary = cleanInlineText(event.summary || \"\");\n if (summary && !isServerFallbackSummary(summary)) return summary;\n return (\n cleanInlineText(event.tool_name) || humanizeEventKind(event.action.kind)\n );\n};\n\nconst getObservationSummary = (\n event: ObservationEvent,\n correspondingAction?: ActionEvent,\n): string => {\n if (correspondingAction) return getActionSummary(correspondingAction);\n return (\n cleanInlineText(event.tool_name) ||\n humanizeEventKind(event.observation.kind)\n );\n};\n\nconst getTextContent = (\n content: Array<{ type: string; text?: string }>,\n): string =>\n content\n .filter(\n (item): item is { type: \"text\"; text: string } =>\n item.type === \"text\" && typeof item.text === \"string\",\n )\n .map((item) => item.text)\n .join(\"\\n\");\n\nconst getTaskActionDetails = (action: TaskAction): string =>\n [\n `${i18n.t(I18nKey.TASK$SUBAGENT)}: ${action.subagent_type}`,\n `${i18n.t(I18nKey.TASK$QUERY)}:\\n${action.prompt}`,\n ].join(\"\\n\\n\");\n\nconst getTaskObservationDetails = (\n observation: TaskObservation,\n correspondingAction?: ActionEvent,\n): string => {\n const taskAction =\n correspondingAction?.action.kind === \"TaskAction\"\n ? correspondingAction.action\n : undefined;\n return [\n `${i18n.t(I18nKey.TASK$SUBAGENT)}: ${observation.subagent}`,\n `${i18n.t(I18nKey.TASK$TASK_ID)}: ${observation.task_id}`,\n taskAction ? `${i18n.t(I18nKey.TASK$QUERY)}:\\n${taskAction.prompt}` : \"\",\n `${i18n.t(I18nKey.TASK$RESULT)}:\\n${getTextContent(observation.content)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n};\n\nconst getSafeActionDetails = (event: ActionEvent): string => {\n if (event.action.kind === \"TaskAction\") {\n return getTaskActionDetails(event.action);\n }\n return SAFE_ACTION_DETAIL_KINDS.has(event.action.kind)\n ? getActionContent(event)\n : \"\";\n};\n\nconst getSafeObservationDetails = (\n event: ObservationEvent,\n correspondingAction?: ActionEvent,\n): string => {\n if (event.observation.kind === \"TaskObservation\") {\n return getTaskObservationDetails(event.observation, correspondingAction);\n }\n return SAFE_OBSERVATION_DETAIL_KINDS.has(event.observation.kind)\n ? getObservationContent(event)\n : \"\";\n};\n\nconst getHookDetails = (event: OpenHandsEvent): string => {\n if (!isHookExecutionEvent(event)) return \"\";\n return [event.reason, event.error, event.stdout, event.stderr]\n .filter((value): value is string => Boolean(value?.trim()))\n .join(\"\\n\\n\");\n};\n\nconst getActionNarration = (event: ActionEvent): string =>\n [getReasoningContent(event), getActionThoughtText(event)]\n .map((content) => content.trim())\n .filter(Boolean)\n .join(\"\\n\\n\");\n\nconst canRenderActionNarration = (event: ActionEvent): boolean =>\n ![\"FinishAction\", \"SwitchLLMAction\", \"ThinkAction\"].includes(\n event.action.kind,\n );\n\nconst buildTranscriptEntries = (\n events: OpenHandsEvent[],\n includeToolDetails: boolean,\n): TranscriptEntry[] => {\n const uiEvents = events.reduce<OpenHandsEvent[]>((current, event) => {\n try {\n return handleEventForUI(event, current);\n } catch {\n return current;\n }\n }, []);\n const actionsById = new Map(\n events.filter(isActionEvent).map((event) => [event.id, event]),\n );\n const entries: TranscriptEntry[] = [];\n const emittedNarrationActionIds = new Set<string>();\n const renderableEvents = uiEvents.filter(\n (event) =>\n (isSwitchLLMObservationEvent(event) && !event.observation.is_error) ||\n shouldRenderEvent(event),\n );\n const renderedItems = groupEvents(\n renderableEvents,\n Number.MAX_SAFE_INTEGER,\n events,\n );\n\n const addActionNarration = (action: ActionEvent) => {\n if (\n emittedNarrationActionIds.has(action.id) ||\n !canRenderActionNarration(action)\n ) {\n return;\n }\n emittedNarrationActionIds.add(action.id);\n const content = getActionNarration(action);\n if (content) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: action.timestamp,\n });\n }\n };\n\n for (const item of renderedItems) {\n if (item.kind === \"group\") continue;\n if (item.kind === \"thought\") {\n addActionNarration(item.action);\n continue;\n }\n\n const { event } = item;\n try {\n const narrationAction = isActionEvent(event)\n ? event\n : isObservationEvent(event)\n ? actionsById.get(event.action_id)\n : undefined;\n if (narrationAction) addActionNarration(narrationAction);\n\n if (isSwitchLLMObservationEvent(event) && !event.observation.is_error) {\n entries.push({\n kind: \"note\",\n summary: translatePlain(I18nKey.MODEL$SWITCHED_TO_PROFILE, {\n name: event.observation.profile_name,\n }),\n content: [\n event.observation.active_model\n ? `${i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL)}: ${event.observation.active_model}`\n : \"\",\n event.observation.reason\n ? `${i18n.t(I18nKey.TRANSCRIPT_EXPORT$REASON)}: ${event.observation.reason}`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isMessageEvent(event)) {\n const parsed = parseMessageFromEvent(event).trim();\n if (event.source === \"agent\") {\n const { reasoning, message } = splitInlineThink(parsed);\n [reasoning, message].filter(Boolean).forEach((content) => {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: event.timestamp,\n });\n });\n } else if (parsed) {\n entries.push({\n kind: \"message\",\n author: \"user\",\n content: parsed,\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isStreamingDeltaEvent(event)) {\n const { reasoning: inlineThink, message } = splitInlineThink(\n event.content ?? \"\",\n { streaming: true },\n );\n const reasoningContent = [\n event.reasoning_content?.trim() || \"\",\n inlineThink,\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n if (reasoningContent) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content: reasoningContent,\n timestamp: event.timestamp,\n });\n }\n if (message.trim()) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content: message.trim(),\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isAgentErrorEvent(event)) {\n entries.push({\n kind: \"error\",\n content: event.error,\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isActionEvent(event)) {\n if (event.action.kind === \"FinishAction\") {\n const content = event.action.message.trim();\n if (content) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: event.timestamp,\n });\n }\n } else {\n entries.push({\n kind: \"tool\",\n summary: getActionSummary(event),\n details: includeToolDetails ? getSafeActionDetails(event) : \"\",\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isObservationEvent(event)) {\n const correspondingAction = actionsById.get(event.action_id);\n entries.push({\n kind: \"tool\",\n summary: getObservationSummary(event, correspondingAction),\n details: includeToolDetails\n ? getSafeObservationDetails(event, correspondingAction)\n : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isACPToolCallEvent(event)) {\n entries.push({\n kind: \"tool\",\n summary:\n stripRedundantTitlePrefix(event) ||\n i18n.t(I18nKey.ACTION_MESSAGE$ACP_TOOL),\n details: includeToolDetails ? getACPToolCallContent(event) : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isHookExecutionEvent(event)) {\n entries.push({\n kind: \"tool\",\n summary: i18n.t(I18nKey.TRANSCRIPT_EXPORT$HOOK, {\n command: truncate(cleanInlineText(event.hook_command), 100),\n }),\n details: includeToolDetails ? getHookDetails(event) : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (\n isConversationStateUpdateEvent(event) &&\n isGoalConversationStateUpdateEvent(event)\n ) {\n entries.push({\n kind: \"note\",\n summary: `${i18n.t(I18nKey.GOAL$PREFIX)} ${i18n.t(\n {\n running: I18nKey.GOAL$STATUS_RUNNING,\n complete: I18nKey.GOAL$STATUS_COMPLETE,\n capped: I18nKey.GOAL$STATUS_CAPPED,\n interrupted: I18nKey.GOAL$STATUS_INTERRUPTED,\n }[event.value.status],\n )}`,\n content: [event.value.objective, event.value.verdict?.missing || \"\"]\n .filter(Boolean)\n .join(\"\\n\\n\"),\n timestamp: event.timestamp,\n });\n }\n } catch {\n // Runtime event payloads may be older than the local TypeScript schema.\n // Skip malformed entries instead of aborting an otherwise valid export.\n }\n }\n\n return entries;\n};\n\nconst markdownTimestamp = (\n entry: TranscriptEntry,\n options: TranscriptExportOptions,\n): string =>\n options.includeTimestamps\n ? `<sub>${escapeHtml(formatTimestamp(entry.timestamp))}</sub>\\n\\n`\n : \"\";\n\nconst markdownFence = (content: string): string => {\n const longestRun = Math.max(\n 0,\n ...Array.from(content.matchAll(/`+/g), (match) => match[0].length),\n );\n const fence = \"`\".repeat(Math.max(3, longestRun + 1));\n return `${fence}text\\n${content}\\n${fence}`;\n};\n\nexport const eventsToMarkdown = (\n events: OpenHandsEvent[],\n options: TranscriptExportOptions,\n): string => {\n const lines = [`# ${sanitizeMarkdownText(safeTitle(options.title))}`, \"\"];\n\n if (options.model) {\n lines.push(\n `**${i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL)}:** ${sanitizeMarkdownText(cleanInlineText(options.model))}`,\n \"\",\n );\n }\n\n for (const entry of buildTranscriptEntries(\n events,\n options.includeToolDetails,\n )) {\n const timestamp = markdownTimestamp(entry, options);\n if (entry.kind === \"message\") {\n const author =\n entry.author === \"user\"\n ? i18n.t(I18nKey.TRANSCRIPT_EXPORT$USER)\n : i18n.t(I18nKey.CHAT_INTERFACE$ASSISTANT);\n lines.push(\n `## ${author}`,\n \"\",\n timestamp + sanitizeMarkdownText(entry.content),\n \"\",\n );\n } else if (entry.kind === \"error\") {\n const quoted = entry.content\n .split(\"\\n\")\n .map((line) => `> ${sanitizeMarkdownText(line)}`)\n .join(\"\\n\");\n lines.push(\n `## ${i18n.t(I18nKey.COMMON$ERROR)}`,\n \"\",\n timestamp + quoted,\n \"\",\n );\n } else if (entry.kind === \"note\") {\n lines.push(\n `> **${escapeHtml(entry.summary)}**`,\n \"\",\n timestamp + sanitizeMarkdownText(entry.content),\n \"\",\n );\n } else if (options.includeToolDetails && entry.details) {\n lines.push(\n \"<details>\",\n `<summary><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}</summary>`,\n \"\",\n timestamp + markdownFence(entry.details),\n \"\",\n \"</details>\",\n \"\",\n );\n } else {\n const timestampMarkup = timestamp ? `<br>${timestamp.trimEnd()}` : \"\";\n lines.push(\n `<p><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestampMarkup}</p>`,\n \"\",\n );\n }\n }\n\n return `${lines.join(\"\\n\").trim()}\\n`;\n};\n\nconst htmlTimestamp = (\n entry: TranscriptEntry,\n options: TranscriptExportOptions,\n): string => {\n if (!options.includeTimestamps) return \"\";\n const timestamp = formatTimestamp(entry.timestamp);\n return `<time datetime=\"${escapeHtml(timestamp)}\">${escapeHtml(timestamp)}</time>`;\n};\n\nexport const eventsToHtml = (\n events: OpenHandsEvent[],\n options: TranscriptExportOptions,\n): string => {\n const title = safeTitle(options.title);\n const body = buildTranscriptEntries(events, options.includeToolDetails)\n .map((entry) => {\n const timestamp = htmlTimestamp(entry, options);\n if (entry.kind === \"message\") {\n const author =\n entry.author === \"user\"\n ? i18n.t(I18nKey.TRANSCRIPT_EXPORT$USER)\n : i18n.t(I18nKey.CHAT_INTERFACE$ASSISTANT);\n return `<section class=\"message ${entry.author.toLowerCase()}\">\n <header><h2>${escapeHtml(author)}</h2>${timestamp}</header>\n <div class=\"content\">${escapeHtml(entry.content)}</div>\n</section>`;\n }\n if (entry.kind === \"error\") {\n return `<section class=\"message error\">\n <header><h2>${escapeHtml(i18n.t(I18nKey.COMMON$ERROR))}</h2>${timestamp}</header>\n <div class=\"content\">${escapeHtml(entry.content)}</div>\n</section>`;\n }\n if (entry.kind === \"note\") {\n return `<aside class=\"note\">\n <header><strong>${escapeHtml(entry.summary)}</strong>${timestamp}</header>\n ${entry.content ? `<div class=\"content\">${escapeHtml(entry.content)}</div>` : \"\"}\n</aside>`;\n }\n const details =\n options.includeToolDetails && entry.details\n ? `<details>\n <summary><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestamp}</summary>\n <pre>${escapeHtml(entry.details)}</pre>\n</details>`\n : `<div class=\"tool-summary\"><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestamp}</div>`;\n return details;\n })\n .join(\"\\n\");\n\n const model = options.model\n ? `<p class=\"model\"><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL))}:</strong> ${escapeHtml(cleanInlineText(options.model))}</p>`\n : \"\";\n\n return `<!doctype html>\n<html lang=\"${escapeHtml(i18n.resolvedLanguage || i18n.language || \"en\")}\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data:\">\n <title>${escapeHtml(title)}</title>\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, sans-serif; }\n body { margin: 0; background: #111827; color: #e5e7eb; line-height: 1.55; }\n main { box-sizing: border-box; width: min(860px, 100%); margin: 0 auto; padding: 48px 24px 80px; }\n h1 { margin: 0; font-size: 2rem; }\n h2 { margin: 0; font-size: 1rem; }\n .model { margin: 8px 0 32px; color: #9ca3af; }\n .message, details, .tool-summary, .note { margin: 16px 0; border: 1px solid #374151; border-radius: 10px; padding: 16px; background: #1f2937; }\n .user { border-left: 4px solid #60a5fa; }\n .assistant { border-left: 4px solid #34d399; }\n .error { border-left: 4px solid #f87171; }\n .note { border-left: 4px solid #a78bfa; }\n header, summary, .tool-summary { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }\n summary { cursor: pointer; }\n time { flex: none; color: #9ca3af; font-size: .75rem; font-weight: 400; }\n .content { margin-top: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }\n pre { margin: 14px 0 0; padding: 14px; overflow-x: auto; border-radius: 7px; background: #111827; color: #d1d5db; white-space: pre-wrap; overflow-wrap: anywhere; }\n @media (prefers-color-scheme: light) {\n body { background: #f9fafb; color: #111827; }\n .message, details, .tool-summary, .note { border-color: #d1d5db; background: #fff; }\n pre { background: #f3f4f6; color: #1f2937; }\n .model, time { color: #6b7280; }\n }\n </style>\n</head>\n<body>\n <main>\n <h1>${escapeHtml(title)}</h1>\n ${model}\n ${body}\n </main>\n</body>\n</html>\n`;\n};\n"],"mappings":"83BAuEA,IAAM,EAA2B,IAAI,IAAI,CACvC,qBACA,wBACA,0BACA,wBACA,sBACA,wBACA,wBACA,sBACA,yBACA,oBACA,oBACA,mBACA,aACA,aACA,oBACA,gBACA,yBACA,aACA,oBACA,iBACA,cACD,CAAC,CAEI,EAAgC,IAAI,IAAI,CAC5C,qBACA,sBACA,yBACA,wBACA,kBACA,kBACA,yBACA,qBACA,8BACA,uBACA,yBACA,kBACA,sBACD,CAAC,CAEI,EAAmB,GACvB,EACG,QAAQ,WAAY,IAAI,CACxB,QAAQ,OAAQ,IAAI,CACpB,MAAM,CAEL,GAAY,EAAe,EAAY,MAC3C,EAAM,OAAS,EAAY,GAAG,EAAM,MAAM,EAAG,EAAU,CAAC,GAAK,EAEzD,EAAmB,GAA8B,CACrD,IAAM,EAAO,IAAI,KAAK,EAAU,CAChC,OAAO,OAAO,MAAM,EAAK,SAAS,CAAC,CAAG,EAAY,EAAK,aAAa,EAGhE,EAAc,GAClB,EACG,WAAW,IAAK,QAAQ,CACxB,WAAW,IAAK,OAAO,CACvB,WAAW,IAAK,OAAO,CACvB,WAAW,IAAK,SAAS,CACzB,WAAW,IAAK,SAAS,CAMxB,EAAwB,GAC5B,EACG,WAAW,IAAK,QAAQ,CACxB,WAAW,IAAK,OAAO,CACvB,WAAW,IAAK,OAAO,CACvB,QACC,gEACA,iBACD,CACA,QACC,iEACA,gBACD,CAEC,EAAa,GACjB,EAAgB,GAAS,EAAA,QAAK,EAAE,EAAA,QAAQ,gCAAgC,CAAC,EACzE,EAAA,QAAK,EAAE,EAAA,QAAQ,gCAAgC,CAE3C,GACJ,EACA,IAEA,EAAgB,EAAA,QAAK,EAAE,EAAK,EAAO,CAAC,QAAQ,qBAAsB,GAAG,CAAC,CAElE,EAA2B,GAC/B,+BAA+B,KAAK,EAAQ,CAExC,EAAqB,GACzB,EACG,QAAQ,0BAA2B,GAAG,CACtC,QAAQ,kBAAmB,QAAQ,CACnC,MAAM,CAEL,EAAoB,GAA+B,CACvD,IAAM,EAAU,EAAgB,EAAM,SAAW,GAAG,CAEpD,OADI,GAAW,CAAC,EAAwB,EAAQ,CAAS,EAEvD,EAAgB,EAAM,UAAU,EAAI,EAAkB,EAAM,OAAO,KAAK,EAItE,GACJ,EACA,IAEI,EAA4B,EAAiB,EAAoB,CAEnE,EAAgB,EAAM,UAAU,EAChC,EAAkB,EAAM,YAAY,KAAK,CAIvC,EACJ,GAEA,EACG,OACE,GACC,EAAK,OAAS,QAAU,OAAO,EAAK,MAAS,SAChD,CACA,IAAK,GAAS,EAAK,KAAK,CACxB,KAAK;EAAK,CAET,EAAwB,GAC5B,CACE,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,cAAc,CAAC,IAAI,EAAO,gBAC5C,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,WAAW,CAAC,KAAK,EAAO,SAC3C,CAAC,KAAK;;EAAO,CAEV,GACJ,EACA,IACW,CACX,IAAM,EACJ,GAAqB,OAAO,OAAS,aACjC,EAAoB,OACpB,IAAA,GACN,MAAO,CACL,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,cAAc,CAAC,IAAI,EAAY,WACjD,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,aAAa,CAAC,IAAI,EAAY,UAChD,EAAa,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,WAAW,CAAC,KAAK,EAAW,SAAW,GACtE,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,YAAY,CAAC,KAAK,EAAe,EAAY,QAAQ,GACxE,CACE,OAAO,QAAQ,CACf,KAAK;;EAAO,EAGX,EAAwB,GACxB,EAAM,OAAO,OAAS,aACjB,EAAqB,EAAM,OAAO,CAEpC,EAAyB,IAAI,EAAM,OAAO,KAAK,CAClD,EAAA,iBAAiB,EAAM,CACvB,GAGA,GACJ,EACA,IAEI,EAAM,YAAY,OAAS,kBACtB,EAA0B,EAAM,YAAa,EAAoB,CAEnE,EAA8B,IAAI,EAAM,YAAY,KAAK,CAC5D,EAAA,sBAAsB,EAAM,CAC5B,GAGA,EAAkB,GACjB,EAAA,qBAAqB,EAAM,CACzB,CAAC,EAAM,OAAQ,EAAM,MAAO,EAAM,OAAQ,EAAM,OAAO,CAC3D,OAAQ,GAA2B,EAAQ,GAAO,MAAM,CAAE,CAC1D,KAAK;;EAAO,CAH0B,GAMrC,EAAsB,GAC1B,CAAC,EAAA,oBAAoB,EAAM,CAAE,EAAA,qBAAqB,EAAM,CAAC,CACtD,IAAK,GAAY,EAAQ,MAAM,CAAC,CAChC,OAAO,QAAQ,CACf,KAAK;;EAAO,CAEX,EAA4B,GAChC,CAAC,CAAC,eAAgB,kBAAmB,cAAc,CAAC,SAClD,EAAM,OAAO,KACd,CAEG,GACJ,EACA,IACsB,CACtB,IAAM,EAAW,EAAO,QAA0B,EAAS,IAAU,CACnE,GAAI,CACF,OAAO,EAAA,iBAAiB,EAAO,EAAQ,MACjC,CACN,OAAO,IAER,EAAE,CAAC,CACA,EAAc,IAAI,IACtB,EAAO,OAAO,EAAA,cAAc,CAAC,IAAK,GAAU,CAAC,EAAM,GAAI,EAAM,CAAC,CAC/D,CACK,EAA6B,EAAE,CAC/B,EAA4B,IAAI,IAMhC,EAAgB,EAAA,YALG,EAAS,OAC/B,GACE,EAAA,4BAA4B,EAAM,EAAI,CAAC,EAAM,YAAY,UAC1D,EAAA,kBAAkB,EAAM,CAG1B,SAEA,EACD,CAEK,EAAsB,GAAwB,CAClD,GACE,EAA0B,IAAI,EAAO,GAAG,EACxC,CAAC,EAAyB,EAAO,CAEjC,OAEF,EAA0B,IAAI,EAAO,GAAG,CACxC,IAAM,EAAU,EAAmB,EAAO,CACtC,GACF,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,YACR,UACA,UAAW,EAAO,UACnB,CAAC,EAIN,IAAK,IAAM,KAAQ,EAAe,CAChC,GAAI,EAAK,OAAS,QAAS,SAC3B,GAAI,EAAK,OAAS,UAAW,CAC3B,EAAmB,EAAK,OAAO,CAC/B,SAGF,GAAM,CAAE,SAAU,EAClB,GAAI,CACF,IAAM,EAAkB,EAAA,cAAc,EAAM,CACxC,EACA,EAAA,mBAAmB,EAAM,CACvB,EAAY,IAAI,EAAM,UAAU,CAChC,IAAA,GAGN,GAFI,GAAiB,EAAmB,EAAgB,CAEpD,EAAA,4BAA4B,EAAM,EAAI,CAAC,EAAM,YAAY,SAAU,CACrE,EAAQ,KAAK,CACX,KAAM,OACN,QAAS,EAAe,EAAA,QAAQ,0BAA2B,CACzD,KAAM,EAAM,YAAY,aACzB,CAAC,CACF,QAAS,CACP,EAAM,YAAY,aACd,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,wBAAwB,CAAC,IAAI,EAAM,YAAY,eACjE,GACJ,EAAM,YAAY,OACd,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,yBAAyB,CAAC,IAAI,EAAM,YAAY,SAClE,GACL,CACE,OAAO,QAAQ,CACf,KAAK;EAAK,CACb,UAAW,EAAM,UAClB,CAAC,CACF,SAGF,GAAI,EAAA,eAAe,EAAM,CAAE,CACzB,IAAM,EAAS,EAAA,sBAAsB,EAAM,CAAC,MAAM,CAClD,GAAI,EAAM,SAAW,QAAS,CAC5B,GAAM,CAAE,YAAW,WAAY,EAAA,iBAAiB,EAAO,CACvD,CAAC,EAAW,EAAQ,CAAC,OAAO,QAAQ,CAAC,QAAS,GAAY,CACxD,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,YACR,UACA,UAAW,EAAM,UAClB,CAAC,EACF,MACO,GACT,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,OACR,QAAS,EACT,UAAW,EAAM,UAClB,CAAC,CAEJ,SAGF,GAAI,EAAA,sBAAsB,EAAM,CAAE,CAChC,GAAM,CAAE,UAAW,EAAa,WAAY,EAAA,iBAC1C,EAAM,SAAW,GACjB,CAAE,UAAW,GAAM,CACpB,CACK,EAAmB,CACvB,EAAM,mBAAmB,MAAM,EAAI,GACnC,EACD,CACE,OAAO,QAAQ,CACf,KAAK;;EAAO,CACX,GACF,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,YACR,QAAS,EACT,UAAW,EAAM,UAClB,CAAC,CAEA,EAAQ,MAAM,EAChB,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,YACR,QAAS,EAAQ,MAAM,CACvB,UAAW,EAAM,UAClB,CAAC,CAEJ,SAGF,GAAI,EAAA,kBAAkB,EAAM,CAAE,CAC5B,EAAQ,KAAK,CACX,KAAM,QACN,QAAS,EAAM,MACf,UAAW,EAAM,UAClB,CAAC,CACF,SAGF,GAAI,EAAA,cAAc,EAAM,CAAE,CACxB,GAAI,EAAM,OAAO,OAAS,eAAgB,CACxC,IAAM,EAAU,EAAM,OAAO,QAAQ,MAAM,CACvC,GACF,EAAQ,KAAK,CACX,KAAM,UACN,OAAQ,YACR,UACA,UAAW,EAAM,UAClB,CAAC,MAGJ,EAAQ,KAAK,CACX,KAAM,OACN,QAAS,EAAiB,EAAM,CAChC,QAAS,EAAqB,EAAqB,EAAM,CAAG,GAC5D,UAAW,EAAM,UAClB,CAAC,CAEJ,SAGF,GAAI,EAAA,mBAAmB,EAAM,CAAE,CAC7B,IAAM,EAAsB,EAAY,IAAI,EAAM,UAAU,CAC5D,EAAQ,KAAK,CACX,KAAM,OACN,QAAS,EAAsB,EAAO,EAAoB,CAC1D,QAAS,EACL,EAA0B,EAAO,EAAoB,CACrD,GACJ,UAAW,EAAM,UAClB,CAAC,CACF,SAGF,GAAI,EAAA,mBAAmB,EAAM,CAAE,CAC7B,EAAQ,KAAK,CACX,KAAM,OACN,QACE,EAAA,0BAA0B,EAAM,EAChC,EAAA,QAAK,EAAE,EAAA,QAAQ,wBAAwB,CACzC,QAAS,EAAqB,EAAA,sBAAsB,EAAM,CAAG,GAC7D,UAAW,EAAM,UAClB,CAAC,CACF,SAGF,GAAI,EAAA,qBAAqB,EAAM,CAAE,CAC/B,EAAQ,KAAK,CACX,KAAM,OACN,QAAS,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAwB,CAC9C,QAAS,EAAS,EAAgB,EAAM,aAAa,CAAE,IAAI,CAC5D,CAAC,CACF,QAAS,EAAqB,EAAe,EAAM,CAAG,GACtD,UAAW,EAAM,UAClB,CAAC,CACF,SAIA,EAAA,+BAA+B,EAAM,EACrC,EAAA,mCAAmC,EAAM,EAEzC,EAAQ,KAAK,CACX,KAAM,OACN,QAAS,GAAG,EAAA,QAAK,EAAE,EAAA,QAAQ,YAAY,CAAC,GAAG,EAAA,QAAK,EAC9C,CACE,QAAS,EAAA,QAAQ,oBACjB,SAAU,EAAA,QAAQ,qBAClB,OAAQ,EAAA,QAAQ,mBAChB,YAAa,EAAA,QAAQ,wBACtB,CAAC,EAAM,MAAM,QACf,GACD,QAAS,CAAC,EAAM,MAAM,UAAW,EAAM,MAAM,SAAS,SAAW,GAAG,CACjE,OAAO,QAAQ,CACf,KAAK;;EAAO,CACf,UAAW,EAAM,UAClB,CAAC,MAEE,GAMV,OAAO,GAGH,GACJ,EACA,IAEA,EAAQ,kBACJ,QAAQ,EAAW,EAAgB,EAAM,UAAU,CAAC,CAAC,YACrD,GAEA,EAAiB,GAA4B,CACjD,IAAM,EAAa,KAAK,IACtB,EACA,GAAG,MAAM,KAAK,EAAQ,SAAS,MAAM,CAAG,GAAU,EAAM,GAAG,OAAO,CACnE,CACK,EAAQ,IAAI,OAAO,KAAK,IAAI,EAAG,EAAa,EAAE,CAAC,CACrD,MAAO,GAAG,EAAM,QAAQ,EAAQ,IAAI,KAGzB,GACX,EACA,IACW,CACX,IAAM,EAAQ,CAAC,KAAK,EAAqB,EAAU,EAAQ,MAAM,CAAC,GAAI,GAAG,CAErE,EAAQ,OACV,EAAM,KACJ,KAAK,EAAA,QAAK,EAAE,EAAA,QAAQ,wBAAwB,CAAC,MAAM,EAAqB,EAAgB,EAAQ,MAAM,CAAC,GACvG,GACD,CAGH,IAAK,IAAM,KAAS,EAClB,EACA,EAAQ,mBACT,CAAE,CACD,IAAM,EAAY,EAAkB,EAAO,EAAQ,CACnD,GAAI,EAAM,OAAS,UAAW,CAC5B,IAAM,EACJ,EAAM,SAAW,OACb,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CACtC,EAAA,QAAK,EAAE,EAAA,QAAQ,yBAAyB,CAC9C,EAAM,KACJ,MAAM,IACN,GACA,EAAY,EAAqB,EAAM,QAAQ,CAC/C,GACD,SACQ,EAAM,OAAS,QAAS,CACjC,IAAM,EAAS,EAAM,QAClB,MAAM;EAAK,CACX,IAAK,GAAS,KAAK,EAAqB,EAAK,GAAG,CAChD,KAAK;EAAK,CACb,EAAM,KACJ,MAAM,EAAA,QAAK,EAAE,EAAA,QAAQ,aAAa,GAClC,GACA,EAAY,EACZ,GACD,SACQ,EAAM,OAAS,OACxB,EAAM,KACJ,OAAO,EAAW,EAAM,QAAQ,CAAC,IACjC,GACA,EAAY,EAAqB,EAAM,QAAQ,CAC/C,GACD,SACQ,EAAQ,oBAAsB,EAAM,QAC7C,EAAM,KACJ,YACA,oBAAoB,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,CAAC,YAC9G,GACA,EAAY,EAAc,EAAM,QAAQ,CACxC,GACA,aACA,GACD,KACI,CACL,IAAM,EAAkB,EAAY,OAAO,EAAU,SAAS,GAAK,GACnE,EAAM,KACJ,cAAc,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAgB,MAC1H,GACD,EAIL,MAAO,GAAG,EAAM,KAAK;EAAK,CAAC,MAAM,CAAC,KAG9B,GACJ,EACA,IACW,CACX,GAAI,CAAC,EAAQ,kBAAmB,MAAO,GACvC,IAAM,EAAY,EAAgB,EAAM,UAAU,CAClD,MAAO,mBAAmB,EAAW,EAAU,CAAC,IAAI,EAAW,EAAU,CAAC,UAG/D,GACX,EACA,IACW,CACX,IAAM,EAAQ,EAAU,EAAQ,MAAM,CAChC,EAAO,EAAuB,EAAQ,EAAQ,mBAAmB,CACpE,IAAK,GAAU,CACd,IAAM,EAAY,EAAc,EAAO,EAAQ,CAC/C,GAAI,EAAM,OAAS,UAAW,CAC5B,IAAM,EACJ,EAAM,SAAW,OACb,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CACtC,EAAA,QAAK,EAAE,EAAA,QAAQ,yBAAyB,CAC9C,MAAO,2BAA2B,EAAM,OAAO,aAAa,CAAC;gBACrD,EAAW,EAAO,CAAC,OAAO,EAAU;yBAC3B,EAAW,EAAM,QAAQ,CAAC;YAsB7C,OAnBI,EAAM,OAAS,QACV;gBACC,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,aAAa,CAAC,CAAC,OAAO,EAAU;yBACjD,EAAW,EAAM,QAAQ,CAAC;YAGzC,EAAM,OAAS,OACV;oBACK,EAAW,EAAM,QAAQ,CAAC,WAAW,EAAU;IAC/D,EAAM,QAAU,wBAAwB,EAAW,EAAM,QAAQ,CAAC,QAAU,GAAG;UAI3E,EAAQ,oBAAsB,EAAM,QAChC;qBACS,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAU;SAClH,EAAW,EAAM,QAAQ,CAAC;YAEvB,qCAAqC,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAU,SAEjJ,CACD,KAAK;EAAK,CAEP,EAAQ,EAAQ,MAClB,4BAA4B,EAAW,EAAA,QAAK,EAAE,EAAA,QAAQ,wBAAwB,CAAC,CAAC,aAAa,EAAW,EAAgB,EAAQ,MAAM,CAAC,CAAC,MACxI,GAEJ,MAAO;cACK,EAAW,EAAA,QAAK,kBAAoB,EAAA,QAAK,UAAY,KAAK,CAAC;;;;;WAK9D,EAAW,EAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BnB,EAAW,EAAM,CAAC;MACtB,EAAM;MACN,EAAK"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { OpenHandsEvent } from "#/types/agent-server/core";
|
|
2
|
+
export type TranscriptExportFormat = "markdown" | "html";
|
|
3
|
+
export interface TranscriptExportOptions {
|
|
4
|
+
includeToolDetails: boolean;
|
|
5
|
+
includeTimestamps: boolean;
|
|
6
|
+
title?: string | null;
|
|
7
|
+
model?: string | null;
|
|
8
|
+
}
|
|
9
|
+
export declare const eventsToMarkdown: (events: OpenHandsEvent[], options: TranscriptExportOptions) => string;
|
|
10
|
+
export declare const eventsToHtml: (events: OpenHandsEvent[], options: TranscriptExportOptions) => string;
|
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { I18nKey as e } from "../../i18n/declaration.js";
|
|
2
|
+
import t from "../../i18n/index.js";
|
|
3
|
+
import { isACPToolCallEvent as n, isActionEvent as r, isAgentErrorEvent as i, isConversationStateUpdateEvent as a, isGoalConversationStateUpdateEvent as o, isHookExecutionEvent as s, isMessageEvent as c, isObservationEvent as l, isStreamingDeltaEvent as u, isSwitchLLMObservationEvent as d } from "../../types/agent-server/type-guards.js";
|
|
4
|
+
import { getActionThoughtText as f, getReasoningContent as p, splitInlineThink as m } from "../../components/conversation-events/chat/event-thought-helpers.js";
|
|
5
|
+
import { handleEventForUI as h } from "../handle-event-for-ui.js";
|
|
6
|
+
import { shouldRenderEvent as g } from "../../components/conversation-events/chat/event-content-helpers/should-render-event.js";
|
|
7
|
+
import { parseMessageFromEvent as _ } from "../../components/conversation-events/chat/event-content-helpers/parse-message-from-event.js";
|
|
8
|
+
import { getActionContent as v } from "../../components/conversation-events/chat/event-content-helpers/get-action-content.js";
|
|
9
|
+
import { getObservationContent as y } from "../../components/conversation-events/chat/event-content-helpers/get-observation-content.js";
|
|
10
|
+
import { getACPToolCallContent as b, stripRedundantTitlePrefix as x } from "../../components/conversation-events/chat/event-content-helpers/get-acp-tool-call-content.js";
|
|
11
|
+
import { groupEvents as S } from "../../components/conversation-events/chat/group-events.js";
|
|
12
|
+
//#region src/utils/transcript-export/index.ts
|
|
13
|
+
var C = new Set([
|
|
14
|
+
"BrowserClickAction",
|
|
15
|
+
"BrowserCloseTabAction",
|
|
16
|
+
"BrowserGetContentAction",
|
|
17
|
+
"BrowserGetStateAction",
|
|
18
|
+
"BrowserGoBackAction",
|
|
19
|
+
"BrowserListTabsAction",
|
|
20
|
+
"BrowserNavigateAction",
|
|
21
|
+
"BrowserScrollAction",
|
|
22
|
+
"BrowserSwitchTabAction",
|
|
23
|
+
"BrowserTypeAction",
|
|
24
|
+
"ExecuteBashAction",
|
|
25
|
+
"FileEditorAction",
|
|
26
|
+
"GlobAction",
|
|
27
|
+
"GrepAction",
|
|
28
|
+
"InvokeSkillAction",
|
|
29
|
+
"MCPToolAction",
|
|
30
|
+
"StrReplaceEditorAction",
|
|
31
|
+
"TaskAction",
|
|
32
|
+
"TaskTrackerAction",
|
|
33
|
+
"TerminalAction",
|
|
34
|
+
"ThinkAction"
|
|
35
|
+
]), w = new Set([
|
|
36
|
+
"BrowserObservation",
|
|
37
|
+
"CanvasUIObservation",
|
|
38
|
+
"ExecuteBashObservation",
|
|
39
|
+
"FileEditorObservation",
|
|
40
|
+
"GlobObservation",
|
|
41
|
+
"GrepObservation",
|
|
42
|
+
"InvokeSkillObservation",
|
|
43
|
+
"MCPToolObservation",
|
|
44
|
+
"StrReplaceEditorObservation",
|
|
45
|
+
"SwitchLLMObservation",
|
|
46
|
+
"TaskTrackerObservation",
|
|
47
|
+
"TaskObservation",
|
|
48
|
+
"TerminalObservation"
|
|
49
|
+
]), T = (e) => e.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim(), E = (e, t = 100) => e.length > t ? `${e.slice(0, t)}…` : e, D = (e) => {
|
|
50
|
+
let t = new Date(e);
|
|
51
|
+
return Number.isNaN(t.getTime()) ? e : t.toISOString();
|
|
52
|
+
}, O = (e) => e.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll("\"", """).replaceAll("'", "'"), k = (e) => e.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replace(/(!?)\[([^\]]*)\]\(\s*((?:javascript|data|vbscript):[^)]*)\)/gi, "$1\\[$2\\]($3)").replace(/^(\s{0,3})\[([^\]]+)\]:(\s*<?(?:javascript|data|vbscript):)/gim, "$1\\[$2\\]:$3"), A = (n) => T(n || t.t(e.TRANSCRIPT_EXPORT$DEFAULT_TITLE)) || t.t(e.TRANSCRIPT_EXPORT$DEFAULT_TITLE), j = (e, n) => T(t.t(e, n).replace(/<\/?(?:cmd|path)>/g, "")), M = (e) => /^[a-z][a-z0-9_]*\s*:\s*[[{]/i.test(e), N = (e) => e.replace(/(?:Action|Observation)$/, "").replace(/([a-z])([A-Z])/g, "$1 $2").trim(), P = (e) => {
|
|
53
|
+
let t = T(e.summary || "");
|
|
54
|
+
return t && !M(t) ? t : T(e.tool_name) || N(e.action.kind);
|
|
55
|
+
}, F = (e, t) => t ? P(t) : T(e.tool_name) || N(e.observation.kind), I = (e) => e.filter((e) => e.type === "text" && typeof e.text == "string").map((e) => e.text).join("\n"), L = (n) => [`${t.t(e.TASK$SUBAGENT)}: ${n.subagent_type}`, `${t.t(e.TASK$QUERY)}:\n${n.prompt}`].join("\n\n"), R = (n, r) => {
|
|
56
|
+
let i = r?.action.kind === "TaskAction" ? r.action : void 0;
|
|
57
|
+
return [
|
|
58
|
+
`${t.t(e.TASK$SUBAGENT)}: ${n.subagent}`,
|
|
59
|
+
`${t.t(e.TASK$TASK_ID)}: ${n.task_id}`,
|
|
60
|
+
i ? `${t.t(e.TASK$QUERY)}:\n${i.prompt}` : "",
|
|
61
|
+
`${t.t(e.TASK$RESULT)}:\n${I(n.content)}`
|
|
62
|
+
].filter(Boolean).join("\n\n");
|
|
63
|
+
}, z = (e) => e.action.kind === "TaskAction" ? L(e.action) : C.has(e.action.kind) ? v(e) : "", B = (e, t) => e.observation.kind === "TaskObservation" ? R(e.observation, t) : w.has(e.observation.kind) ? y(e) : "", V = (e) => s(e) ? [
|
|
64
|
+
e.reason,
|
|
65
|
+
e.error,
|
|
66
|
+
e.stdout,
|
|
67
|
+
e.stderr
|
|
68
|
+
].filter((e) => !!e?.trim()).join("\n\n") : "", H = (e) => [p(e), f(e)].map((e) => e.trim()).filter(Boolean).join("\n\n"), U = (e) => ![
|
|
69
|
+
"FinishAction",
|
|
70
|
+
"SwitchLLMAction",
|
|
71
|
+
"ThinkAction"
|
|
72
|
+
].includes(e.action.kind), W = (f, p) => {
|
|
73
|
+
let v = f.reduce((e, t) => {
|
|
74
|
+
try {
|
|
75
|
+
return h(t, e);
|
|
76
|
+
} catch {
|
|
77
|
+
return e;
|
|
78
|
+
}
|
|
79
|
+
}, []), y = new Map(f.filter(r).map((e) => [e.id, e])), C = [], w = /* @__PURE__ */ new Set(), D = S(v.filter((e) => d(e) && !e.observation.is_error || g(e)), 2 ** 53 - 1, f), O = (e) => {
|
|
80
|
+
if (w.has(e.id) || !U(e)) return;
|
|
81
|
+
w.add(e.id);
|
|
82
|
+
let t = H(e);
|
|
83
|
+
t && C.push({
|
|
84
|
+
kind: "message",
|
|
85
|
+
author: "assistant",
|
|
86
|
+
content: t,
|
|
87
|
+
timestamp: e.timestamp
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
for (let f of D) {
|
|
91
|
+
if (f.kind === "group") continue;
|
|
92
|
+
if (f.kind === "thought") {
|
|
93
|
+
O(f.action);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
let { event: h } = f;
|
|
97
|
+
try {
|
|
98
|
+
let f = r(h) ? h : l(h) ? y.get(h.action_id) : void 0;
|
|
99
|
+
if (f && O(f), d(h) && !h.observation.is_error) {
|
|
100
|
+
C.push({
|
|
101
|
+
kind: "note",
|
|
102
|
+
summary: j(e.MODEL$SWITCHED_TO_PROFILE, { name: h.observation.profile_name }),
|
|
103
|
+
content: [h.observation.active_model ? `${t.t(e.TRANSCRIPT_EXPORT$MODEL)}: ${h.observation.active_model}` : "", h.observation.reason ? `${t.t(e.TRANSCRIPT_EXPORT$REASON)}: ${h.observation.reason}` : ""].filter(Boolean).join("\n"),
|
|
104
|
+
timestamp: h.timestamp
|
|
105
|
+
});
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (c(h)) {
|
|
109
|
+
let e = _(h).trim();
|
|
110
|
+
if (h.source === "agent") {
|
|
111
|
+
let { reasoning: t, message: n } = m(e);
|
|
112
|
+
[t, n].filter(Boolean).forEach((e) => {
|
|
113
|
+
C.push({
|
|
114
|
+
kind: "message",
|
|
115
|
+
author: "assistant",
|
|
116
|
+
content: e,
|
|
117
|
+
timestamp: h.timestamp
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
} else e && C.push({
|
|
121
|
+
kind: "message",
|
|
122
|
+
author: "user",
|
|
123
|
+
content: e,
|
|
124
|
+
timestamp: h.timestamp
|
|
125
|
+
});
|
|
126
|
+
continue;
|
|
127
|
+
}
|
|
128
|
+
if (u(h)) {
|
|
129
|
+
let { reasoning: e, message: t } = m(h.content ?? "", { streaming: !0 }), n = [h.reasoning_content?.trim() || "", e].filter(Boolean).join("\n\n");
|
|
130
|
+
n && C.push({
|
|
131
|
+
kind: "message",
|
|
132
|
+
author: "assistant",
|
|
133
|
+
content: n,
|
|
134
|
+
timestamp: h.timestamp
|
|
135
|
+
}), t.trim() && C.push({
|
|
136
|
+
kind: "message",
|
|
137
|
+
author: "assistant",
|
|
138
|
+
content: t.trim(),
|
|
139
|
+
timestamp: h.timestamp
|
|
140
|
+
});
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
if (i(h)) {
|
|
144
|
+
C.push({
|
|
145
|
+
kind: "error",
|
|
146
|
+
content: h.error,
|
|
147
|
+
timestamp: h.timestamp
|
|
148
|
+
});
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
if (r(h)) {
|
|
152
|
+
if (h.action.kind === "FinishAction") {
|
|
153
|
+
let e = h.action.message.trim();
|
|
154
|
+
e && C.push({
|
|
155
|
+
kind: "message",
|
|
156
|
+
author: "assistant",
|
|
157
|
+
content: e,
|
|
158
|
+
timestamp: h.timestamp
|
|
159
|
+
});
|
|
160
|
+
} else C.push({
|
|
161
|
+
kind: "tool",
|
|
162
|
+
summary: P(h),
|
|
163
|
+
details: p ? z(h) : "",
|
|
164
|
+
timestamp: h.timestamp
|
|
165
|
+
});
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (l(h)) {
|
|
169
|
+
let e = y.get(h.action_id);
|
|
170
|
+
C.push({
|
|
171
|
+
kind: "tool",
|
|
172
|
+
summary: F(h, e),
|
|
173
|
+
details: p ? B(h, e) : "",
|
|
174
|
+
timestamp: h.timestamp
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (n(h)) {
|
|
179
|
+
C.push({
|
|
180
|
+
kind: "tool",
|
|
181
|
+
summary: x(h) || t.t(e.ACTION_MESSAGE$ACP_TOOL),
|
|
182
|
+
details: p ? b(h) : "",
|
|
183
|
+
timestamp: h.timestamp
|
|
184
|
+
});
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (s(h)) {
|
|
188
|
+
C.push({
|
|
189
|
+
kind: "tool",
|
|
190
|
+
summary: t.t(e.TRANSCRIPT_EXPORT$HOOK, { command: E(T(h.hook_command), 100) }),
|
|
191
|
+
details: p ? V(h) : "",
|
|
192
|
+
timestamp: h.timestamp
|
|
193
|
+
});
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
a(h) && o(h) && C.push({
|
|
197
|
+
kind: "note",
|
|
198
|
+
summary: `${t.t(e.GOAL$PREFIX)} ${t.t({
|
|
199
|
+
running: e.GOAL$STATUS_RUNNING,
|
|
200
|
+
complete: e.GOAL$STATUS_COMPLETE,
|
|
201
|
+
capped: e.GOAL$STATUS_CAPPED,
|
|
202
|
+
interrupted: e.GOAL$STATUS_INTERRUPTED
|
|
203
|
+
}[h.value.status])}`,
|
|
204
|
+
content: [h.value.objective, h.value.verdict?.missing || ""].filter(Boolean).join("\n\n"),
|
|
205
|
+
timestamp: h.timestamp
|
|
206
|
+
});
|
|
207
|
+
} catch {}
|
|
208
|
+
}
|
|
209
|
+
return C;
|
|
210
|
+
}, G = (e, t) => t.includeTimestamps ? `<sub>${O(D(e.timestamp))}</sub>\n\n` : "", K = (e) => {
|
|
211
|
+
let t = Math.max(0, ...Array.from(e.matchAll(/`+/g), (e) => e[0].length)), n = "`".repeat(Math.max(3, t + 1));
|
|
212
|
+
return `${n}text\n${e}\n${n}`;
|
|
213
|
+
}, q = (n, r) => {
|
|
214
|
+
let i = [`# ${k(A(r.title))}`, ""];
|
|
215
|
+
r.model && i.push(`**${t.t(e.TRANSCRIPT_EXPORT$MODEL)}:** ${k(T(r.model))}`, "");
|
|
216
|
+
for (let a of W(n, r.includeToolDetails)) {
|
|
217
|
+
let n = G(a, r);
|
|
218
|
+
if (a.kind === "message") {
|
|
219
|
+
let r = a.author === "user" ? t.t(e.TRANSCRIPT_EXPORT$USER) : t.t(e.CHAT_INTERFACE$ASSISTANT);
|
|
220
|
+
i.push(`## ${r}`, "", n + k(a.content), "");
|
|
221
|
+
} else if (a.kind === "error") {
|
|
222
|
+
let r = a.content.split("\n").map((e) => `> ${k(e)}`).join("\n");
|
|
223
|
+
i.push(`## ${t.t(e.COMMON$ERROR)}`, "", n + r, "");
|
|
224
|
+
} else if (a.kind === "note") i.push(`> **${O(a.summary)}**`, "", n + k(a.content), "");
|
|
225
|
+
else if (r.includeToolDetails && a.details) i.push("<details>", `<summary><strong>${O(t.t(e.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${O(a.summary)}</summary>`, "", n + K(a.details), "", "</details>", "");
|
|
226
|
+
else {
|
|
227
|
+
let r = n ? `<br>${n.trimEnd()}` : "";
|
|
228
|
+
i.push(`<p><strong>${O(t.t(e.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${O(a.summary)}${r}</p>`, "");
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return `${i.join("\n").trim()}\n`;
|
|
232
|
+
}, J = (e, t) => {
|
|
233
|
+
if (!t.includeTimestamps) return "";
|
|
234
|
+
let n = D(e.timestamp);
|
|
235
|
+
return `<time datetime="${O(n)}">${O(n)}</time>`;
|
|
236
|
+
}, Y = (n, r) => {
|
|
237
|
+
let i = A(r.title), a = W(n, r.includeToolDetails).map((n) => {
|
|
238
|
+
let i = J(n, r);
|
|
239
|
+
if (n.kind === "message") {
|
|
240
|
+
let r = n.author === "user" ? t.t(e.TRANSCRIPT_EXPORT$USER) : t.t(e.CHAT_INTERFACE$ASSISTANT);
|
|
241
|
+
return `<section class="message ${n.author.toLowerCase()}">
|
|
242
|
+
<header><h2>${O(r)}</h2>${i}</header>
|
|
243
|
+
<div class="content">${O(n.content)}</div>
|
|
244
|
+
</section>`;
|
|
245
|
+
}
|
|
246
|
+
return n.kind === "error" ? `<section class="message error">
|
|
247
|
+
<header><h2>${O(t.t(e.COMMON$ERROR))}</h2>${i}</header>
|
|
248
|
+
<div class="content">${O(n.content)}</div>
|
|
249
|
+
</section>` : n.kind === "note" ? `<aside class="note">
|
|
250
|
+
<header><strong>${O(n.summary)}</strong>${i}</header>
|
|
251
|
+
${n.content ? `<div class="content">${O(n.content)}</div>` : ""}
|
|
252
|
+
</aside>` : r.includeToolDetails && n.details ? `<details>
|
|
253
|
+
<summary><strong>${O(t.t(e.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${O(n.summary)}${i}</summary>
|
|
254
|
+
<pre>${O(n.details)}</pre>
|
|
255
|
+
</details>` : `<div class="tool-summary"><strong>${O(t.t(e.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${O(n.summary)}${i}</div>`;
|
|
256
|
+
}).join("\n"), o = r.model ? `<p class="model"><strong>${O(t.t(e.TRANSCRIPT_EXPORT$MODEL))}:</strong> ${O(T(r.model))}</p>` : "";
|
|
257
|
+
return `<!doctype html>
|
|
258
|
+
<html lang="${O(t.resolvedLanguage || t.language || "en")}">
|
|
259
|
+
<head>
|
|
260
|
+
<meta charset="utf-8">
|
|
261
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
262
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; img-src data:">
|
|
263
|
+
<title>${O(i)}</title>
|
|
264
|
+
<style>
|
|
265
|
+
:root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, sans-serif; }
|
|
266
|
+
body { margin: 0; background: #111827; color: #e5e7eb; line-height: 1.55; }
|
|
267
|
+
main { box-sizing: border-box; width: min(860px, 100%); margin: 0 auto; padding: 48px 24px 80px; }
|
|
268
|
+
h1 { margin: 0; font-size: 2rem; }
|
|
269
|
+
h2 { margin: 0; font-size: 1rem; }
|
|
270
|
+
.model { margin: 8px 0 32px; color: #9ca3af; }
|
|
271
|
+
.message, details, .tool-summary, .note { margin: 16px 0; border: 1px solid #374151; border-radius: 10px; padding: 16px; background: #1f2937; }
|
|
272
|
+
.user { border-left: 4px solid #60a5fa; }
|
|
273
|
+
.assistant { border-left: 4px solid #34d399; }
|
|
274
|
+
.error { border-left: 4px solid #f87171; }
|
|
275
|
+
.note { border-left: 4px solid #a78bfa; }
|
|
276
|
+
header, summary, .tool-summary { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }
|
|
277
|
+
summary { cursor: pointer; }
|
|
278
|
+
time { flex: none; color: #9ca3af; font-size: .75rem; font-weight: 400; }
|
|
279
|
+
.content { margin-top: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
280
|
+
pre { margin: 14px 0 0; padding: 14px; overflow-x: auto; border-radius: 7px; background: #111827; color: #d1d5db; white-space: pre-wrap; overflow-wrap: anywhere; }
|
|
281
|
+
@media (prefers-color-scheme: light) {
|
|
282
|
+
body { background: #f9fafb; color: #111827; }
|
|
283
|
+
.message, details, .tool-summary, .note { border-color: #d1d5db; background: #fff; }
|
|
284
|
+
pre { background: #f3f4f6; color: #1f2937; }
|
|
285
|
+
.model, time { color: #6b7280; }
|
|
286
|
+
}
|
|
287
|
+
</style>
|
|
288
|
+
</head>
|
|
289
|
+
<body>
|
|
290
|
+
<main>
|
|
291
|
+
<h1>${O(i)}</h1>
|
|
292
|
+
${o}
|
|
293
|
+
${a}
|
|
294
|
+
</main>
|
|
295
|
+
</body>
|
|
296
|
+
</html>
|
|
297
|
+
`;
|
|
298
|
+
};
|
|
299
|
+
//#endregion
|
|
300
|
+
export { Y as eventsToHtml, q as eventsToMarkdown };
|
|
301
|
+
|
|
302
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/utils/transcript-export/index.ts"],"sourcesContent":["import type {\n ActionEvent,\n OpenHandsEvent,\n ObservationEvent,\n} from \"#/types/agent-server/core\";\nimport type { TaskAction } from \"#/types/agent-server/core/base/action\";\nimport type { TaskObservation } from \"#/types/agent-server/core/base/observation\";\nimport {\n isACPToolCallEvent,\n isActionEvent,\n isAgentErrorEvent,\n isConversationStateUpdateEvent,\n isGoalConversationStateUpdateEvent,\n isHookExecutionEvent,\n isMessageEvent,\n isObservationEvent,\n isStreamingDeltaEvent,\n isSwitchLLMObservationEvent,\n} from \"#/types/agent-server/type-guards\";\nimport { handleEventForUI } from \"#/utils/handle-event-for-ui\";\nimport { shouldRenderEvent } from \"#/components/conversation-events/chat/event-content-helpers/should-render-event\";\nimport { parseMessageFromEvent } from \"#/components/conversation-events/chat/event-content-helpers/parse-message-from-event\";\nimport { getActionContent } from \"#/components/conversation-events/chat/event-content-helpers/get-action-content\";\nimport { getObservationContent } from \"#/components/conversation-events/chat/event-content-helpers/get-observation-content\";\nimport {\n getACPToolCallContent,\n stripRedundantTitlePrefix,\n} from \"#/components/conversation-events/chat/event-content-helpers/get-acp-tool-call-content\";\nimport { groupEvents } from \"#/components/conversation-events/chat/group-events\";\nimport {\n getActionThoughtText,\n getReasoningContent,\n splitInlineThink,\n} from \"#/components/conversation-events/chat/event-thought-helpers\";\nimport i18n from \"#/i18n\";\nimport { I18nKey } from \"#/i18n/declaration\";\n\nexport type TranscriptExportFormat = \"markdown\" | \"html\";\n\nexport interface TranscriptExportOptions {\n includeToolDetails: boolean;\n includeTimestamps: boolean;\n title?: string | null;\n model?: string | null;\n}\n\ntype TranscriptEntry =\n | {\n kind: \"message\";\n author: \"user\" | \"assistant\";\n content: string;\n timestamp: string;\n }\n | {\n kind: \"tool\";\n summary: string;\n details: string;\n timestamp: string;\n }\n | {\n kind: \"error\";\n content: string;\n timestamp: string;\n }\n | {\n kind: \"note\";\n summary: string;\n content: string;\n timestamp: string;\n };\n\nconst SAFE_ACTION_DETAIL_KINDS = new Set([\n \"BrowserClickAction\",\n \"BrowserCloseTabAction\",\n \"BrowserGetContentAction\",\n \"BrowserGetStateAction\",\n \"BrowserGoBackAction\",\n \"BrowserListTabsAction\",\n \"BrowserNavigateAction\",\n \"BrowserScrollAction\",\n \"BrowserSwitchTabAction\",\n \"BrowserTypeAction\",\n \"ExecuteBashAction\",\n \"FileEditorAction\",\n \"GlobAction\",\n \"GrepAction\",\n \"InvokeSkillAction\",\n \"MCPToolAction\",\n \"StrReplaceEditorAction\",\n \"TaskAction\",\n \"TaskTrackerAction\",\n \"TerminalAction\",\n \"ThinkAction\",\n]);\n\nconst SAFE_OBSERVATION_DETAIL_KINDS = new Set([\n \"BrowserObservation\",\n \"CanvasUIObservation\",\n \"ExecuteBashObservation\",\n \"FileEditorObservation\",\n \"GlobObservation\",\n \"GrepObservation\",\n \"InvokeSkillObservation\",\n \"MCPToolObservation\",\n \"StrReplaceEditorObservation\",\n \"SwitchLLMObservation\",\n \"TaskTrackerObservation\",\n \"TaskObservation\",\n \"TerminalObservation\",\n]);\n\nconst cleanInlineText = (value: string): string =>\n value\n .replace(/[\\r\\n]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n\nconst truncate = (value: string, maxLength = 100): string =>\n value.length > maxLength ? `${value.slice(0, maxLength)}…` : value;\n\nconst formatTimestamp = (timestamp: string): string => {\n const date = new Date(timestamp);\n return Number.isNaN(date.getTime()) ? timestamp : date.toISOString();\n};\n\nconst escapeHtml = (value: string): string =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replaceAll('\"', \""\")\n .replaceAll(\"'\", \"'\");\n\n/**\n * GitHub Markdown permits raw HTML, so dynamic conversation text needs the\n * same inert representation as the chat's sanitized Markdown renderer.\n */\nconst sanitizeMarkdownText = (value: string): string =>\n value\n .replaceAll(\"&\", \"&\")\n .replaceAll(\"<\", \"<\")\n .replaceAll(\">\", \">\")\n .replace(\n /(!?)\\[([^\\]]*)\\]\\(\\s*((?:javascript|data|vbscript):[^)]*)\\)/gi,\n \"$1\\\\[$2\\\\]($3)\",\n )\n .replace(\n /^(\\s{0,3})\\[([^\\]]+)\\]:(\\s*<?(?:javascript|data|vbscript):)/gim,\n \"$1\\\\[$2\\\\]:$3\",\n );\n\nconst safeTitle = (title?: string | null): string =>\n cleanInlineText(title || i18n.t(I18nKey.TRANSCRIPT_EXPORT$DEFAULT_TITLE)) ||\n i18n.t(I18nKey.TRANSCRIPT_EXPORT$DEFAULT_TITLE);\n\nconst translatePlain = (\n key: I18nKey,\n values?: Record<string, unknown>,\n): string =>\n cleanInlineText(i18n.t(key, values).replace(/<\\/?(?:cmd|path)>/g, \"\"));\n\nconst isServerFallbackSummary = (summary: string): boolean =>\n /^[a-z][a-z0-9_]*\\s*:\\s*[[{]/i.test(summary);\n\nconst humanizeEventKind = (kind: string): string =>\n kind\n .replace(/(?:Action|Observation)$/, \"\")\n .replace(/([a-z])([A-Z])/g, \"$1 $2\")\n .trim();\n\nconst getActionSummary = (event: ActionEvent): string => {\n const summary = cleanInlineText(event.summary || \"\");\n if (summary && !isServerFallbackSummary(summary)) return summary;\n return (\n cleanInlineText(event.tool_name) || humanizeEventKind(event.action.kind)\n );\n};\n\nconst getObservationSummary = (\n event: ObservationEvent,\n correspondingAction?: ActionEvent,\n): string => {\n if (correspondingAction) return getActionSummary(correspondingAction);\n return (\n cleanInlineText(event.tool_name) ||\n humanizeEventKind(event.observation.kind)\n );\n};\n\nconst getTextContent = (\n content: Array<{ type: string; text?: string }>,\n): string =>\n content\n .filter(\n (item): item is { type: \"text\"; text: string } =>\n item.type === \"text\" && typeof item.text === \"string\",\n )\n .map((item) => item.text)\n .join(\"\\n\");\n\nconst getTaskActionDetails = (action: TaskAction): string =>\n [\n `${i18n.t(I18nKey.TASK$SUBAGENT)}: ${action.subagent_type}`,\n `${i18n.t(I18nKey.TASK$QUERY)}:\\n${action.prompt}`,\n ].join(\"\\n\\n\");\n\nconst getTaskObservationDetails = (\n observation: TaskObservation,\n correspondingAction?: ActionEvent,\n): string => {\n const taskAction =\n correspondingAction?.action.kind === \"TaskAction\"\n ? correspondingAction.action\n : undefined;\n return [\n `${i18n.t(I18nKey.TASK$SUBAGENT)}: ${observation.subagent}`,\n `${i18n.t(I18nKey.TASK$TASK_ID)}: ${observation.task_id}`,\n taskAction ? `${i18n.t(I18nKey.TASK$QUERY)}:\\n${taskAction.prompt}` : \"\",\n `${i18n.t(I18nKey.TASK$RESULT)}:\\n${getTextContent(observation.content)}`,\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n};\n\nconst getSafeActionDetails = (event: ActionEvent): string => {\n if (event.action.kind === \"TaskAction\") {\n return getTaskActionDetails(event.action);\n }\n return SAFE_ACTION_DETAIL_KINDS.has(event.action.kind)\n ? getActionContent(event)\n : \"\";\n};\n\nconst getSafeObservationDetails = (\n event: ObservationEvent,\n correspondingAction?: ActionEvent,\n): string => {\n if (event.observation.kind === \"TaskObservation\") {\n return getTaskObservationDetails(event.observation, correspondingAction);\n }\n return SAFE_OBSERVATION_DETAIL_KINDS.has(event.observation.kind)\n ? getObservationContent(event)\n : \"\";\n};\n\nconst getHookDetails = (event: OpenHandsEvent): string => {\n if (!isHookExecutionEvent(event)) return \"\";\n return [event.reason, event.error, event.stdout, event.stderr]\n .filter((value): value is string => Boolean(value?.trim()))\n .join(\"\\n\\n\");\n};\n\nconst getActionNarration = (event: ActionEvent): string =>\n [getReasoningContent(event), getActionThoughtText(event)]\n .map((content) => content.trim())\n .filter(Boolean)\n .join(\"\\n\\n\");\n\nconst canRenderActionNarration = (event: ActionEvent): boolean =>\n ![\"FinishAction\", \"SwitchLLMAction\", \"ThinkAction\"].includes(\n event.action.kind,\n );\n\nconst buildTranscriptEntries = (\n events: OpenHandsEvent[],\n includeToolDetails: boolean,\n): TranscriptEntry[] => {\n const uiEvents = events.reduce<OpenHandsEvent[]>((current, event) => {\n try {\n return handleEventForUI(event, current);\n } catch {\n return current;\n }\n }, []);\n const actionsById = new Map(\n events.filter(isActionEvent).map((event) => [event.id, event]),\n );\n const entries: TranscriptEntry[] = [];\n const emittedNarrationActionIds = new Set<string>();\n const renderableEvents = uiEvents.filter(\n (event) =>\n (isSwitchLLMObservationEvent(event) && !event.observation.is_error) ||\n shouldRenderEvent(event),\n );\n const renderedItems = groupEvents(\n renderableEvents,\n Number.MAX_SAFE_INTEGER,\n events,\n );\n\n const addActionNarration = (action: ActionEvent) => {\n if (\n emittedNarrationActionIds.has(action.id) ||\n !canRenderActionNarration(action)\n ) {\n return;\n }\n emittedNarrationActionIds.add(action.id);\n const content = getActionNarration(action);\n if (content) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: action.timestamp,\n });\n }\n };\n\n for (const item of renderedItems) {\n if (item.kind === \"group\") continue;\n if (item.kind === \"thought\") {\n addActionNarration(item.action);\n continue;\n }\n\n const { event } = item;\n try {\n const narrationAction = isActionEvent(event)\n ? event\n : isObservationEvent(event)\n ? actionsById.get(event.action_id)\n : undefined;\n if (narrationAction) addActionNarration(narrationAction);\n\n if (isSwitchLLMObservationEvent(event) && !event.observation.is_error) {\n entries.push({\n kind: \"note\",\n summary: translatePlain(I18nKey.MODEL$SWITCHED_TO_PROFILE, {\n name: event.observation.profile_name,\n }),\n content: [\n event.observation.active_model\n ? `${i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL)}: ${event.observation.active_model}`\n : \"\",\n event.observation.reason\n ? `${i18n.t(I18nKey.TRANSCRIPT_EXPORT$REASON)}: ${event.observation.reason}`\n : \"\",\n ]\n .filter(Boolean)\n .join(\"\\n\"),\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isMessageEvent(event)) {\n const parsed = parseMessageFromEvent(event).trim();\n if (event.source === \"agent\") {\n const { reasoning, message } = splitInlineThink(parsed);\n [reasoning, message].filter(Boolean).forEach((content) => {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: event.timestamp,\n });\n });\n } else if (parsed) {\n entries.push({\n kind: \"message\",\n author: \"user\",\n content: parsed,\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isStreamingDeltaEvent(event)) {\n const { reasoning: inlineThink, message } = splitInlineThink(\n event.content ?? \"\",\n { streaming: true },\n );\n const reasoningContent = [\n event.reasoning_content?.trim() || \"\",\n inlineThink,\n ]\n .filter(Boolean)\n .join(\"\\n\\n\");\n if (reasoningContent) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content: reasoningContent,\n timestamp: event.timestamp,\n });\n }\n if (message.trim()) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content: message.trim(),\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isAgentErrorEvent(event)) {\n entries.push({\n kind: \"error\",\n content: event.error,\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isActionEvent(event)) {\n if (event.action.kind === \"FinishAction\") {\n const content = event.action.message.trim();\n if (content) {\n entries.push({\n kind: \"message\",\n author: \"assistant\",\n content,\n timestamp: event.timestamp,\n });\n }\n } else {\n entries.push({\n kind: \"tool\",\n summary: getActionSummary(event),\n details: includeToolDetails ? getSafeActionDetails(event) : \"\",\n timestamp: event.timestamp,\n });\n }\n continue;\n }\n\n if (isObservationEvent(event)) {\n const correspondingAction = actionsById.get(event.action_id);\n entries.push({\n kind: \"tool\",\n summary: getObservationSummary(event, correspondingAction),\n details: includeToolDetails\n ? getSafeObservationDetails(event, correspondingAction)\n : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isACPToolCallEvent(event)) {\n entries.push({\n kind: \"tool\",\n summary:\n stripRedundantTitlePrefix(event) ||\n i18n.t(I18nKey.ACTION_MESSAGE$ACP_TOOL),\n details: includeToolDetails ? getACPToolCallContent(event) : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (isHookExecutionEvent(event)) {\n entries.push({\n kind: \"tool\",\n summary: i18n.t(I18nKey.TRANSCRIPT_EXPORT$HOOK, {\n command: truncate(cleanInlineText(event.hook_command), 100),\n }),\n details: includeToolDetails ? getHookDetails(event) : \"\",\n timestamp: event.timestamp,\n });\n continue;\n }\n\n if (\n isConversationStateUpdateEvent(event) &&\n isGoalConversationStateUpdateEvent(event)\n ) {\n entries.push({\n kind: \"note\",\n summary: `${i18n.t(I18nKey.GOAL$PREFIX)} ${i18n.t(\n {\n running: I18nKey.GOAL$STATUS_RUNNING,\n complete: I18nKey.GOAL$STATUS_COMPLETE,\n capped: I18nKey.GOAL$STATUS_CAPPED,\n interrupted: I18nKey.GOAL$STATUS_INTERRUPTED,\n }[event.value.status],\n )}`,\n content: [event.value.objective, event.value.verdict?.missing || \"\"]\n .filter(Boolean)\n .join(\"\\n\\n\"),\n timestamp: event.timestamp,\n });\n }\n } catch {\n // Runtime event payloads may be older than the local TypeScript schema.\n // Skip malformed entries instead of aborting an otherwise valid export.\n }\n }\n\n return entries;\n};\n\nconst markdownTimestamp = (\n entry: TranscriptEntry,\n options: TranscriptExportOptions,\n): string =>\n options.includeTimestamps\n ? `<sub>${escapeHtml(formatTimestamp(entry.timestamp))}</sub>\\n\\n`\n : \"\";\n\nconst markdownFence = (content: string): string => {\n const longestRun = Math.max(\n 0,\n ...Array.from(content.matchAll(/`+/g), (match) => match[0].length),\n );\n const fence = \"`\".repeat(Math.max(3, longestRun + 1));\n return `${fence}text\\n${content}\\n${fence}`;\n};\n\nexport const eventsToMarkdown = (\n events: OpenHandsEvent[],\n options: TranscriptExportOptions,\n): string => {\n const lines = [`# ${sanitizeMarkdownText(safeTitle(options.title))}`, \"\"];\n\n if (options.model) {\n lines.push(\n `**${i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL)}:** ${sanitizeMarkdownText(cleanInlineText(options.model))}`,\n \"\",\n );\n }\n\n for (const entry of buildTranscriptEntries(\n events,\n options.includeToolDetails,\n )) {\n const timestamp = markdownTimestamp(entry, options);\n if (entry.kind === \"message\") {\n const author =\n entry.author === \"user\"\n ? i18n.t(I18nKey.TRANSCRIPT_EXPORT$USER)\n : i18n.t(I18nKey.CHAT_INTERFACE$ASSISTANT);\n lines.push(\n `## ${author}`,\n \"\",\n timestamp + sanitizeMarkdownText(entry.content),\n \"\",\n );\n } else if (entry.kind === \"error\") {\n const quoted = entry.content\n .split(\"\\n\")\n .map((line) => `> ${sanitizeMarkdownText(line)}`)\n .join(\"\\n\");\n lines.push(\n `## ${i18n.t(I18nKey.COMMON$ERROR)}`,\n \"\",\n timestamp + quoted,\n \"\",\n );\n } else if (entry.kind === \"note\") {\n lines.push(\n `> **${escapeHtml(entry.summary)}**`,\n \"\",\n timestamp + sanitizeMarkdownText(entry.content),\n \"\",\n );\n } else if (options.includeToolDetails && entry.details) {\n lines.push(\n \"<details>\",\n `<summary><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}</summary>`,\n \"\",\n timestamp + markdownFence(entry.details),\n \"\",\n \"</details>\",\n \"\",\n );\n } else {\n const timestampMarkup = timestamp ? `<br>${timestamp.trimEnd()}` : \"\";\n lines.push(\n `<p><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestampMarkup}</p>`,\n \"\",\n );\n }\n }\n\n return `${lines.join(\"\\n\").trim()}\\n`;\n};\n\nconst htmlTimestamp = (\n entry: TranscriptEntry,\n options: TranscriptExportOptions,\n): string => {\n if (!options.includeTimestamps) return \"\";\n const timestamp = formatTimestamp(entry.timestamp);\n return `<time datetime=\"${escapeHtml(timestamp)}\">${escapeHtml(timestamp)}</time>`;\n};\n\nexport const eventsToHtml = (\n events: OpenHandsEvent[],\n options: TranscriptExportOptions,\n): string => {\n const title = safeTitle(options.title);\n const body = buildTranscriptEntries(events, options.includeToolDetails)\n .map((entry) => {\n const timestamp = htmlTimestamp(entry, options);\n if (entry.kind === \"message\") {\n const author =\n entry.author === \"user\"\n ? i18n.t(I18nKey.TRANSCRIPT_EXPORT$USER)\n : i18n.t(I18nKey.CHAT_INTERFACE$ASSISTANT);\n return `<section class=\"message ${entry.author.toLowerCase()}\">\n <header><h2>${escapeHtml(author)}</h2>${timestamp}</header>\n <div class=\"content\">${escapeHtml(entry.content)}</div>\n</section>`;\n }\n if (entry.kind === \"error\") {\n return `<section class=\"message error\">\n <header><h2>${escapeHtml(i18n.t(I18nKey.COMMON$ERROR))}</h2>${timestamp}</header>\n <div class=\"content\">${escapeHtml(entry.content)}</div>\n</section>`;\n }\n if (entry.kind === \"note\") {\n return `<aside class=\"note\">\n <header><strong>${escapeHtml(entry.summary)}</strong>${timestamp}</header>\n ${entry.content ? `<div class=\"content\">${escapeHtml(entry.content)}</div>` : \"\"}\n</aside>`;\n }\n const details =\n options.includeToolDetails && entry.details\n ? `<details>\n <summary><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestamp}</summary>\n <pre>${escapeHtml(entry.details)}</pre>\n</details>`\n : `<div class=\"tool-summary\"><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$TOOL))}:</strong> ${escapeHtml(entry.summary)}${timestamp}</div>`;\n return details;\n })\n .join(\"\\n\");\n\n const model = options.model\n ? `<p class=\"model\"><strong>${escapeHtml(i18n.t(I18nKey.TRANSCRIPT_EXPORT$MODEL))}:</strong> ${escapeHtml(cleanInlineText(options.model))}</p>`\n : \"\";\n\n return `<!doctype html>\n<html lang=\"${escapeHtml(i18n.resolvedLanguage || i18n.language || \"en\")}\">\n<head>\n <meta charset=\"utf-8\">\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n <meta http-equiv=\"Content-Security-Policy\" content=\"default-src 'none'; style-src 'unsafe-inline'; img-src data:\">\n <title>${escapeHtml(title)}</title>\n <style>\n :root { color-scheme: light dark; font-family: ui-sans-serif, system-ui, sans-serif; }\n body { margin: 0; background: #111827; color: #e5e7eb; line-height: 1.55; }\n main { box-sizing: border-box; width: min(860px, 100%); margin: 0 auto; padding: 48px 24px 80px; }\n h1 { margin: 0; font-size: 2rem; }\n h2 { margin: 0; font-size: 1rem; }\n .model { margin: 8px 0 32px; color: #9ca3af; }\n .message, details, .tool-summary, .note { margin: 16px 0; border: 1px solid #374151; border-radius: 10px; padding: 16px; background: #1f2937; }\n .user { border-left: 4px solid #60a5fa; }\n .assistant { border-left: 4px solid #34d399; }\n .error { border-left: 4px solid #f87171; }\n .note { border-left: 4px solid #a78bfa; }\n header, summary, .tool-summary { display: flex; align-items: baseline; justify-content: space-between; gap: 16px; }\n summary { cursor: pointer; }\n time { flex: none; color: #9ca3af; font-size: .75rem; font-weight: 400; }\n .content { margin-top: 10px; white-space: pre-wrap; overflow-wrap: anywhere; }\n pre { margin: 14px 0 0; padding: 14px; overflow-x: auto; border-radius: 7px; background: #111827; color: #d1d5db; white-space: pre-wrap; overflow-wrap: anywhere; }\n @media (prefers-color-scheme: light) {\n body { background: #f9fafb; color: #111827; }\n .message, details, .tool-summary, .note { border-color: #d1d5db; background: #fff; }\n pre { background: #f3f4f6; color: #1f2937; }\n .model, time { color: #6b7280; }\n }\n </style>\n</head>\n<body>\n <main>\n <h1>${escapeHtml(title)}</h1>\n ${model}\n ${body}\n </main>\n</body>\n</html>\n`;\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
require(`../../_virtual/_rolldown/runtime.cjs`);var e=(e,t)=>e.timestamp.localeCompare(t.timestamp),t=async(t,n,r)=>{let i=[],a=new Set,o=new Set,s,c,l=!1,u=!1;for(;;){let e=await n({limit:100,sortOrder:`TIMESTAMP_DESC`,strictPagination:!0,...c?{pageId:c}:{},...s?{timestampLt:s}:{}});if(!Array.isArray(e.items))throw Error(`Invalid transcript history response: expected page.items to be an array.`);i.push(...e.items);let t,d=!1;if(e.items.forEach(e=>{a.has(e.id)||(a.add(e.id),d=!0),(!t||e.timestamp<t)&&(t=e.timestamp)}),e.next_page_id){if(o.has(e.next_page_id))throw Error(`Transcript history pagination repeated a page cursor.`);o.add(e.next_page_id),c=e.next_page_id,s=void 0,l=!0;continue}if(l&&!u||e.items.length<100)break;if(r===void 0)throw Error(`Transcript history pagination cannot prove that all events were loaded.`);if(!t||s&&(!d||t>=s))throw Error(`Transcript history pagination did not advance.`);c=void 0,s=t,u=!0}let d=new Map;i.slice().reverse().forEach(e=>{d.has(e.id)||d.set(e.id,e)}),t.forEach(e=>{d.has(e.id)||d.set(e.id,e)});let f=[...d.values()].sort(e);if(r!==void 0&&a.size<r)throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size}.`);return f};exports.loadCompleteTranscriptEvents=t;
|
|
2
|
+
//# sourceMappingURL=load-complete-events.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-complete-events.cjs","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => first.timestamp.localeCompare(second.timestamp);\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id)) {\n fetchedEventIds.add(event.id);\n addedEvent = true;\n }\n if (!pageOldestTimestamp || event.timestamp < pageOldestTimestamp) {\n pageOldestTimestamp = event.timestamp;\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (!eventsById.has(event.id)) eventsById.set(event.id, event);\n });\n loadedEvents.forEach((event) => {\n if (!eventsById.has(event.id)) eventsById.set(event.id, event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...eventsById.values()].sort(compareEventTimestamps);\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"gDAqBA,IAAM,GACJ,EACA,IACW,EAAM,UAAU,cAAc,EAAO,UAAU,CAQ/C,EAA+B,MAC1C,EACA,EACA,IAC8B,CAC9B,IAAM,EAAwC,EAAE,CAC1C,EAAkB,IAAI,IACtB,EAAc,IAAI,IACpB,EACA,EACA,EAAa,GACb,EAAyB,GAE7B,OAAa,CACX,IAAM,EAAO,MAAM,EAAa,CAC9B,MAAA,IACA,UAAW,iBACX,iBAAkB,GAClB,GAAI,EAAS,CAAE,SAAQ,CAAG,EAAE,CAC5B,GAAI,EAAkB,CAAE,YAAa,EAAiB,CAAG,EAAE,CAC5D,CAAC,CAEF,GAAI,CAAC,MAAM,QAAQ,EAAK,MAAM,CAC5B,MAAU,MACR,2EACD,CAGH,EAAoB,KAAK,GAAG,EAAK,MAAM,CACvC,IAAI,EACA,EAAa,GAWjB,GAVA,EAAK,MAAM,QAAS,GAAU,CACvB,EAAgB,IAAI,EAAM,GAAG,GAChC,EAAgB,IAAI,EAAM,GAAG,CAC7B,EAAa,KAEX,CAAC,GAAuB,EAAM,UAAY,KAC5C,EAAsB,EAAM,YAE9B,CAEE,EAAK,aAAc,CACrB,GAAI,EAAY,IAAI,EAAK,aAAa,CACpC,MAAU,MACR,wDACD,CAEH,EAAY,IAAI,EAAK,aAAa,CAClC,EAAS,EAAK,aACd,EAAkB,IAAA,GAClB,EAAa,GACb,SAMF,GADI,GAAc,CAAC,GACf,EAAK,MAAM,OAAA,IAAuC,MAKtD,GAAI,IAAuB,IAAA,GACzB,MAAU,MACR,0EACD,CAKH,GAHI,CAAC,GAIH,IACC,CAAC,GAAc,GAAuB,GAEvC,MAAU,MAAM,iDAAiD,CAGnE,EAAS,IAAA,GACT,EAAkB,EAClB,EAAyB,GAG3B,IAAM,EAAa,IAAI,IACvB,EACG,OAAO,CACP,SAAS,CACT,QAAS,GAAU,CACb,EAAW,IAAI,EAAM,GAAG,EAAE,EAAW,IAAI,EAAM,GAAI,EAAM,EAC9D,CACJ,EAAa,QAAS,GAAU,CACzB,EAAW,IAAI,EAAM,GAAG,EAAE,EAAW,IAAI,EAAM,GAAI,EAAM,EAC9D,CAGF,IAAM,EAAiB,CAAC,GAAG,EAAW,QAAQ,CAAC,CAAC,KAAK,EAAuB,CAC5E,GACE,IAAuB,IAAA,IACvB,EAAgB,KAAO,EAEvB,MAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,KAAK,GACrH,CAEH,OAAO"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { OpenHandsEvent } from "#/types/agent-server/core";
|
|
2
|
+
export declare const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;
|
|
3
|
+
interface TranscriptEventSearchOptions {
|
|
4
|
+
limit: number;
|
|
5
|
+
sortOrder: "TIMESTAMP_DESC";
|
|
6
|
+
pageId?: string;
|
|
7
|
+
timestampLt?: string;
|
|
8
|
+
strictPagination: true;
|
|
9
|
+
}
|
|
10
|
+
interface TranscriptEventPage {
|
|
11
|
+
items: OpenHandsEvent[];
|
|
12
|
+
next_page_id?: string | null;
|
|
13
|
+
}
|
|
14
|
+
type SearchTranscriptEvents = (options: TranscriptEventSearchOptions) => Promise<TranscriptEventPage>;
|
|
15
|
+
/**
|
|
16
|
+
* Loads the persisted history from the newest page back to the beginning,
|
|
17
|
+
* then merges any live store events that have not persisted yet. The timestamp
|
|
18
|
+
* anchor matches the chat's existing history pagination, while id-based
|
|
19
|
+
* de-duplication keeps the result stable if pages overlap.
|
|
20
|
+
*/
|
|
21
|
+
export declare const loadCompleteTranscriptEvents: (loadedEvents: OpenHandsEvent[], searchEvents: SearchTranscriptEvents, expectedEventCount?: number) => Promise<OpenHandsEvent[]>;
|
|
22
|
+
export {};
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
var e = (e, t) => e.timestamp.localeCompare(t.timestamp), t = async (t, n, r) => {
|
|
2
|
+
let i = [], a = /* @__PURE__ */ new Set(), o = /* @__PURE__ */ new Set(), s, c, l = !1, u = !1;
|
|
3
|
+
for (;;) {
|
|
4
|
+
let e = await n({
|
|
5
|
+
limit: 100,
|
|
6
|
+
sortOrder: "TIMESTAMP_DESC",
|
|
7
|
+
strictPagination: !0,
|
|
8
|
+
...c ? { pageId: c } : {},
|
|
9
|
+
...s ? { timestampLt: s } : {}
|
|
10
|
+
});
|
|
11
|
+
if (!Array.isArray(e.items)) throw Error("Invalid transcript history response: expected page.items to be an array.");
|
|
12
|
+
i.push(...e.items);
|
|
13
|
+
let t, d = !1;
|
|
14
|
+
if (e.items.forEach((e) => {
|
|
15
|
+
a.has(e.id) || (a.add(e.id), d = !0), (!t || e.timestamp < t) && (t = e.timestamp);
|
|
16
|
+
}), e.next_page_id) {
|
|
17
|
+
if (o.has(e.next_page_id)) throw Error("Transcript history pagination repeated a page cursor.");
|
|
18
|
+
o.add(e.next_page_id), c = e.next_page_id, s = void 0, l = !0;
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (l && !u || e.items.length < 100) break;
|
|
22
|
+
if (r === void 0) throw Error("Transcript history pagination cannot prove that all events were loaded.");
|
|
23
|
+
if (!t || s && (!d || t >= s)) throw Error("Transcript history pagination did not advance.");
|
|
24
|
+
c = void 0, s = t, u = !0;
|
|
25
|
+
}
|
|
26
|
+
let d = /* @__PURE__ */ new Map();
|
|
27
|
+
i.slice().reverse().forEach((e) => {
|
|
28
|
+
d.has(e.id) || d.set(e.id, e);
|
|
29
|
+
}), t.forEach((e) => {
|
|
30
|
+
d.has(e.id) || d.set(e.id, e);
|
|
31
|
+
});
|
|
32
|
+
let f = [...d.values()].sort(e);
|
|
33
|
+
if (r !== void 0 && a.size < r) throw Error(`Transcript history is incomplete: expected ${r} persisted events, received ${a.size}.`);
|
|
34
|
+
return f;
|
|
35
|
+
};
|
|
36
|
+
//#endregion
|
|
37
|
+
export { t as loadCompleteTranscriptEvents };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=load-complete-events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"load-complete-events.js","names":[],"sources":["../../../src/utils/transcript-export/load-complete-events.ts"],"sourcesContent":["import type { OpenHandsEvent } from \"#/types/agent-server/core\";\n\nexport const TRANSCRIPT_HISTORY_PAGE_SIZE = 100;\n\ninterface TranscriptEventSearchOptions {\n limit: number;\n sortOrder: \"TIMESTAMP_DESC\";\n pageId?: string;\n timestampLt?: string;\n strictPagination: true;\n}\n\ninterface TranscriptEventPage {\n items: OpenHandsEvent[];\n next_page_id?: string | null;\n}\n\ntype SearchTranscriptEvents = (\n options: TranscriptEventSearchOptions,\n) => Promise<TranscriptEventPage>;\n\nconst compareEventTimestamps = (\n first: OpenHandsEvent,\n second: OpenHandsEvent,\n): number => first.timestamp.localeCompare(second.timestamp);\n\n/**\n * Loads the persisted history from the newest page back to the beginning,\n * then merges any live store events that have not persisted yet. The timestamp\n * anchor matches the chat's existing history pagination, while id-based\n * de-duplication keeps the result stable if pages overlap.\n */\nexport const loadCompleteTranscriptEvents = async (\n loadedEvents: OpenHandsEvent[],\n searchEvents: SearchTranscriptEvents,\n expectedEventCount?: number,\n): Promise<OpenHandsEvent[]> => {\n const persistedDescending: OpenHandsEvent[] = [];\n const fetchedEventIds = new Set<string>();\n const seenPageIds = new Set<string>();\n let oldestTimestamp: string | undefined;\n let pageId: string | undefined;\n let usedCursor = false;\n let usingTimestampFallback = false;\n\n while (true) {\n const page = await searchEvents({\n limit: TRANSCRIPT_HISTORY_PAGE_SIZE,\n sortOrder: \"TIMESTAMP_DESC\",\n strictPagination: true,\n ...(pageId ? { pageId } : {}),\n ...(oldestTimestamp ? { timestampLt: oldestTimestamp } : {}),\n });\n\n if (!Array.isArray(page.items)) {\n throw new Error(\n \"Invalid transcript history response: expected page.items to be an array.\",\n );\n }\n\n persistedDescending.push(...page.items);\n let pageOldestTimestamp: string | undefined;\n let addedEvent = false;\n page.items.forEach((event) => {\n if (!fetchedEventIds.has(event.id)) {\n fetchedEventIds.add(event.id);\n addedEvent = true;\n }\n if (!pageOldestTimestamp || event.timestamp < pageOldestTimestamp) {\n pageOldestTimestamp = event.timestamp;\n }\n });\n\n if (page.next_page_id) {\n if (seenPageIds.has(page.next_page_id)) {\n throw new Error(\n \"Transcript history pagination repeated a page cursor.\",\n );\n }\n seenPageIds.add(page.next_page_id);\n pageId = page.next_page_id;\n oldestTimestamp = undefined;\n usedCursor = true;\n continue;\n }\n\n // Once a server supplies a cursor, a page without a next cursor is an\n // explicit exhaustion signal, even when the final page is exactly full.\n if (usedCursor && !usingTimestampFallback) break;\n if (page.items.length < TRANSCRIPT_HISTORY_PAGE_SIZE) break;\n\n // Some older servers omit cursors for filtered searches. A timestamp\n // fallback is safe to attempt only when an independent event count can\n // prove completeness; otherwise fail instead of exporting a partial tail.\n if (expectedEventCount === undefined) {\n throw new Error(\n \"Transcript history pagination cannot prove that all events were loaded.\",\n );\n }\n if (!pageOldestTimestamp) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n if (\n oldestTimestamp &&\n (!addedEvent || pageOldestTimestamp >= oldestTimestamp)\n ) {\n throw new Error(\"Transcript history pagination did not advance.\");\n }\n\n pageId = undefined;\n oldestTimestamp = pageOldestTimestamp;\n usingTimestampFallback = true;\n }\n\n const eventsById = new Map<string, OpenHandsEvent>();\n persistedDescending\n .slice()\n .reverse()\n .forEach((event) => {\n if (!eventsById.has(event.id)) eventsById.set(event.id, event);\n });\n loadedEvents.forEach((event) => {\n if (!eventsById.has(event.id)) eventsById.set(event.id, event);\n });\n // Array.prototype.sort is stable, so equal-timestamp events keep the causal\n // order returned by the server/store rather than being reordered by id.\n const completeEvents = [...eventsById.values()].sort(compareEventTimestamps);\n if (\n expectedEventCount !== undefined &&\n fetchedEventIds.size < expectedEventCount\n ) {\n throw new Error(\n `Transcript history is incomplete: expected ${expectedEventCount} persisted events, received ${fetchedEventIds.size}.`,\n );\n }\n return completeEvents;\n};\n"],"mappings":"AAqBA,IAAM,KACJ,GACA,MACW,EAAM,UAAU,cAAc,EAAO,UAAU,EAQ/C,IAA+B,OAC1C,GACA,GACA,MAC8B;CAC9B,IAAM,IAAwC,EAAE,EAC1C,oBAAkB,IAAI,KAAa,EACnC,oBAAc,IAAI,KAAa,EACjC,GACA,GACA,IAAa,IACb,IAAyB;AAE7B,UAAa;EACX,IAAM,IAAO,MAAM,EAAa;GAC9B,OAAA;GACA,WAAW;GACX,kBAAkB;GAClB,GAAI,IAAS,EAAE,WAAQ,GAAG,EAAE;GAC5B,GAAI,IAAkB,EAAE,aAAa,GAAiB,GAAG,EAAE;GAC5D,CAAC;AAEF,MAAI,CAAC,MAAM,QAAQ,EAAK,MAAM,CAC5B,OAAU,MACR,2EACD;AAGH,IAAoB,KAAK,GAAG,EAAK,MAAM;EACvC,IAAI,GACA,IAAa;AAWjB,MAVA,EAAK,MAAM,SAAS,MAAU;AAK5B,GAJK,EAAgB,IAAI,EAAM,GAAG,KAChC,EAAgB,IAAI,EAAM,GAAG,EAC7B,IAAa,MAEX,CAAC,KAAuB,EAAM,YAAY,OAC5C,IAAsB,EAAM;IAE9B,EAEE,EAAK,cAAc;AACrB,OAAI,EAAY,IAAI,EAAK,aAAa,CACpC,OAAU,MACR,wDACD;AAKH,GAHA,EAAY,IAAI,EAAK,aAAa,EAClC,IAAS,EAAK,cACd,IAAkB,KAAA,GAClB,IAAa;AACb;;AAMF,MADI,KAAc,CAAC,KACf,EAAK,MAAM,SAAA,IAAuC;AAKtD,MAAI,MAAuB,KAAA,EACzB,OAAU,MACR,0EACD;AAKH,MAHI,CAAC,KAIH,MACC,CAAC,KAAc,KAAuB,GAEvC,OAAU,MAAM,iDAAiD;AAKnE,EAFA,IAAS,KAAA,GACT,IAAkB,GAClB,IAAyB;;CAG3B,IAAM,oBAAa,IAAI,KAA6B;AAOpD,CANA,EACG,OAAO,CACP,SAAS,CACT,SAAS,MAAU;AAClB,EAAK,EAAW,IAAI,EAAM,GAAG,IAAE,EAAW,IAAI,EAAM,IAAI,EAAM;GAC9D,EACJ,EAAa,SAAS,MAAU;AAC9B,EAAK,EAAW,IAAI,EAAM,GAAG,IAAE,EAAW,IAAI,EAAM,IAAI,EAAM;GAC9D;CAGF,IAAM,IAAiB,CAAC,GAAG,EAAW,QAAQ,CAAC,CAAC,KAAK,EAAuB;AAC5E,KACE,MAAuB,KAAA,KACvB,EAAgB,OAAO,EAEvB,OAAU,MACR,8CAA8C,EAAmB,8BAA8B,EAAgB,KAAK,GACrH;AAEH,QAAO"}
|
package/package.json
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*
|
|
20
20
|
* Usage:
|
|
21
21
|
* node scripts/check-sdk-version-sync.mjs
|
|
22
|
-
* EXPECTED_SDK_VERSION=1.
|
|
22
|
+
* EXPECTED_SDK_VERSION=1.35.0 node scripts/check-sdk-version-sync.mjs
|
|
23
23
|
* node scripts/check-sdk-version-sync.mjs --check-pypi
|
|
24
24
|
*
|
|
25
25
|
* Environment variables:
|
|
@@ -78,7 +78,7 @@ Triggering from other repos:
|
|
|
78
78
|
-H "Authorization: token \$GITHUB_TOKEN" \\
|
|
79
79
|
-H "Accept: application/vnd.github.v3+json" \\
|
|
80
80
|
https://api.github.com/repos/OpenHands/agent-canvas/dispatches \\
|
|
81
|
-
-d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.
|
|
81
|
+
-d '{"event_type": "sdk-version-check", "client_payload": {"version": "1.35.0"}}'
|
|
82
82
|
`);
|
|
83
83
|
process.exit(0);
|
|
84
84
|
}
|
|
@@ -260,9 +260,9 @@ async function fetchPyPIDependencies(packageName, version) {
|
|
|
260
260
|
* Parse PyPI requires_dist array and extract SDK package versions
|
|
261
261
|
*
|
|
262
262
|
* PyPI returns dependencies in PEP 508 format like:
|
|
263
|
-
* "openhands-sdk>=1.
|
|
264
|
-
* "openhands-tools==1.
|
|
265
|
-
* "openhands-workspace (>=1.
|
|
263
|
+
* "openhands-sdk>=1.35.0,<2.0.0"
|
|
264
|
+
* "openhands-tools==1.35.0"
|
|
265
|
+
* "openhands-workspace (>=1.35.0)"
|
|
266
266
|
*/
|
|
267
267
|
function parseSdkVersionsFromRequiresDist(requiresDist) {
|
|
268
268
|
const versions = {};
|
|
@@ -276,7 +276,7 @@ function parseSdkVersionsFromRequiresDist(requiresDist) {
|
|
|
276
276
|
}
|
|
277
277
|
|
|
278
278
|
// Extract the version number - look for patterns like:
|
|
279
|
-
// ">=1.
|
|
279
|
+
// ">=1.35.0", "==1.35.0", "(>=1.35.0)", "~=1.35.0"
|
|
280
280
|
// After the package name and before any comma or closing paren
|
|
281
281
|
const versionPattern = /[><=~!]+\s*([0-9]+(?:\.[0-9]+)*)/;
|
|
282
282
|
const match = dep.match(versionPattern);
|