@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.
Files changed (326) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{Trans-De9RwQhA.js → Trans-Ch-OVWEA.js} +1 -1
  4. package/build/assets/{acp-providers-D_D8VUpP.js → acp-providers-Sy2LKyMa.js} +1 -1
  5. package/build/assets/active-backend-context-hxq31W25.js +1 -0
  6. package/build/assets/{add-backend-modal-DIEyAkA4.js → add-backend-modal-Bgy8anoR.js} +1 -1
  7. package/build/assets/{agent-profiles-settings-BovdV4N8.js → agent-profiles-settings-3mmXljZX.js} +1 -1
  8. package/build/assets/{agent-server-conversation-service.api-BP73yONA.js → agent-server-conversation-service.api-CuSdr4Qb.js} +4 -4
  9. package/build/assets/{agent-settings-Ofbh6UTD.js → agent-settings-B-W10B4E.js} +1 -1
  10. package/build/assets/agent-settings-D_yOy8XB.js +1 -0
  11. package/build/assets/{alert-banner-BW0wRYw6.js → alert-banner-Bpf2NQJq.js} +1 -1
  12. package/build/assets/{api-key-entry-screen-CZIdRmUJ.js → api-key-entry-screen-Dl0qVYi4.js} +1 -1
  13. package/build/assets/{app-settings-BHJ9nKti.js → app-settings-yVizGXox.js} +1 -1
  14. package/build/assets/automation-detail-dvJfLs5u.js +2 -0
  15. package/build/assets/automation-export-BPcgLqPD.js +2 -0
  16. package/build/assets/automations-list-CUXogTe0.js +1 -0
  17. package/build/assets/{back-nav-button-BYmxFxGt.js → back-nav-button-CUS2mi-U.js} +1 -1
  18. package/build/assets/backend-form-modal-CQJGmItq.js +1 -0
  19. package/build/assets/{backend-synced-settings-badge-Bs3FSXsS.js → backend-synced-settings-badge-D9hZx05w.js} +1 -1
  20. package/build/assets/{base-modal-CC5Pp8CF.js → base-modal-BuF2f0YL.js} +1 -1
  21. package/build/assets/{brand-button-DKPMEKcW.js → brand-button-vMVuyDXu.js} +1 -1
  22. package/build/assets/{browser-B39N74jU.js → browser-BifzWIBd.js} +1 -1
  23. package/build/assets/{browser-tab-aiDg7aEn.js → browser-tab-CkkqJnqw.js} +1 -1
  24. package/build/assets/{chat-send-button-DxuZIoW8.js → chat-send-button-B_jc5z5C.js} +1 -1
  25. package/build/assets/{circle-plus-check-toggle-T3eAIuPQ.js → circle-plus-check-toggle-C5YxjxvI.js} +1 -1
  26. package/build/assets/{combobox-caret-CLGvOs32.js → combobox-caret-DoObzqf4.js} +1 -1
  27. package/build/assets/{command-menu-DKrLCRla.js → command-menu-D-JV3Wqm.js} +1 -1
  28. package/build/assets/{condenser-settings-D_5vY4m3.js → condenser-settings-Ca8fYZ_Z.js} +1 -1
  29. package/build/assets/{confirmation-modal-BK1F0A68.js → confirmation-modal-BGLu6XJN.js} +1 -1
  30. package/build/assets/{context-menu-list-item-DBFzplWW.js → context-menu-list-item-BD5_EaKN.js} +1 -1
  31. package/build/assets/conversation-9NBSRoR9.js +1 -0
  32. package/build/assets/conversation-BXg6Oti2.js +89 -0
  33. package/build/assets/conversation-panel-CZri9Rup.js +1 -0
  34. package/build/assets/{conversation-service.api-CqZcHr1c.js → conversation-service.api-DPBQvTKD.js} +1 -1
  35. package/build/assets/{conversation-tab-empty-state-m8IUkfMj.js → conversation-tab-empty-state-B2dDYEwf.js} +1 -1
  36. package/build/assets/{copy-to-clipboard-button-Aj9wFmt2.js → copy-to-clipboard-button-CaR52OnA.js} +1 -1
  37. package/build/assets/declaration-gq8V5eTU.js +1 -0
  38. package/build/assets/{device-verify-B5YCrvSu.js → device-verify-Dj6I4Iqe.js} +1 -1
  39. package/build/assets/{dropdown-classes-CgQ45JmQ.js → dropdown-classes-DJSXeLqc.js} +1 -1
  40. package/build/assets/{ellipsis-button-1BV9Sga8.js → ellipsis-button-C5Z-tPFp.js} +1 -1
  41. package/build/assets/{entry.client-Dy--3GeA.js → entry.client-2_3ePAm0.js} +2 -2
  42. package/build/assets/{enum-filter-dropdown-DCITiYVi.js → enum-filter-dropdown-DXbVwiNZ.js} +1 -1
  43. package/build/assets/{environment-switch-overlay-DUnKILNH.js → environment-switch-overlay-DZQfRGTx.js} +1 -1
  44. package/build/assets/{extensions-hub-CksKNpP9.js → extensions-hub-CLjmOGqS.js} +1 -1
  45. package/build/assets/{extensions-navigation-Di9rJ-b5.js → extensions-navigation-B5MeUiMq.js} +1 -1
  46. package/build/assets/files-tab-UVoray88.js +1 -0
  47. package/build/assets/{form-control-classes-9rkU4usZ.js → form-control-classes-ChpNBe9n.js} +1 -1
  48. package/build/assets/{git-provider-icon-aQRcUVI-.js → git-provider-icon-o6hdnJUB.js} +1 -1
  49. package/build/assets/home-D1IHvGTN.js +1 -0
  50. package/build/assets/{install-server-modal-BRab9inF.js → install-server-modal-BbuQaA2w.js} +1 -1
  51. package/build/assets/{launch-1TJRKkkX.js → launch-V_3Rp7WS.js} +1 -1
  52. package/build/assets/lesson-plan-jC0HBoe6.js +4 -0
  53. package/build/assets/{llm-not-configured-banner-BrU5uHto.js → llm-not-configured-banner-DoARbcdL.js} +4 -4
  54. package/build/assets/{llm-settings-BpdU51Jo.js → llm-settings-4JMLV8mq.js} +1 -1
  55. package/build/assets/llm-settings-D91gMw3U.js +1 -0
  56. package/build/assets/{loading-spinner-DHAIPZkP.js → loading-spinner-BLdswi6q.js} +1 -1
  57. package/build/assets/{manage-backends-modal-BREySP-J.js → manage-backends-modal-DkWAMSle.js} +1 -1
  58. package/build/assets/manifest-aa5c29f6.js +1 -0
  59. package/build/assets/{markdown-renderer-D8mayGjI.js → markdown-renderer-DMT7Zkpd.js} +1 -1
  60. package/build/assets/{mcp-XXf8k6mb.js → mcp-BQufNPZ6.js} +1 -1
  61. package/build/assets/{messages-D2oZb47Z.js → messages-BnJCqnFf.js} +11 -11
  62. package/build/assets/{modal-body-Dcu5McCa.js → modal-body-DouOE5KH.js} +1 -1
  63. package/build/assets/{modal-classes-DPqxSyuP.js → modal-classes-Cf-4tbHz.js} +1 -1
  64. package/build/assets/{modal-close-button-CTCX4nCH.js → modal-close-button-B6CfxxJj.js} +1 -1
  65. package/build/assets/{model-selector-VDHBytb6.js → model-selector-BfqpMkSJ.js} +1 -1
  66. package/build/assets/onboarding-Bu-HN1ze.js +1 -0
  67. package/build/assets/{onboarding-modal-C084vkv7.js → onboarding-modal-CZgjs5Bx.js} +1 -1
  68. package/build/assets/{option-service.api-Cl5VLGyw.js → option-service.api-BrZ5q87N.js} +1 -1
  69. package/build/assets/{organization-service.api-CnynNoT-.js → organization-service.api-Bk4pCGU1.js} +1 -1
  70. package/build/assets/{path-utils-BeOmjIcE.js → path-utils-CSmuZoix.js} +1 -1
  71. package/build/assets/{plan-components-j9X4AxYM.js → plan-components-CcCBmCzW.js} +1 -1
  72. package/build/assets/{planner-tab-AZMjJx31.js → planner-tab-Dhyqcqmb.js} +1 -1
  73. package/build/assets/{plugins-management-service-BAp70KN7.js → plugins-management-service-BRBLxvUl.js} +1 -1
  74. package/build/assets/{profiles-service.api-CeYEmSM3.js → profiles-service.api-BMXQ9pZC.js} +1 -1
  75. package/build/assets/providers-BpO967xS.js +1 -0
  76. package/build/assets/{proxy-CHmFkV16.js → proxy-BgJLtxu7.js} +1 -1
  77. package/build/assets/{recommended-automations-launcher-Bk8tQ-Cn.js → recommended-automations-launcher-CTcGTl4d.js} +1 -1
  78. package/build/assets/root-BM5WgQ4_.js +2 -0
  79. package/build/assets/root-V8fkplMy.css +1 -0
  80. package/build/assets/root-layout-BdoyoobY.js +2 -0
  81. package/build/assets/{runtime-waiting-state-DkzLgvMn.js → runtime-waiting-state-BsXLElvN.js} +1 -1
  82. package/build/assets/{schema-field-nvq_Juo2.js → schema-field-BSHrUMme.js} +1 -1
  83. package/build/assets/{sdk-section-page-CgT70FaZ.js → sdk-section-page-t82gsOqa.js} +1 -1
  84. package/build/assets/{sdk-settings-schema-BjBkMHzZ.js → sdk-settings-schema-Cv7M9eSe.js} +1 -1
  85. package/build/assets/{secrets-service-CZYjNxFw.js → secrets-service-B2GqPV0N.js} +1 -1
  86. package/build/assets/{secrets-settings-BCxk9p9s.js → secrets-settings-Cx6yJlyb.js} +1 -1
  87. package/build/assets/{segmented-toggle-Cen5AMUc.js → segmented-toggle-DyQqPhoD.js} +1 -1
  88. package/build/assets/{settings-CedTdtb5.js → settings-DmPLkgq0.js} +1 -1
  89. package/build/assets/{settings-dropdown-input-xzoZwgUe.js → settings-dropdown-input-BjunGXJb.js} +1 -1
  90. package/build/assets/{settings-index-Cct3PJSP.js → settings-index-D6sxWSRk.js} +1 -1
  91. package/build/assets/{settings-input-DYs7t3oK.js → settings-input-bD6npsDm.js} +1 -1
  92. package/build/assets/{settings-list-classes-DvQr5jVS.js → settings-list-classes-Dp_OgzN0.js} +1 -1
  93. package/build/assets/{settings-modal-DwLybV_O.js → settings-modal-CwrZKrTq.js} +1 -1
  94. package/build/assets/settings-service.api-Ce-ub11I.js +1 -0
  95. package/build/assets/{settings-switch-C2N2xqEb.js → settings-switch-5cuxl8Dc.js} +1 -1
  96. package/build/assets/{shared-conversation-DnDwfLgg.js → shared-conversation-BDREQPnE.js} +1 -1
  97. package/build/assets/{sidebar-layout-BtpUSN3j.js → sidebar-layout-DuzJMMw-.js} +1 -1
  98. package/build/assets/{sidebar-mobile-menu-toggle-B8k3qu84.js → sidebar-mobile-menu-toggle-VvvX32iF.js} +1 -1
  99. package/build/assets/{sidebar-nav-link-CIB3NBRW.js → sidebar-nav-link-5AlTT5-j.js} +1 -1
  100. package/build/assets/{skill-card-pill-row-D2bUe2kB.js → skill-card-pill-row-eTIkE4nz.js} +1 -1
  101. package/build/assets/{skills-plugins-B68ay6Pu.js → skills-plugins-2Y44YHbO.js} +1 -1
  102. package/build/assets/{skills-settings-A6sUy0T7.js → skills-settings-BUv04tSc.js} +1 -1
  103. package/build/assets/{styled-tooltip-DsKsFjuq.js → styled-tooltip-CgX2oRaK.js} +1 -1
  104. package/build/assets/{suspense-X5dBsJzn.js → suspense-BO1Tl_Vf.js} +1 -1
  105. package/build/assets/{task-list-tab-BAThr9Ex.js → task-list-tab-0k9PhuqO.js} +1 -1
  106. package/build/assets/{terminal-BmT0_gIH.js → terminal-B61WrJdS.js} +1 -1
  107. package/build/assets/{toggle-switch-DBlOOzBr.js → toggle-switch-2j5wwR85.js} +1 -1
  108. package/build/assets/{typography-YGPBGzOG.js → typography-D8mHDeNG.js} +1 -1
  109. package/build/assets/{use-acp-credential-form-Bs6s0DTU.js → use-acp-credential-form-MzZM6D7D.js} +1 -1
  110. package/build/assets/{use-activate-agent-profile-CKtTToSx.js → use-activate-agent-profile-20v1xU0H.js} +1 -1
  111. package/build/assets/{use-active-agent-profile-CWLPpOtK.js → use-active-agent-profile-BkJN7Xsy.js} +1 -1
  112. package/build/assets/{use-active-conversation-DI5S5M1w.js → use-active-conversation-DOZVSVtU.js} +1 -1
  113. package/build/assets/{use-agent-profiles-DPcaYLpm.js → use-agent-profiles-DwZxnwO5.js} +1 -1
  114. package/build/assets/{use-agent-state-Buh3bQDh.js → use-agent-state-DHZO1obC.js} +1 -1
  115. package/build/assets/{use-backends-health-CxkvJy2N.js → use-backends-health-CRHzuvxO.js} +1 -1
  116. package/build/assets/{use-can-manage-org-profiles-DeX2Mfje.js → use-can-manage-org-profiles-C-OhWVBS.js} +1 -1
  117. package/build/assets/{use-cloud-current-user-id-D_PQGfmT.js → use-cloud-current-user-id-utFreJ0i.js} +1 -1
  118. package/build/assets/use-config-mJrtZHP1.js +1 -0
  119. package/build/assets/{use-create-conversation-D9OWYcte.js → use-create-conversation-DdqsP78F.js} +1 -1
  120. package/build/assets/{use-create-secret-CVFSlhlR.js → use-create-secret-D0HCOZFu.js} +1 -1
  121. package/build/assets/{use-event-store-YSOxy9FC.js → use-event-store-DjgU4wfO.js} +1 -1
  122. package/build/assets/{use-handle-plan-click-Dug8egQl.js → use-handle-plan-click-DyRASKMC.js} +1 -1
  123. package/build/assets/use-is-authed-B6M0mcGL.js +1 -0
  124. package/build/assets/use-llm-profiles-RC-PkpBl.js +1 -0
  125. package/build/assets/{use-plugins-marketplace-BGb-KgfD.js → use-plugins-marketplace-FHlbeQ4c.js} +1 -1
  126. package/build/assets/use-runtime-is-ready-RKj2WmEh.js +1 -0
  127. package/build/assets/use-save-agent-profile-tvDP15mh.js +1 -0
  128. package/build/assets/{use-save-settings-CChDyjOB.js → use-save-settings-BGlSfuYn.js} +1 -1
  129. package/build/assets/{use-settings-dX4fKUPF.js → use-settings-CH5LzXD8.js} +1 -1
  130. package/build/assets/{use-settings-nav-items-D6pHnvxg.js → use-settings-nav-items-Dwktv8wo.js} +1 -1
  131. package/build/assets/{use-skills-Dqo-pDDx.js → use-skills-CSzSL6HG.js} +1 -1
  132. package/build/assets/{use-task-list-CmWGElgb.js → use-task-list-AVBLbbcR.js} +1 -1
  133. package/build/assets/{use-tracking-GtXCHmTv.js → use-tracking-BIXzagSI.js} +1 -1
  134. package/build/assets/{use-user-conversation-6uOXFP95.js → use-user-conversation-zpx7I18x.js} +1 -1
  135. package/build/assets/{useMutation-DCE9RcgK.js → useMutation-DrCAVg5w.js} +1 -1
  136. package/build/assets/{useMutationState-BUab8Kxr.js → useMutationState-D8QN0ZCy.js} +1 -1
  137. package/build/assets/{useQueries-CRzaAqzH.js → useQueries-DClgUTc7.js} +1 -1
  138. package/build/assets/{useQuery-CpjHYtZc.js → useQuery-CRBP8vIS.js} +1 -1
  139. package/build/assets/{useTranslation-Cgy4JA_g.js → useTranslation-CDRCKC9-.js} +1 -1
  140. package/build/assets/{utils-CE0lDWtb.js → utils-D3U3rlqr.js} +1 -1
  141. 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
  142. 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
  143. 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
  144. package/build/assets/{verification-settings-D_zsNB_c.js → verification-settings-D576N2xa.js} +1 -1
  145. package/build/index.html +4 -4
  146. package/build/locales/ar/openhands.json +24 -0
  147. package/build/locales/ca/openhands.json +24 -0
  148. package/build/locales/de/openhands.json +24 -0
  149. package/build/locales/en/openhands.json +25 -1
  150. package/build/locales/es/openhands.json +24 -0
  151. package/build/locales/fr/openhands.json +24 -0
  152. package/build/locales/it/openhands.json +24 -0
  153. package/build/locales/ja/openhands.json +24 -0
  154. package/build/locales/ko-KR/openhands.json +24 -0
  155. package/build/locales/no/openhands.json +24 -0
  156. package/build/locales/pt/openhands.json +24 -0
  157. package/build/locales/tr/openhands.json +24 -0
  158. package/build/locales/uk/openhands.json +24 -0
  159. package/build/locales/zh-CN/openhands.json +24 -0
  160. package/build/locales/zh-TW/openhands.json +24 -0
  161. package/config/defaults.json +4 -4
  162. package/dist/api/automation-service/automation-service.api.d.ts +2 -1
  163. package/dist/api/event-service/event-service.api.cjs +1 -1
  164. package/dist/api/event-service/event-service.api.cjs.map +1 -1
  165. package/dist/api/event-service/event-service.api.js +1 -1
  166. package/dist/api/event-service/event-service.api.js.map +1 -1
  167. package/dist/api/event-service/event-service.types.d.ts +6 -0
  168. package/dist/api/git-service/agent-server-git-service.api.cjs +1 -1
  169. package/dist/api/git-service/agent-server-git-service.api.cjs.map +1 -1
  170. package/dist/api/git-service/agent-server-git-service.api.js +2 -2
  171. package/dist/api/git-service/agent-server-git-service.api.js.map +1 -1
  172. package/dist/api/git-service/git-service.api.cjs +1 -1
  173. package/dist/api/git-service/git-service.api.cjs.map +1 -1
  174. package/dist/api/git-service/git-service.api.js +2 -2
  175. package/dist/api/git-service/git-service.api.js.map +1 -1
  176. package/dist/components/conversation-events/chat/event-thought-helpers.cjs +1 -1
  177. package/dist/components/conversation-events/chat/event-thought-helpers.js +1 -1
  178. package/dist/components/features/automations/automation-card.d.ts +2 -1
  179. package/dist/components/features/automations/automation-group.d.ts +2 -1
  180. package/dist/components/features/automations/automation-list-row.d.ts +2 -1
  181. package/dist/components/features/automations/build-automation-menu-items.d.ts +2 -1
  182. package/dist/components/features/automations/detail/detail-header.d.ts +3 -1
  183. package/dist/components/features/automations/import-automation-modal.d.ts +10 -0
  184. package/dist/components/features/backends/backend-form-modal.cjs +1 -1
  185. package/dist/components/features/backends/backend-form-modal.cjs.map +1 -1
  186. package/dist/components/features/backends/backend-form-modal.d.ts +3 -1
  187. package/dist/components/features/backends/backend-form-modal.js +17 -16
  188. package/dist/components/features/backends/backend-form-modal.js.map +1 -1
  189. package/dist/components/features/conversation/conversation-name-context-menu.cjs +1 -1
  190. package/dist/components/features/conversation/conversation-name-context-menu.cjs.map +1 -1
  191. package/dist/components/features/conversation/conversation-name-context-menu.d.ts +2 -1
  192. package/dist/components/features/conversation/conversation-name-context-menu.js +74 -63
  193. package/dist/components/features/conversation/conversation-name-context-menu.js.map +1 -1
  194. package/dist/components/features/conversation/conversation-name.cjs +1 -1
  195. package/dist/components/features/conversation/conversation-name.cjs.map +1 -1
  196. package/dist/components/features/conversation/conversation-name.js +73 -61
  197. package/dist/components/features/conversation/conversation-name.js.map +1 -1
  198. package/dist/components/features/conversation/transcript-export-modal.cjs +2 -0
  199. package/dist/components/features/conversation/transcript-export-modal.cjs.map +1 -0
  200. package/dist/components/features/conversation/transcript-export-modal.d.ts +10 -0
  201. package/dist/components/features/conversation/transcript-export-modal.js +140 -0
  202. package/dist/components/features/conversation/transcript-export-modal.js.map +1 -0
  203. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
  204. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
  205. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +1 -1
  206. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
  207. package/dist/components/features/home/shared/dropdown-item.cjs +1 -1
  208. package/dist/components/features/home/shared/dropdown-item.cjs.map +1 -1
  209. package/dist/components/features/home/shared/dropdown-item.d.ts +7 -1
  210. package/dist/components/features/home/shared/dropdown-item.js +2 -1
  211. package/dist/components/features/home/shared/dropdown-item.js.map +1 -1
  212. package/dist/components/features/home/shared/generic-dropdown-menu.cjs +1 -1
  213. package/dist/components/features/home/shared/generic-dropdown-menu.cjs.map +1 -1
  214. package/dist/components/features/home/shared/generic-dropdown-menu.d.ts +8 -1
  215. package/dist/components/features/home/shared/generic-dropdown-menu.js +18 -10
  216. package/dist/components/features/home/shared/generic-dropdown-menu.js.map +1 -1
  217. package/dist/components/features/home/workspace-dropdown/workspace-dropdown.d.ts +11 -2
  218. package/dist/config/defaults.cjs +1 -1
  219. package/dist/config/defaults.cjs.map +1 -1
  220. package/dist/config/defaults.js +4 -4
  221. package/dist/config/defaults.js.map +1 -1
  222. package/dist/hooks/query/use-automations.d.ts +2 -1
  223. package/dist/hooks/query/use-resolved-workspaces.cjs +1 -1
  224. package/dist/hooks/query/use-resolved-workspaces.cjs.map +1 -1
  225. package/dist/hooks/query/use-resolved-workspaces.d.ts +9 -1
  226. package/dist/hooks/query/use-resolved-workspaces.js +1 -0
  227. package/dist/hooks/query/use-resolved-workspaces.js.map +1 -1
  228. package/dist/hooks/query/use-workspace-file-content.cjs +1 -1
  229. package/dist/hooks/query/use-workspace-file-content.cjs.map +1 -1
  230. package/dist/hooks/query/use-workspace-file-content.js +61 -55
  231. package/dist/hooks/query/use-workspace-file-content.js.map +1 -1
  232. package/dist/hooks/query/use-workspace-files.cjs +1 -1
  233. package/dist/hooks/query/use-workspace-files.cjs.map +1 -1
  234. package/dist/hooks/query/use-workspace-files.d.ts +11 -4
  235. package/dist/hooks/query/use-workspace-files.js +39 -18
  236. package/dist/hooks/query/use-workspace-files.js.map +1 -1
  237. package/dist/hooks/use-auto-refresh-files-on-edit.cjs +1 -1
  238. package/dist/hooks/use-auto-refresh-files-on-edit.cjs.map +1 -1
  239. package/dist/hooks/use-auto-refresh-files-on-edit.d.ts +9 -0
  240. package/dist/hooks/use-auto-refresh-files-on-edit.js +15 -11
  241. package/dist/hooks/use-auto-refresh-files-on-edit.js.map +1 -1
  242. package/dist/hooks/use-tracking.cjs +1 -1
  243. package/dist/hooks/use-tracking.cjs.map +1 -1
  244. package/dist/hooks/use-tracking.d.ts +7 -0
  245. package/dist/hooks/use-tracking.js +9 -0
  246. package/dist/hooks/use-tracking.js.map +1 -1
  247. package/dist/i18n/declaration.cjs +1 -1
  248. package/dist/i18n/declaration.cjs.map +1 -1
  249. package/dist/i18n/declaration.d.ts +24 -0
  250. package/dist/i18n/declaration.js +1 -1
  251. package/dist/i18n/declaration.js.map +1 -1
  252. package/dist/i18n/translation.cjs +2 -2
  253. package/dist/i18n/translation.cjs.map +1 -1
  254. package/dist/i18n/translation.js +409 -1
  255. package/dist/i18n/translation.js.map +1 -1
  256. package/dist/locales/ar/openhands.json +24 -0
  257. package/dist/locales/ca/openhands.json +24 -0
  258. package/dist/locales/de/openhands.json +24 -0
  259. package/dist/locales/en/openhands.json +25 -1
  260. package/dist/locales/es/openhands.json +24 -0
  261. package/dist/locales/fr/openhands.json +24 -0
  262. package/dist/locales/it/openhands.json +24 -0
  263. package/dist/locales/ja/openhands.json +24 -0
  264. package/dist/locales/ko-KR/openhands.json +24 -0
  265. package/dist/locales/no/openhands.json +24 -0
  266. package/dist/locales/pt/openhands.json +24 -0
  267. package/dist/locales/tr/openhands.json +24 -0
  268. package/dist/locales/uk/openhands.json +24 -0
  269. package/dist/locales/zh-CN/openhands.json +24 -0
  270. package/dist/locales/zh-TW/openhands.json +24 -0
  271. package/dist/package.cjs +1 -1
  272. package/dist/package.cjs.map +1 -1
  273. package/dist/package.js +1 -1
  274. package/dist/package.js.map +1 -1
  275. package/dist/routes/files-tab.cjs +1 -1
  276. package/dist/routes/files-tab.js +10 -10
  277. package/dist/types/automation.d.ts +8 -0
  278. package/dist/utils/custom-toast-handlers.cjs.map +1 -1
  279. package/dist/utils/custom-toast-handlers.d.ts +1 -0
  280. package/dist/utils/custom-toast-handlers.js.map +1 -1
  281. package/dist/utils/mcp-server-name.cjs +1 -1
  282. package/dist/utils/mcp-server-name.cjs.map +1 -1
  283. package/dist/utils/mcp-server-name.js +2 -2
  284. package/dist/utils/mcp-server-name.js.map +1 -1
  285. package/dist/utils/transcript-export/index.cjs +72 -0
  286. package/dist/utils/transcript-export/index.cjs.map +1 -0
  287. package/dist/utils/transcript-export/index.d.ts +10 -0
  288. package/dist/utils/transcript-export/index.js +302 -0
  289. package/dist/utils/transcript-export/index.js.map +1 -0
  290. package/dist/utils/transcript-export/load-complete-events.cjs +2 -0
  291. package/dist/utils/transcript-export/load-complete-events.cjs.map +1 -0
  292. package/dist/utils/transcript-export/load-complete-events.d.ts +22 -0
  293. package/dist/utils/transcript-export/load-complete-events.js +39 -0
  294. package/dist/utils/transcript-export/load-complete-events.js.map +1 -0
  295. package/package.json +1 -1
  296. package/scripts/check-sdk-version-sync.mjs +6 -6
  297. package/scripts/dev-safe.mjs +2 -2
  298. package/build/assets/active-backend-context-u2tdYRgH.js +0 -1
  299. package/build/assets/agent-settings-FGL3Yvkp.js +0 -1
  300. package/build/assets/automation-detail-DwYGzLkp.js +0 -2
  301. package/build/assets/automations-list-CuAfJmMW.js +0 -1
  302. package/build/assets/backend-form-modal-BiaOB2C4.js +0 -1
  303. package/build/assets/conversation-DnVC6vRV.js +0 -1
  304. package/build/assets/conversation-panel-ZEz-z3az.js +0 -1
  305. package/build/assets/conversation-xkBS3AHC.js +0 -19
  306. package/build/assets/declaration-B9kpCaYl.js +0 -1
  307. package/build/assets/edit-automation-modal-B6U96gVV.js +0 -1
  308. package/build/assets/files-tab-61Vaihc6.js +0 -1
  309. package/build/assets/home-5gOtUe-n.js +0 -1
  310. package/build/assets/lesson-plan-BtrZUCuj.js +0 -4
  311. package/build/assets/llm-settings-DsInf6yD.js +0 -1
  312. package/build/assets/manifest-a5bb7568.js +0 -1
  313. package/build/assets/onboarding-D32kiGME.js +0 -1
  314. package/build/assets/providers-vX9jk9Vy.js +0 -1
  315. package/build/assets/root-CxWZUXTQ.css +0 -1
  316. package/build/assets/root-layout-CoWw4kpz.js +0 -2
  317. package/build/assets/root-tuEPDx-i.js +0 -2
  318. package/build/assets/settings-service.api-DldkFI1x.js +0 -1
  319. package/build/assets/use-config-ur0RbjCP.js +0 -1
  320. package/build/assets/use-is-authed-_dhQ2Xuj.js +0 -1
  321. package/build/assets/use-llm-profiles-DjackuP1.js +0 -1
  322. package/build/assets/use-runtime-is-ready-Diiq65Q-.js +0 -1
  323. package/build/assets/use-save-agent-profile-aKKLqDJM.js +0 -1
  324. /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
  325. /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
  326. /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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\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,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("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;").replaceAll("'", "&#039;"), k = (e) => e.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;").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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\n .replaceAll('\"', \"&quot;\")\n .replaceAll(\"'\", \"&#039;\");\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(\"&\", \"&amp;\")\n .replaceAll(\"<\", \"&lt;\")\n .replaceAll(\">\", \"&gt;\")\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":";;;;;;;;;;;;AAuEA,IAAM,IAA2B,IAAI,IAAI;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,EAEI,IAAgC,IAAI,IAAI;CAC5C;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC,EAEI,KAAmB,MACvB,EACG,QAAQ,YAAY,IAAI,CACxB,QAAQ,QAAQ,IAAI,CACpB,MAAM,EAEL,KAAY,GAAe,IAAY,QAC3C,EAAM,SAAS,IAAY,GAAG,EAAM,MAAM,GAAG,EAAU,CAAC,KAAK,GAEzD,KAAmB,MAA8B;CACrD,IAAM,IAAO,IAAI,KAAK,EAAU;AAChC,QAAO,OAAO,MAAM,EAAK,SAAS,CAAC,GAAG,IAAY,EAAK,aAAa;GAGhE,KAAc,MAClB,EACG,WAAW,KAAK,QAAQ,CACxB,WAAW,KAAK,OAAO,CACvB,WAAW,KAAK,OAAO,CACvB,WAAW,MAAK,SAAS,CACzB,WAAW,KAAK,SAAS,EAMxB,KAAwB,MAC5B,EACG,WAAW,KAAK,QAAQ,CACxB,WAAW,KAAK,OAAO,CACvB,WAAW,KAAK,OAAO,CACvB,QACC,iEACA,iBACD,CACA,QACC,kEACA,gBACD,EAEC,KAAa,MACjB,EAAgB,KAAS,EAAK,EAAE,EAAQ,gCAAgC,CAAC,IACzE,EAAK,EAAE,EAAQ,gCAAgC,EAE3C,KACJ,GACA,MAEA,EAAgB,EAAK,EAAE,GAAK,EAAO,CAAC,QAAQ,sBAAsB,GAAG,CAAC,EAElE,KAA2B,MAC/B,+BAA+B,KAAK,EAAQ,EAExC,KAAqB,MACzB,EACG,QAAQ,2BAA2B,GAAG,CACtC,QAAQ,mBAAmB,QAAQ,CACnC,MAAM,EAEL,KAAoB,MAA+B;CACvD,IAAM,IAAU,EAAgB,EAAM,WAAW,GAAG;AAEpD,QADI,KAAW,CAAC,EAAwB,EAAQ,GAAS,IAEvD,EAAgB,EAAM,UAAU,IAAI,EAAkB,EAAM,OAAO,KAAK;GAItE,KACJ,GACA,MAEI,IAA4B,EAAiB,EAAoB,GAEnE,EAAgB,EAAM,UAAU,IAChC,EAAkB,EAAM,YAAY,KAAK,EAIvC,KACJ,MAEA,EACG,QACE,MACC,EAAK,SAAS,UAAU,OAAO,EAAK,QAAS,SAChD,CACA,KAAK,MAAS,EAAK,KAAK,CACxB,KAAK,KAAK,EAET,KAAwB,MAC5B,CACE,GAAG,EAAK,EAAE,EAAQ,cAAc,CAAC,IAAI,EAAO,iBAC5C,GAAG,EAAK,EAAE,EAAQ,WAAW,CAAC,KAAK,EAAO,SAC3C,CAAC,KAAK,OAAO,EAEV,KACJ,GACA,MACW;CACX,IAAM,IACJ,GAAqB,OAAO,SAAS,eACjC,EAAoB,SACpB,KAAA;AACN,QAAO;EACL,GAAG,EAAK,EAAE,EAAQ,cAAc,CAAC,IAAI,EAAY;EACjD,GAAG,EAAK,EAAE,EAAQ,aAAa,CAAC,IAAI,EAAY;EAChD,IAAa,GAAG,EAAK,EAAE,EAAQ,WAAW,CAAC,KAAK,EAAW,WAAW;EACtE,GAAG,EAAK,EAAE,EAAQ,YAAY,CAAC,KAAK,EAAe,EAAY,QAAQ;EACxE,CACE,OAAO,QAAQ,CACf,KAAK,OAAO;GAGX,KAAwB,MACxB,EAAM,OAAO,SAAS,eACjB,EAAqB,EAAM,OAAO,GAEpC,EAAyB,IAAI,EAAM,OAAO,KAAK,GAClD,EAAiB,EAAM,GACvB,IAGA,KACJ,GACA,MAEI,EAAM,YAAY,SAAS,oBACtB,EAA0B,EAAM,aAAa,EAAoB,GAEnE,EAA8B,IAAI,EAAM,YAAY,KAAK,GAC5D,EAAsB,EAAM,GAC5B,IAGA,KAAkB,MACjB,EAAqB,EAAM,GACzB;CAAC,EAAM;CAAQ,EAAM;CAAO,EAAM;CAAQ,EAAM;CAAO,CAC3D,QAAQ,MAA2B,EAAQ,GAAO,MAAM,CAAE,CAC1D,KAAK,OAAO,GAH0B,IAMrC,KAAsB,MAC1B,CAAC,EAAoB,EAAM,EAAE,EAAqB,EAAM,CAAC,CACtD,KAAK,MAAY,EAAQ,MAAM,CAAC,CAChC,OAAO,QAAQ,CACf,KAAK,OAAO,EAEX,KAA4B,MAChC,CAAC;CAAC;CAAgB;CAAmB;CAAc,CAAC,SAClD,EAAM,OAAO,KACd,EAEG,KACJ,GACA,MACsB;CACtB,IAAM,IAAW,EAAO,QAA0B,GAAS,MAAU;AACnE,MAAI;AACF,UAAO,EAAiB,GAAO,EAAQ;UACjC;AACN,UAAO;;IAER,EAAE,CAAC,EACA,IAAc,IAAI,IACtB,EAAO,OAAO,EAAc,CAAC,KAAK,MAAU,CAAC,EAAM,IAAI,EAAM,CAAC,CAC/D,EACK,IAA6B,EAAE,EAC/B,oBAA4B,IAAI,KAAa,EAM7C,IAAgB,EALG,EAAS,QAC/B,MACE,EAA4B,EAAM,IAAI,CAAC,EAAM,YAAY,YAC1D,EAAkB,EAAM,CAG1B,eAEA,EACD,EAEK,KAAsB,MAAwB;AAClD,MACE,EAA0B,IAAI,EAAO,GAAG,IACxC,CAAC,EAAyB,EAAO,CAEjC;AAEF,IAA0B,IAAI,EAAO,GAAG;EACxC,IAAM,IAAU,EAAmB,EAAO;AAC1C,EAAI,KACF,EAAQ,KAAK;GACX,MAAM;GACN,QAAQ;GACR;GACA,WAAW,EAAO;GACnB,CAAC;;AAIN,MAAK,IAAM,KAAQ,GAAe;AAChC,MAAI,EAAK,SAAS,QAAS;AAC3B,MAAI,EAAK,SAAS,WAAW;AAC3B,KAAmB,EAAK,OAAO;AAC/B;;EAGF,IAAM,EAAE,aAAU;AAClB,MAAI;GACF,IAAM,IAAkB,EAAc,EAAM,GACxC,IACA,EAAmB,EAAM,GACvB,EAAY,IAAI,EAAM,UAAU,GAChC,KAAA;AAGN,OAFI,KAAiB,EAAmB,EAAgB,EAEpD,EAA4B,EAAM,IAAI,CAAC,EAAM,YAAY,UAAU;AACrE,MAAQ,KAAK;KACX,MAAM;KACN,SAAS,EAAe,EAAQ,2BAA2B,EACzD,MAAM,EAAM,YAAY,cACzB,CAAC;KACF,SAAS,CACP,EAAM,YAAY,eACd,GAAG,EAAK,EAAE,EAAQ,wBAAwB,CAAC,IAAI,EAAM,YAAY,iBACjE,IACJ,EAAM,YAAY,SACd,GAAG,EAAK,EAAE,EAAQ,yBAAyB,CAAC,IAAI,EAAM,YAAY,WAClE,GACL,CACE,OAAO,QAAQ,CACf,KAAK,KAAK;KACb,WAAW,EAAM;KAClB,CAAC;AACF;;AAGF,OAAI,EAAe,EAAM,EAAE;IACzB,IAAM,IAAS,EAAsB,EAAM,CAAC,MAAM;AAClD,QAAI,EAAM,WAAW,SAAS;KAC5B,IAAM,EAAE,cAAW,eAAY,EAAiB,EAAO;AACvD,MAAC,GAAW,EAAQ,CAAC,OAAO,QAAQ,CAAC,SAAS,MAAY;AACxD,QAAQ,KAAK;OACX,MAAM;OACN,QAAQ;OACR;OACA,WAAW,EAAM;OAClB,CAAC;OACF;WACO,KACT,EAAQ,KAAK;KACX,MAAM;KACN,QAAQ;KACR,SAAS;KACT,WAAW,EAAM;KAClB,CAAC;AAEJ;;AAGF,OAAI,EAAsB,EAAM,EAAE;IAChC,IAAM,EAAE,WAAW,GAAa,eAAY,EAC1C,EAAM,WAAW,IACjB,EAAE,WAAW,IAAM,CACpB,EACK,IAAmB,CACvB,EAAM,mBAAmB,MAAM,IAAI,IACnC,EACD,CACE,OAAO,QAAQ,CACf,KAAK,OAAO;AASf,IARI,KACF,EAAQ,KAAK;KACX,MAAM;KACN,QAAQ;KACR,SAAS;KACT,WAAW,EAAM;KAClB,CAAC,EAEA,EAAQ,MAAM,IAChB,EAAQ,KAAK;KACX,MAAM;KACN,QAAQ;KACR,SAAS,EAAQ,MAAM;KACvB,WAAW,EAAM;KAClB,CAAC;AAEJ;;AAGF,OAAI,EAAkB,EAAM,EAAE;AAC5B,MAAQ,KAAK;KACX,MAAM;KACN,SAAS,EAAM;KACf,WAAW,EAAM;KAClB,CAAC;AACF;;AAGF,OAAI,EAAc,EAAM,EAAE;AACxB,QAAI,EAAM,OAAO,SAAS,gBAAgB;KACxC,IAAM,IAAU,EAAM,OAAO,QAAQ,MAAM;AAC3C,KAAI,KACF,EAAQ,KAAK;MACX,MAAM;MACN,QAAQ;MACR;MACA,WAAW,EAAM;MAClB,CAAC;UAGJ,GAAQ,KAAK;KACX,MAAM;KACN,SAAS,EAAiB,EAAM;KAChC,SAAS,IAAqB,EAAqB,EAAM,GAAG;KAC5D,WAAW,EAAM;KAClB,CAAC;AAEJ;;AAGF,OAAI,EAAmB,EAAM,EAAE;IAC7B,IAAM,IAAsB,EAAY,IAAI,EAAM,UAAU;AAC5D,MAAQ,KAAK;KACX,MAAM;KACN,SAAS,EAAsB,GAAO,EAAoB;KAC1D,SAAS,IACL,EAA0B,GAAO,EAAoB,GACrD;KACJ,WAAW,EAAM;KAClB,CAAC;AACF;;AAGF,OAAI,EAAmB,EAAM,EAAE;AAC7B,MAAQ,KAAK;KACX,MAAM;KACN,SACE,EAA0B,EAAM,IAChC,EAAK,EAAE,EAAQ,wBAAwB;KACzC,SAAS,IAAqB,EAAsB,EAAM,GAAG;KAC7D,WAAW,EAAM;KAClB,CAAC;AACF;;AAGF,OAAI,EAAqB,EAAM,EAAE;AAC/B,MAAQ,KAAK;KACX,MAAM;KACN,SAAS,EAAK,EAAE,EAAQ,wBAAwB,EAC9C,SAAS,EAAS,EAAgB,EAAM,aAAa,EAAE,IAAI,EAC5D,CAAC;KACF,SAAS,IAAqB,EAAe,EAAM,GAAG;KACtD,WAAW,EAAM;KAClB,CAAC;AACF;;AAGF,GACE,EAA+B,EAAM,IACrC,EAAmC,EAAM,IAEzC,EAAQ,KAAK;IACX,MAAM;IACN,SAAS,GAAG,EAAK,EAAE,EAAQ,YAAY,CAAC,GAAG,EAAK,EAC9C;KACE,SAAS,EAAQ;KACjB,UAAU,EAAQ;KAClB,QAAQ,EAAQ;KAChB,aAAa,EAAQ;KACtB,CAAC,EAAM,MAAM,QACf;IACD,SAAS,CAAC,EAAM,MAAM,WAAW,EAAM,MAAM,SAAS,WAAW,GAAG,CACjE,OAAO,QAAQ,CACf,KAAK,OAAO;IACf,WAAW,EAAM;IAClB,CAAC;UAEE;;AAMV,QAAO;GAGH,KACJ,GACA,MAEA,EAAQ,oBACJ,QAAQ,EAAW,EAAgB,EAAM,UAAU,CAAC,CAAC,cACrD,IAEA,KAAiB,MAA4B;CACjD,IAAM,IAAa,KAAK,IACtB,GACA,GAAG,MAAM,KAAK,EAAQ,SAAS,MAAM,GAAG,MAAU,EAAM,GAAG,OAAO,CACnE,EACK,IAAQ,IAAI,OAAO,KAAK,IAAI,GAAG,IAAa,EAAE,CAAC;AACrD,QAAO,GAAG,EAAM,QAAQ,EAAQ,IAAI;GAGzB,KACX,GACA,MACW;CACX,IAAM,IAAQ,CAAC,KAAK,EAAqB,EAAU,EAAQ,MAAM,CAAC,IAAI,GAAG;AAEzE,CAAI,EAAQ,SACV,EAAM,KACJ,KAAK,EAAK,EAAE,EAAQ,wBAAwB,CAAC,MAAM,EAAqB,EAAgB,EAAQ,MAAM,CAAC,IACvG,GACD;AAGH,MAAK,IAAM,KAAS,EAClB,GACA,EAAQ,mBACT,EAAE;EACD,IAAM,IAAY,EAAkB,GAAO,EAAQ;AACnD,MAAI,EAAM,SAAS,WAAW;GAC5B,IAAM,IACJ,EAAM,WAAW,SACb,EAAK,EAAE,EAAQ,uBAAuB,GACtC,EAAK,EAAE,EAAQ,yBAAyB;AAC9C,KAAM,KACJ,MAAM,KACN,IACA,IAAY,EAAqB,EAAM,QAAQ,EAC/C,GACD;aACQ,EAAM,SAAS,SAAS;GACjC,IAAM,IAAS,EAAM,QAClB,MAAM,KAAK,CACX,KAAK,MAAS,KAAK,EAAqB,EAAK,GAAG,CAChD,KAAK,KAAK;AACb,KAAM,KACJ,MAAM,EAAK,EAAE,EAAQ,aAAa,IAClC,IACA,IAAY,GACZ,GACD;aACQ,EAAM,SAAS,OACxB,GAAM,KACJ,OAAO,EAAW,EAAM,QAAQ,CAAC,KACjC,IACA,IAAY,EAAqB,EAAM,QAAQ,EAC/C,GACD;WACQ,EAAQ,sBAAsB,EAAM,QAC7C,GAAM,KACJ,aACA,oBAAoB,EAAW,EAAK,EAAE,EAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,CAAC,aAC9G,IACA,IAAY,EAAc,EAAM,QAAQ,EACxC,IACA,cACA,GACD;OACI;GACL,IAAM,IAAkB,IAAY,OAAO,EAAU,SAAS,KAAK;AACnE,KAAM,KACJ,cAAc,EAAW,EAAK,EAAE,EAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAgB,OAC1H,GACD;;;AAIL,QAAO,GAAG,EAAM,KAAK,KAAK,CAAC,MAAM,CAAC;GAG9B,KACJ,GACA,MACW;AACX,KAAI,CAAC,EAAQ,kBAAmB,QAAO;CACvC,IAAM,IAAY,EAAgB,EAAM,UAAU;AAClD,QAAO,mBAAmB,EAAW,EAAU,CAAC,IAAI,EAAW,EAAU,CAAC;GAG/D,KACX,GACA,MACW;CACX,IAAM,IAAQ,EAAU,EAAQ,MAAM,EAChC,IAAO,EAAuB,GAAQ,EAAQ,mBAAmB,CACpE,KAAK,MAAU;EACd,IAAM,IAAY,EAAc,GAAO,EAAQ;AAC/C,MAAI,EAAM,SAAS,WAAW;GAC5B,IAAM,IACJ,EAAM,WAAW,SACb,EAAK,EAAE,EAAQ,uBAAuB,GACtC,EAAK,EAAE,EAAQ,yBAAyB;AAC9C,UAAO,2BAA2B,EAAM,OAAO,aAAa,CAAC;gBACrD,EAAW,EAAO,CAAC,OAAO,EAAU;yBAC3B,EAAW,EAAM,QAAQ,CAAC;;;AAsB7C,SAnBI,EAAM,SAAS,UACV;gBACC,EAAW,EAAK,EAAE,EAAQ,aAAa,CAAC,CAAC,OAAO,EAAU;yBACjD,EAAW,EAAM,QAAQ,CAAC;cAGzC,EAAM,SAAS,SACV;oBACK,EAAW,EAAM,QAAQ,CAAC,WAAW,EAAU;IAC/D,EAAM,UAAU,wBAAwB,EAAW,EAAM,QAAQ,CAAC,UAAU,GAAG;YAI3E,EAAQ,sBAAsB,EAAM,UAChC;qBACS,EAAW,EAAK,EAAE,EAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAU;SAClH,EAAW,EAAM,QAAQ,CAAC;cAEvB,qCAAqC,EAAW,EAAK,EAAE,EAAQ,uBAAuB,CAAC,CAAC,aAAa,EAAW,EAAM,QAAQ,GAAG,EAAU;GAEjJ,CACD,KAAK,KAAK,EAEP,IAAQ,EAAQ,QAClB,4BAA4B,EAAW,EAAK,EAAE,EAAQ,wBAAwB,CAAC,CAAC,aAAa,EAAW,EAAgB,EAAQ,MAAM,CAAC,CAAC,QACxI;AAEJ,QAAO;cACK,EAAW,EAAK,oBAAoB,EAAK,YAAY,KAAK,CAAC;;;;;WAK9D,EAAW,EAAM,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;UA4BnB,EAAW,EAAM,CAAC;MACtB,EAAM;MACN,EAAK"}
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openhands/agent-canvas",
3
- "version": "1.2.1",
3
+ "version": "1.4.0",
4
4
  "description": "Agent Canvas UI for OpenHands - run AI coding agents with a visual interface",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * Usage:
21
21
  * node scripts/check-sdk-version-sync.mjs
22
- * EXPECTED_SDK_VERSION=1.33.0 node scripts/check-sdk-version-sync.mjs
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.33.0"}}'
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.33.0,<2.0.0"
264
- * "openhands-tools==1.33.0"
265
- * "openhands-workspace (>=1.33.0)"
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.33.0", "==1.33.0", "(>=1.33.0)", "~=1.33.0"
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);