@openhands/agent-canvas 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (899) hide show
  1. package/README.md +1 -1
  2. package/README.windows.md +2 -2
  3. package/build/assets/{acp-providers-BkL3HWa3.js → acp-providers-CYy8qA2z.js} +1 -1
  4. package/build/assets/active-backend-context-BZDG15C0.js +2 -0
  5. package/build/assets/{agent-context-settings-CzWS1DgR.js → agent-context-settings-DGWBBbcp.js} +1 -1
  6. package/build/assets/agent-profiles-settings-DrkGdf4_.js +1 -0
  7. package/build/assets/agent-server-conversation-service.api-DK56YPTs.js +86 -0
  8. package/build/assets/{agent-settings-D2J1dKVd.js → agent-settings-DfcSgkLD.js} +2 -2
  9. package/build/assets/agent-settings-Lrc5-twS.js +1 -0
  10. package/build/assets/{alert-banner-lrK-2_0Y.js → alert-banner-BgDHhL7b.js} +1 -1
  11. package/build/assets/api-error-message-BEIE_yD1.js +1 -0
  12. package/build/assets/{api-key-entry-screen-D70oJj2c.js → api-key-entry-screen-DaZnOlVo.js} +1 -1
  13. package/build/assets/{app-settings-D0OKHB3M.js → app-settings-BG5NAUbC.js} +1 -1
  14. package/build/assets/automation-detail-D4Hs831Z.js +3 -0
  15. package/build/assets/automation-export-n0X6FB0C.js +2 -0
  16. package/build/assets/automation-git-sync-CDrm8WV7.js +1 -0
  17. package/build/assets/automation-setup-route-CZCl4ONs.js +1 -0
  18. package/build/assets/{automation-templates-D9gs6ihu.js → automation-templates-DFF96AdL.js} +1 -1
  19. package/build/assets/automations-list-Bl-oGOrV.js +1 -0
  20. package/build/assets/{back-nav-button-B3pYwmUJ.js → back-nav-button-H_ThtDA0.js} +1 -1
  21. package/build/assets/{backend-form-modal-HwclKrUS.js → backend-form-modal-CbCemvHt.js} +1 -1
  22. package/build/assets/backend-not-configured-DP8KLNq_.js +1 -0
  23. package/build/assets/{backend-synced-settings-badge-YI3k09Wr.js → backend-synced-settings-badge-BBASWqsb.js} +1 -1
  24. package/build/assets/{badge-Dpn3-LTV.js → badge-B-51h-SW.js} +1 -1
  25. package/build/assets/{base-modal-3NmIOKDm.js → base-modal-BbjycA8X.js} +1 -1
  26. package/build/assets/bot-CGPCg03p.js +1 -0
  27. package/build/assets/{brand-button-CJcwjnfb.js → brand-button-Blxv5nww.js} +1 -1
  28. package/build/assets/browser-CuW0nWvh.js +7 -0
  29. package/build/assets/{browser-tab-4CPdj3J3.js → browser-tab-D5G0ORwt.js} +1 -1
  30. package/build/assets/{chat-send-button-B8n6-DQA.js → chat-send-button-BD2tii1r.js} +1 -1
  31. package/build/assets/check-DSuS-lyr.js +1 -0
  32. package/build/assets/chevron-down-CgGcmtE2.js +1 -0
  33. package/build/assets/circle-check-CG5-NPw7.js +1 -0
  34. package/build/assets/{circle-plus-check-toggle-BVzv5EkZ.js → circle-plus-check-toggle-ASbpB8vN.js} +1 -1
  35. package/build/assets/cloud-funnel-analytics-GlCUG0gk.js +1 -0
  36. package/build/assets/cog-CZiR80uT.js +1 -0
  37. package/build/assets/{combobox-caret-BlmRwiM8.js → combobox-caret-DvpEsh2O.js} +1 -1
  38. package/build/assets/{command-menu-BKOLj-TC.js → command-menu-DwUQzjYP.js} +1 -1
  39. package/build/assets/{condenser-settings-DLQogFnB.js → condenser-settings-BptcoiLA.js} +1 -1
  40. package/build/assets/{confirmation-modal-DAH0XtOp.js → confirmation-modal-CiEwxZAA.js} +1 -1
  41. package/build/assets/{context-menu-list-item-BQhmFVEK.js → context-menu-list-item-CdcamfM1.js} +1 -1
  42. package/build/assets/conversation-DREHj4nT.js +1 -0
  43. package/build/assets/conversation-DotRfiDz.js +90 -0
  44. package/build/assets/conversation-panel-DREHj4nT.js +1 -0
  45. package/build/assets/conversation-state-store-DMLtlHbE.js +1 -0
  46. package/build/assets/conversation-store-Blc5xysY.js +6 -0
  47. package/build/assets/{conversation-tab-empty-state-C9sfDOlt.js → conversation-tab-empty-state-D-puECpY.js} +1 -1
  48. package/build/assets/{copy-to-clipboard-button-DatoElj8.js → copy-to-clipboard-button-BGxcUbgy.js} +1 -1
  49. package/build/assets/{device-verify-BIjXeW9N.js → device-verify-CZJfWaJf.js} +1 -1
  50. package/build/assets/{dropdown-classes-zFW7qPSK.js → dropdown-classes-tLhpwtxS.js} +1 -1
  51. package/build/assets/{entry.client-EBn3ZtNP.js → entry.client-D8GnI6GZ.js} +2 -2
  52. package/build/assets/{enum-filter-dropdown-CctILg5t.js → enum-filter-dropdown-Fk8k60nk.js} +1 -1
  53. package/build/assets/{environment-switch-overlay-Cvmo6clk.js → environment-switch-overlay-DeztVYuL.js} +1 -1
  54. package/build/assets/{extensions-hub-BZ3HYztk.js → extensions-hub-Cd6GCYzq.js} +1 -1
  55. package/build/assets/{extensions-navigation-Bnx32WAb.js → extensions-navigation-C4sgZg-y.js} +1 -1
  56. package/build/assets/external-link-C0UpBgsE.js +1 -0
  57. package/build/assets/{file-text-RecuaId_.js → file-text-C6WYtNdV.js} +1 -1
  58. package/build/assets/files-tab-BW_qzyAm.js +1 -0
  59. package/build/assets/{form-control-classes-DLN3Qgub.js → form-control-classes-CGXBLcn1.js} +1 -1
  60. package/build/assets/format-model-name-BNPKVsIq.js +1 -0
  61. package/build/assets/format-relative-time-ZhQD4rsl.js +1 -0
  62. package/build/assets/git-provider-icon-CaQ1yLPi.js +1 -0
  63. package/build/assets/{git-repo-dropdown-B5VnMftu.js → git-repo-dropdown-CgXR6M_E.js} +1 -1
  64. package/build/assets/git-repo-dropdown-D2Uei3CO.js +1 -0
  65. package/build/assets/{highlighted-source-view-CgJfD5VT.js → highlighted-source-view-C_YOp8Fo.js} +1 -1
  66. package/build/assets/home-DNzzJcMP.js +1 -0
  67. package/build/assets/home-aCos-5vc.js +1 -0
  68. package/build/assets/{home-store-Cubdq0uP.js → home-store-DgzViC3f.js} +1 -1
  69. package/build/assets/index-home-DNzzJcMP.js +1 -0
  70. package/build/assets/info-CLoC1LRo.js +1 -0
  71. package/build/assets/install-server-modal-D7zIqNKz.js +1 -0
  72. package/build/assets/kebab-menu-item-content-DERMdnDv.js +1 -0
  73. package/build/assets/{launch-c4GEP8oo.js → launch-5PjKeLak.js} +1 -1
  74. package/build/assets/{list-todo-CheUtB7F.js → list-todo-BF049YaY.js} +1 -1
  75. package/build/assets/llm-not-configured-banner-BK9z5eS0.js +27 -0
  76. package/build/assets/llm-settings-BLAMK2tR.js +1 -0
  77. package/build/assets/llm-settings-BWsbarGM.js +1 -0
  78. package/build/assets/llm-subscription-service-SfnEV9Uc.js +1 -0
  79. package/build/assets/loader-circle-D7IagAyF.js +1 -0
  80. package/build/assets/{loading-spinner-CN3riY7k.js → loading-spinner-DBgXgra0.js} +1 -1
  81. package/build/assets/{manage-backends-modal-DTCME_Pu.js → manage-backends-modal-BTqNPhzc.js} +1 -1
  82. package/build/assets/manifest-abebca3c.js +1 -0
  83. package/build/assets/manifest-subpage-layout-Rn-glaxV.js +1 -0
  84. package/build/assets/{map-provider-7Bp2aih5.js → map-provider-BK4HnqYz.js} +1 -1
  85. package/build/assets/markdown-renderer-Cp6DlHQN.js +8 -0
  86. package/build/assets/{mcp-Da7wFU7A.js → mcp-BC-Ixn4T.js} +2 -2
  87. package/build/assets/mcp-marketplace-utils-BJWJgbB6.js +1 -0
  88. package/build/assets/messages-CzFKrzPG.js +40 -0
  89. package/build/assets/{modal-body-duRaxj_K.js → modal-body-BdKfD3Hl.js} +1 -1
  90. package/build/assets/{modal-classes-CLEXO6dj.js → modal-classes-D5_b5vMi.js} +1 -1
  91. package/build/assets/{modal-close-button-D0aT02hs.js → modal-close-button-objukxk_.js} +1 -1
  92. package/build/assets/{mouse-pointer-click-Px-dXWcS.js → mouse-pointer-click-C_gXyLyD.js} +1 -1
  93. package/build/assets/{npm-Cswluf6-.js → npm-CHVHV1MC.js} +1 -1
  94. package/build/assets/{onboarding-BKbTYlW1.js → onboarding-Diw0V51A.js} +1 -1
  95. package/build/assets/onboarding-modal-Da4yHzsA.js +1 -0
  96. package/build/assets/{organization-service.api-DK8csGvK.js → organization-service.api-BztSy69u.js} +1 -1
  97. package/build/assets/parse-git-remote-url-BkXN9jzl.js +1 -0
  98. package/build/assets/pencil-49avMHlD.js +1 -0
  99. package/build/assets/{planner-tab-D-mZ1I7n.js → planner-tab-CJiSpNax.js} +1 -1
  100. package/build/assets/{plugins-management-service-CywqyfLK.js → plugins-management-service-lHdNKWNU.js} +1 -1
  101. package/build/assets/plus-C0BagBpV.js +1 -0
  102. package/build/assets/profiles-service.api-BLr7FXec.js +1 -0
  103. package/build/assets/providers-CWXnb7S3.js +1 -0
  104. package/build/assets/{proxy-b4aqL4SX.js → proxy-DYDXxtje.js} +1 -1
  105. package/build/assets/{puzzle-B3v361yY.js → puzzle-Bt2B1rzf.js} +1 -1
  106. package/build/assets/recommended-automations-launcher-CcFMfJRR.js +1 -0
  107. package/build/assets/refresh-cw-nmcesifB.js +1 -0
  108. package/build/assets/root-CT4t7apy.css +1 -0
  109. package/build/assets/root-DnRgLKpU.js +2 -0
  110. package/build/assets/root-layout-2L9tr-BN.js +2 -0
  111. package/build/assets/{schema-field-Z-rMg9ti.js → schema-field-C59lVZTA.js} +1 -1
  112. package/build/assets/{sdk-section-page-m_cjygxR.js → sdk-section-page-BbBEQQjq.js} +1 -1
  113. package/build/assets/search-CYSDBczG.js +1 -0
  114. package/build/assets/secrets-service-BtsgYSlj.js +1 -0
  115. package/build/assets/secrets-settings-DJ0OKa4_.js +1 -0
  116. package/build/assets/{segmented-toggle-SmvsR3jW.js → segmented-toggle-DiVfTZuN.js} +1 -1
  117. package/build/assets/{settings-CZPGNens.js → settings-CDqL_Uqh.js} +1 -1
  118. package/build/assets/{settings-CWFxymAf.js → settings-DcNPxz0v.js} +1 -1
  119. package/build/assets/{settings-dropdown-input-TWIeiG02.js → settings-dropdown-input-Ba1UN5Du.js} +1 -1
  120. package/build/assets/{settings-index-Dtlegd6s.js → settings-index-Cz2cKt2K.js} +1 -1
  121. package/build/assets/{settings-input-D61Vzp9e.js → settings-input-CWUEHHvS.js} +1 -1
  122. package/build/assets/{settings-list-classes-sE0ASr1s.js → settings-list-classes-DdcLXDGn.js} +1 -1
  123. package/build/assets/{settings-modal-CalzJP2l.js → settings-modal-nEEK1Kh1.js} +1 -1
  124. package/build/assets/{settings-service.api-DfPYaxZJ.js → settings-service.api-3yAUFcaP.js} +1 -1
  125. package/build/assets/{settings-switch-Drt-rF5s.js → settings-switch-Dk9hChx1.js} +1 -1
  126. package/build/assets/{settings-utils-gw2ppAvw.js → settings-utils-CxjtCSB3.js} +1 -1
  127. package/build/assets/shared-conversation-DbAcrx3s.js +1 -0
  128. package/build/assets/shield-check-CiOTXxV2.js +1 -0
  129. package/build/assets/{sidebar-layout-Dxg6zc2K.js → sidebar-layout-lrfPooIB.js} +1 -1
  130. package/build/assets/{sidebar-mobile-menu-toggle-C6HpQ55l.js → sidebar-mobile-menu-toggle-KERXfNu1.js} +1 -1
  131. package/build/assets/{sidebar-nav-link-CxLoG-dG.js → sidebar-nav-link-B8lFD6Cb.js} +1 -1
  132. package/build/assets/{sidebar-store-BJeYY-E9.js → sidebar-store-u6pUpxJ7.js} +1 -1
  133. package/build/assets/{skill-card-pill-row-B_BYBONu.js → skill-card-pill-row-dGIUzr_b.js} +1 -1
  134. package/build/assets/{skill-icon-badge-CN41Hy9l.js → skill-icon-badge-kkOWZ89w.js} +1 -1
  135. package/build/assets/{skill-scope-Dt1V5X2p.js → skill-scope-DWN1PBPH.js} +1 -1
  136. package/build/assets/{skills-plugins-B-H0cCOP.js → skills-plugins-DFjH2Nq6.js} +1 -1
  137. package/build/assets/{skills-settings-DthdIDUb.js → skills-settings-DIHpl3ol.js} +2 -2
  138. package/build/assets/{sparkles-g7y3r-Ut.js → sparkles-ntyMLnbj.js} +1 -1
  139. package/build/assets/{styled-tooltip-C5C07RQC.js → styled-tooltip-B6JXcsmL.js} +1 -1
  140. package/build/assets/{task-list-tab-BTde81Iu.js → task-list-tab-aEJzEcHD.js} +1 -1
  141. package/build/assets/{terminal-DRrJo-cR.js → terminal-Dch7Ty5K.js} +1 -1
  142. package/build/assets/{toggle-switch-CIioisto.js → toggle-switch-B2MLKsgt.js} +1 -1
  143. package/build/assets/trash-2-yZnDM4Ji.js +1 -0
  144. package/build/assets/{typography-D9tiHct7.js → typography-DB5HoCk0.js} +1 -1
  145. package/build/assets/usage-tab-DrEtBmBT.js +1 -0
  146. package/build/assets/use-acp-credential-form-c8KXAa8A.js +1 -0
  147. package/build/assets/{use-activate-agent-profile-DrPzIKJF.js → use-activate-agent-profile-B0p34N3q.js} +1 -1
  148. package/build/assets/use-agent-profiles-CPIc6wLH.js +1 -0
  149. package/build/assets/{use-can-manage-org-profiles-BzE28H3x.js → use-can-manage-org-profiles-Dz_8Eqkv.js} +1 -1
  150. package/build/assets/{use-cloud-current-user-id-U6HPjy50.js → use-cloud-current-user-id-DvUVqCle.js} +1 -1
  151. package/build/assets/use-config-C3PO4TBw.js +1 -0
  152. package/build/assets/use-create-conversation-DVrA_uLD.js +1 -0
  153. package/build/assets/use-create-secret-DbBIpaCR.js +1 -0
  154. package/build/assets/use-get-secrets-6PBitf54.js +1 -0
  155. package/build/assets/use-has-permission-CQ31Cl8j.js +1 -0
  156. package/build/assets/use-is-authed-2UrDIziV.js +1 -0
  157. package/build/assets/use-is-creating-conversation-BvYpmb3v.js +1 -0
  158. package/build/assets/{use-launch-skill-in-chat-BcKNjxxd.js → use-launch-skill-in-chat-COnDpxCQ.js} +1 -1
  159. package/build/assets/use-llm-profiles-diZS4FiN.js +1 -0
  160. package/build/assets/use-manifest-capabilities-HIWCtIUm.js +1 -0
  161. package/build/assets/{use-onboarding-completion-6kWOW3ul.js → use-onboarding-completion-Bs8PcXik.js} +1 -1
  162. package/build/assets/{use-plugins-marketplace-CqkR8kLE.js → use-plugins-marketplace-BCuNLFsr.js} +1 -1
  163. package/build/assets/use-save-agent-profile-BaMNSpBV.js +1 -0
  164. package/build/assets/{use-save-settings-DIEurJ3D.js → use-save-settings-BahvC2Gw.js} +1 -1
  165. package/build/assets/{use-settings-BmLzQjnC.js → use-settings-C6ZxpBDX.js} +1 -1
  166. package/build/assets/{use-settings-nav-items-DV9qgeu7.js → use-settings-nav-items-Bqn69Ggw.js} +2 -2
  167. package/build/assets/use-tracking-Bhu7gk2y.js +3 -0
  168. package/build/assets/{use-user-conversation-Dt-W6uNw.js → use-user-conversation-BkNXpXrd.js} +1 -1
  169. package/build/assets/{useInfiniteQuery-DbCv0Jzd.js → useInfiniteQuery-c_OmS3lK.js} +1 -1
  170. package/build/assets/{useMutation-ChpkYxI5.js → useMutation-6ZsQCqAn.js} +1 -1
  171. package/build/assets/{useQueries-Cjv3E8_1.js → useQueries-xDrGlTO_.js} +1 -1
  172. package/build/assets/{useQuery-BCnzy4LX.js → useQuery-Bs5NFz2s.js} +1 -1
  173. package/build/assets/utils-DUD5ScdA.js +1 -0
  174. package/build/assets/{vendor~browser-DJ1pl_wn.js → vendor~browser-B-fae1JU.js} +14 -14
  175. package/build/assets/{vendor~entry.client-CgNUwtQL.js → vendor~entry.client-BFE7Llz9.js} +1 -1
  176. package/build/assets/{vendor~entry.client-zSIea5EO.js → vendor~entry.client-BaKt_PIp.js} +1 -1
  177. package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kv3pap4j-oGvLV0dO.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~kepq2zme-D87mAf6K.js} +3 -3
  178. package/build/assets/{vendor~files-tab-C87pjSpi.js → vendor~files-tab-B6POTS66.js} +1 -1
  179. package/build/assets/vendor~index-home~home~conversation-panel~conversation-53Q_U6uP.js +13 -0
  180. package/build/assets/vendor~root-layout-DeQACFVA.js +1 -0
  181. package/build/assets/{vendor~root-layout~conversation-panel~conversation-DEzjR4K7.js → vendor~root-layout~conversation-panel~conversation-bv7ooVgH.js} +1 -1
  182. package/build/assets/{vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-U-xdsAEx.js → vendor~root-layout~conversation-panel~conversation~skills-settings~automations-list~shared-~bovqqk8e-e8pX05xf.js} +1 -1
  183. package/build/assets/{vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-CQR5Sa0H.js → vendor~root-layout~index-home~home~conversation-panel~conversation~shared-conversation-BvS6PCK2.js} +1 -1
  184. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-CuQhkiS7.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~hjcjabwg-DOPLJMH9.js} +2 -2
  185. package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~gxu4j2ea-Cwhh3zLe.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~extensions-h~iw7io29p-Dz8NQve5.js} +1 -1
  186. package/build/assets/{verification-settings-oi54wEc1.js → verification-settings-COueVXmM.js} +1 -1
  187. package/build/assets/{wrench-B1_H-tL1.js → wrench-C95ujnYo.js} +1 -1
  188. package/build/assets/x-86ZCfxKY.js +1 -0
  189. package/build/assets/zap-DRa-r-Tf.js +1 -0
  190. package/build/index.html +4 -4
  191. package/build/locales/ar/openhands.json +88 -3
  192. package/build/locales/ca/openhands.json +88 -3
  193. package/build/locales/de/openhands.json +88 -3
  194. package/build/locales/en/openhands.json +88 -3
  195. package/build/locales/es/openhands.json +88 -3
  196. package/build/locales/fr/openhands.json +88 -3
  197. package/build/locales/it/openhands.json +88 -3
  198. package/build/locales/ja/openhands.json +88 -3
  199. package/build/locales/ko-KR/openhands.json +88 -3
  200. package/build/locales/no/openhands.json +88 -3
  201. package/build/locales/pt/openhands.json +88 -3
  202. package/build/locales/tr/openhands.json +88 -3
  203. package/build/locales/uk/openhands.json +88 -3
  204. package/build/locales/zh-CN/openhands.json +88 -3
  205. package/build/locales/zh-TW/openhands.json +88 -3
  206. package/config/defaults.json +3 -3
  207. package/dist/api/agent-server-compatibility.cjs +1 -1
  208. package/dist/api/agent-server-compatibility.cjs.map +1 -1
  209. package/dist/api/agent-server-compatibility.js +1 -1
  210. package/dist/api/agent-server-compatibility.js.map +1 -1
  211. package/dist/api/automation-service/automation-service.api.cjs +1 -1
  212. package/dist/api/automation-service/automation-service.api.cjs.map +1 -1
  213. package/dist/api/automation-service/automation-service.api.d.ts +10 -0
  214. package/dist/api/automation-service/automation-service.api.js +42 -0
  215. package/dist/api/automation-service/automation-service.api.js.map +1 -1
  216. package/dist/api/cloud/conversation-service.api.cjs +1 -1
  217. package/dist/api/cloud/conversation-service.api.cjs.map +1 -1
  218. package/dist/api/cloud/conversation-service.api.d.ts +9 -0
  219. package/dist/api/cloud/conversation-service.api.js +11 -1
  220. package/dist/api/cloud/conversation-service.api.js.map +1 -1
  221. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs +1 -1
  222. package/dist/api/conversation-service/agent-server-conversation-service.api.cjs.map +1 -1
  223. package/dist/api/conversation-service/agent-server-conversation-service.api.d.ts +24 -1
  224. package/dist/api/conversation-service/agent-server-conversation-service.api.js +116 -97
  225. package/dist/api/conversation-service/agent-server-conversation-service.api.js.map +1 -1
  226. package/dist/api/llm-balance-service.cjs +2 -0
  227. package/dist/api/llm-balance-service.cjs.map +1 -0
  228. package/dist/api/llm-balance-service.d.ts +33 -0
  229. package/dist/api/llm-balance-service.js +53 -0
  230. package/dist/api/llm-balance-service.js.map +1 -0
  231. package/dist/api/llm-subscription-service.cjs +1 -1
  232. package/dist/api/llm-subscription-service.cjs.map +1 -1
  233. package/dist/api/llm-subscription-service.js +42 -34
  234. package/dist/api/llm-subscription-service.js.map +1 -1
  235. package/dist/api/profiles-service/profiles-service.api.cjs +1 -1
  236. package/dist/api/profiles-service/profiles-service.api.cjs.map +1 -1
  237. package/dist/api/profiles-service/profiles-service.api.d.ts +15 -2
  238. package/dist/api/profiles-service/profiles-service.api.js +25 -2
  239. package/dist/api/profiles-service/profiles-service.api.js.map +1 -1
  240. package/dist/api/secrets-service.cjs +1 -1
  241. package/dist/api/secrets-service.cjs.map +1 -1
  242. package/dist/api/secrets-service.d.ts +5 -0
  243. package/dist/api/secrets-service.js +12 -6
  244. package/dist/api/secrets-service.js.map +1 -1
  245. package/dist/components/conversation/index.cjs +1 -1
  246. package/dist/components/conversation/index.d.ts +0 -1
  247. package/dist/components/conversation/index.js +0 -1
  248. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs +1 -1
  249. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.cjs.map +1 -1
  250. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js +1 -1
  251. package/dist/components/conversation-events/chat/event-content-helpers/get-event-content.js.map +1 -1
  252. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs +1 -1
  253. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.cjs.map +1 -1
  254. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js +24 -23
  255. package/dist/components/conversation-events/chat/event-message-components/generic-event-message-wrapper.js.map +1 -1
  256. package/dist/components/conversation-events/chat/event-message-components/index.d.ts +0 -1
  257. package/dist/components/conversation-events/chat/group-events.cjs +1 -1
  258. package/dist/components/conversation-events/chat/group-events.cjs.map +1 -1
  259. package/dist/components/conversation-events/chat/group-events.d.ts +3 -3
  260. package/dist/components/conversation-events/chat/group-events.js +27 -26
  261. package/dist/components/conversation-events/chat/group-events.js.map +1 -1
  262. package/dist/components/features/automations/git-sync/git-sync-activity-row.d.ts +15 -0
  263. package/dist/components/features/automations/git-sync/git-sync-config-form.d.ts +13 -0
  264. package/dist/components/features/automations/git-sync/git-sync-error-banner.d.ts +6 -0
  265. package/dist/components/features/automations/git-sync/git-sync-not-local-state.d.ts +1 -0
  266. package/dist/components/features/automations/git-sync/git-sync-overview-section.d.ts +12 -0
  267. package/dist/components/features/automations/git-sync/git-sync-skeleton.d.ts +1 -0
  268. package/dist/components/features/automations/git-sync/git-sync-status-pill.d.ts +7 -0
  269. package/dist/components/features/automations/git-sync/git-sync-unsupported-state.d.ts +7 -0
  270. package/dist/components/features/automations/recommended-automations-section.d.ts +7 -0
  271. package/dist/components/features/automations/responder-deployment-modal.d.ts +2 -1
  272. package/dist/components/features/chat/chat-interface.cjs +2 -2
  273. package/dist/components/features/chat/chat-interface.cjs.map +1 -1
  274. package/dist/components/features/chat/chat-interface.js +177 -176
  275. package/dist/components/features/chat/chat-interface.js.map +1 -1
  276. package/dist/components/features/chat/components/chat-input-actions.cjs +1 -1
  277. package/dist/components/features/chat/components/chat-input-actions.cjs.map +1 -1
  278. package/dist/components/features/chat/components/chat-input-actions.js +123 -118
  279. package/dist/components/features/chat/components/chat-input-actions.js.map +1 -1
  280. package/dist/components/features/chat/components/chat-input-grip.cjs +1 -1
  281. package/dist/components/features/chat/components/chat-input-grip.cjs.map +1 -1
  282. package/dist/components/features/chat/components/chat-input-grip.d.ts +2 -1
  283. package/dist/components/features/chat/components/chat-input-grip.js +7 -7
  284. package/dist/components/features/chat/components/chat-input-grip.js.map +1 -1
  285. package/dist/components/features/chat/components/context-window-meter.cjs +2 -0
  286. package/dist/components/features/chat/components/context-window-meter.cjs.map +1 -0
  287. package/dist/components/features/chat/components/context-window-meter.d.ts +2 -0
  288. package/dist/components/features/chat/components/context-window-meter.js +131 -0
  289. package/dist/components/features/chat/components/context-window-meter.js.map +1 -0
  290. package/dist/components/features/chat/components/context-window-ring.cjs +2 -0
  291. package/dist/components/features/chat/components/context-window-ring.cjs.map +1 -0
  292. package/dist/components/features/chat/components/context-window-ring.d.ts +26 -0
  293. package/dist/components/features/chat/components/context-window-ring.js +45 -0
  294. package/dist/components/features/chat/components/context-window-ring.js.map +1 -0
  295. package/dist/components/features/chat/custom-chat-input.cjs +1 -1
  296. package/dist/components/features/chat/custom-chat-input.cjs.map +1 -1
  297. package/dist/components/features/chat/custom-chat-input.js +68 -67
  298. package/dist/components/features/chat/custom-chat-input.js.map +1 -1
  299. package/dist/components/features/chat/error-message-banner.cjs +1 -1
  300. package/dist/components/features/chat/error-message-banner.cjs.map +1 -1
  301. package/dist/components/features/chat/error-message-banner.d.ts +3 -1
  302. package/dist/components/features/chat/error-message-banner.js +64 -58
  303. package/dist/components/features/chat/error-message-banner.js.map +1 -1
  304. package/dist/components/features/chat/path-component.cjs +1 -1
  305. package/dist/components/features/chat/path-component.cjs.map +1 -1
  306. package/dist/components/features/chat/path-component.js +9 -6
  307. package/dist/components/features/chat/path-component.js.map +1 -1
  308. package/dist/components/features/chat/plan-preview.cjs +1 -1
  309. package/dist/components/features/chat/plan-preview.js +4 -4
  310. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs +1 -1
  311. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.cjs.map +1 -1
  312. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.d.ts +0 -8
  313. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js +92 -50
  314. package/dist/components/features/chat/tool-visualizers/file-editor/file-editor.js.map +1 -1
  315. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs +1 -1
  316. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.cjs.map +1 -1
  317. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.d.ts +7 -2
  318. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js +15 -9
  319. package/dist/components/features/chat/tool-visualizers/primitives/file-path-chip.js.map +1 -1
  320. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.cjs +2 -0
  321. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.cjs.map +1 -0
  322. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.d.ts +20 -0
  323. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.js +76 -0
  324. package/dist/components/features/chat/tool-visualizers/primitives/markdown-file-preview.js.map +1 -0
  325. package/dist/components/features/controls/server-status-context-menu.cjs +1 -1
  326. package/dist/components/features/controls/server-status-context-menu.cjs.map +1 -1
  327. package/dist/components/features/controls/server-status-context-menu.d.ts +5 -3
  328. package/dist/components/features/controls/server-status-context-menu.js +8 -8
  329. package/dist/components/features/controls/server-status-context-menu.js.map +1 -1
  330. package/dist/components/features/conversation/conversation-name-context-menu.cjs +1 -1
  331. package/dist/components/features/conversation/conversation-name-context-menu.cjs.map +1 -1
  332. package/dist/components/features/conversation/conversation-name-context-menu.js +58 -61
  333. package/dist/components/features/conversation/conversation-name-context-menu.js.map +1 -1
  334. package/dist/components/features/conversation/conversation-name-with-status.cjs +1 -1
  335. package/dist/components/features/conversation/conversation-name-with-status.cjs.map +1 -1
  336. package/dist/components/features/conversation/conversation-name-with-status.js +74 -46
  337. package/dist/components/features/conversation/conversation-name-with-status.js.map +1 -1
  338. package/dist/components/features/conversation/conversation-name.cjs +1 -1
  339. package/dist/components/features/conversation/conversation-name.cjs.map +1 -1
  340. package/dist/components/features/conversation/conversation-name.js +69 -71
  341. package/dist/components/features/conversation/conversation-name.js.map +1 -1
  342. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.cjs +1 -1
  343. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.cjs.map +1 -1
  344. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.js +7 -6
  345. package/dist/components/features/conversation/conversation-tabs/conversation-tab-content/conversation-tab-content.js.map +1 -1
  346. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs +1 -1
  347. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.cjs.map +1 -1
  348. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js +92 -86
  349. package/dist/components/features/conversation/conversation-tabs/conversation-tabs-context-menu.js.map +1 -1
  350. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs +1 -1
  351. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.cjs.map +1 -1
  352. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js +81 -71
  353. package/dist/components/features/conversation/conversation-tabs/conversation-tabs.js.map +1 -1
  354. package/dist/components/features/conversation/metrics-modal/cost-section.cjs +1 -1
  355. package/dist/components/features/conversation/metrics-modal/cost-section.cjs.map +1 -1
  356. package/dist/components/features/conversation/metrics-modal/cost-section.js +6 -6
  357. package/dist/components/features/conversation/metrics-modal/cost-section.js.map +1 -1
  358. package/dist/components/features/conversation/metrics-modal/metric-row.cjs +1 -1
  359. package/dist/components/features/conversation/metrics-modal/metric-row.cjs.map +1 -1
  360. package/dist/components/features/conversation/metrics-modal/metric-row.d.ts +2 -1
  361. package/dist/components/features/conversation/metrics-modal/metric-row.js +10 -9
  362. package/dist/components/features/conversation/metrics-modal/metric-row.js.map +1 -1
  363. package/dist/components/features/conversation/metrics-modal/usage-section.cjs +1 -1
  364. package/dist/components/features/conversation/metrics-modal/usage-section.cjs.map +1 -1
  365. package/dist/components/features/conversation/metrics-modal/usage-section.js +2 -1
  366. package/dist/components/features/conversation/metrics-modal/usage-section.js.map +1 -1
  367. package/dist/components/features/conversation/usage-panel/compact-context-button.cjs +2 -0
  368. package/dist/components/features/conversation/usage-panel/compact-context-button.cjs.map +1 -0
  369. package/dist/components/features/conversation/usage-panel/compact-context-button.d.ts +16 -0
  370. package/dist/components/features/conversation/usage-panel/compact-context-button.js +59 -0
  371. package/dist/components/features/conversation/usage-panel/compact-context-button.js.map +1 -0
  372. package/dist/components/features/conversation/usage-panel/context-meter.cjs +2 -0
  373. package/dist/components/features/conversation/usage-panel/context-meter.cjs.map +1 -0
  374. package/dist/components/features/conversation/usage-panel/context-meter.d.ts +19 -0
  375. package/dist/components/features/conversation/usage-panel/context-meter.js +46 -0
  376. package/dist/components/features/conversation/usage-panel/context-meter.js.map +1 -0
  377. package/dist/components/features/conversation/usage-panel/provider-balance-card.cjs +2 -0
  378. package/dist/components/features/conversation/usage-panel/provider-balance-card.cjs.map +1 -0
  379. package/dist/components/features/conversation/usage-panel/provider-balance-card.d.ts +7 -0
  380. package/dist/components/features/conversation/usage-panel/provider-balance-card.js +57 -0
  381. package/dist/components/features/conversation/usage-panel/provider-balance-card.js.map +1 -0
  382. package/dist/components/features/conversation/usage-panel/usage-panel.cjs +2 -0
  383. package/dist/components/features/conversation/usage-panel/usage-panel.cjs.map +1 -0
  384. package/dist/components/features/conversation/usage-panel/usage-panel.d.ts +9 -0
  385. package/dist/components/features/conversation/usage-panel/usage-panel.js +68 -0
  386. package/dist/components/features/conversation/usage-panel/usage-panel.js.map +1 -0
  387. package/dist/components/features/conversation-panel/budget-display.cjs +1 -1
  388. package/dist/components/features/conversation-panel/budget-display.cjs.map +1 -1
  389. package/dist/components/features/conversation-panel/budget-display.js +10 -13
  390. package/dist/components/features/conversation-panel/budget-display.js.map +1 -1
  391. package/dist/components/features/conversation-panel/budget-progress-bar.cjs +1 -1
  392. package/dist/components/features/conversation-panel/budget-progress-bar.cjs.map +1 -1
  393. package/dist/components/features/conversation-panel/budget-progress-bar.js +2 -2
  394. package/dist/components/features/conversation-panel/budget-progress-bar.js.map +1 -1
  395. package/dist/components/features/conversation-panel/compact-conversation-row.cjs +1 -1
  396. package/dist/components/features/conversation-panel/compact-conversation-row.js +6 -6
  397. package/dist/components/features/conversation-panel/confirm-archive-modal.cjs +2 -0
  398. package/dist/components/features/conversation-panel/confirm-archive-modal.cjs.map +1 -0
  399. package/dist/components/features/conversation-panel/confirm-archive-modal.d.ts +8 -0
  400. package/dist/components/features/conversation-panel/confirm-archive-modal.js +48 -0
  401. package/dist/components/features/conversation-panel/confirm-archive-modal.js.map +1 -0
  402. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs +1 -1
  403. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.cjs.map +1 -1
  404. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.d.ts +3 -1
  405. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js +39 -34
  406. package/dist/components/features/conversation-panel/conversation-card/conversation-card-actions.js.map +1 -1
  407. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs +1 -1
  408. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.cjs.map +1 -1
  409. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.d.ts +3 -1
  410. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js +104 -80
  411. package/dist/components/features/conversation-panel/conversation-card/conversation-card-context-menu.js.map +1 -1
  412. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs +1 -1
  413. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.cjs.map +1 -1
  414. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.d.ts +7 -1
  415. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js +25 -17
  416. package/dist/components/features/conversation-panel/conversation-card/conversation-card-footer.js.map +1 -1
  417. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs +1 -1
  418. package/dist/components/features/conversation-panel/conversation-card/conversation-card.cjs.map +1 -1
  419. package/dist/components/features/conversation-panel/conversation-card/conversation-card.d.ts +9 -1
  420. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js +94 -84
  421. package/dist/components/features/conversation-panel/conversation-card/conversation-card.js.map +1 -1
  422. package/dist/components/features/conversation-panel/conversation-group-folder-list.cjs +1 -1
  423. package/dist/components/features/conversation-panel/conversation-group-folder-list.cjs.map +1 -1
  424. package/dist/components/features/conversation-panel/conversation-group-folder-list.d.ts +2 -1
  425. package/dist/components/features/conversation-panel/conversation-group-folder-list.js +32 -31
  426. package/dist/components/features/conversation-panel/conversation-group-folder-list.js.map +1 -1
  427. package/dist/components/features/conversation-panel/conversation-group-folder-row.cjs +1 -1
  428. package/dist/components/features/conversation-panel/conversation-group-folder-row.cjs.map +1 -1
  429. package/dist/components/features/conversation-panel/conversation-group-folder-row.d.ts +2 -1
  430. package/dist/components/features/conversation-panel/conversation-group-folder-row.js +27 -26
  431. package/dist/components/features/conversation-panel/conversation-group-folder-row.js.map +1 -1
  432. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs +1 -1
  433. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.cjs.map +1 -1
  434. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.d.ts +3 -1
  435. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js +116 -106
  436. package/dist/components/features/conversation-panel/conversation-panel-filter-menu.js.map +1 -1
  437. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs +1 -1
  438. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.cjs.map +1 -1
  439. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.d.ts +27 -0
  440. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js +69 -46
  441. package/dist/components/features/conversation-panel/conversation-panel-list-helpers.js.map +1 -1
  442. package/dist/components/features/conversation-panel/conversation-panel.cjs +1 -1
  443. package/dist/components/features/conversation-panel/conversation-panel.cjs.map +1 -1
  444. package/dist/components/features/conversation-panel/conversation-panel.js +240 -175
  445. package/dist/components/features/conversation-panel/conversation-panel.js.map +1 -1
  446. package/dist/components/features/files-tab/file-content-viewer.cjs +1 -1
  447. package/dist/components/features/files-tab/file-content-viewer.cjs.map +1 -1
  448. package/dist/components/features/files-tab/file-content-viewer.js +22 -25
  449. package/dist/components/features/files-tab/file-content-viewer.js.map +1 -1
  450. package/dist/components/features/markdown/markdown-table-scroll.cjs +1 -1
  451. package/dist/components/features/markdown/markdown-table-scroll.cjs.map +1 -1
  452. package/dist/components/features/markdown/markdown-table-scroll.js +2 -2
  453. package/dist/components/features/markdown/markdown-table-scroll.js.map +1 -1
  454. package/dist/components/features/onboarding/steps/setup-llm-step.d.ts +10 -7
  455. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs +1 -1
  456. package/dist/components/shared/buttons/conversation-confirmation-buttons.cjs.map +1 -1
  457. package/dist/components/shared/buttons/conversation-confirmation-buttons.js +2 -2
  458. package/dist/components/shared/buttons/conversation-confirmation-buttons.js.map +1 -1
  459. package/dist/components/shared/modals/confirmation-modals/base-modal.cjs +1 -1
  460. package/dist/components/shared/modals/confirmation-modals/base-modal.cjs.map +1 -1
  461. package/dist/components/shared/modals/confirmation-modals/base-modal.js +1 -1
  462. package/dist/components/shared/modals/confirmation-modals/base-modal.js.map +1 -1
  463. package/dist/config/defaults.cjs +1 -1
  464. package/dist/config/defaults.cjs.map +1 -1
  465. package/dist/config/defaults.js +3 -3
  466. package/dist/config/defaults.js.map +1 -1
  467. package/dist/constants/llm-balance.cjs +2 -0
  468. package/dist/constants/llm-balance.cjs.map +1 -0
  469. package/dist/constants/llm-balance.d.ts +13 -0
  470. package/dist/constants/llm-balance.js +6 -0
  471. package/dist/constants/llm-balance.js.map +1 -0
  472. package/dist/contexts/conversation-websocket-context.cjs +2 -2
  473. package/dist/contexts/conversation-websocket-context.cjs.map +1 -1
  474. package/dist/contexts/conversation-websocket-context.js +227 -219
  475. package/dist/contexts/conversation-websocket-context.js.map +1 -1
  476. package/dist/hooks/chat/use-grip-resize.cjs +1 -1
  477. package/dist/hooks/chat/use-grip-resize.cjs.map +1 -1
  478. package/dist/hooks/chat/use-grip-resize.d.ts +1 -0
  479. package/dist/hooks/chat/use-grip-resize.js +39 -30
  480. package/dist/hooks/chat/use-grip-resize.js.map +1 -1
  481. package/dist/hooks/mutation/use-condense-conversation.cjs +2 -0
  482. package/dist/hooks/mutation/use-condense-conversation.cjs.map +1 -0
  483. package/dist/hooks/mutation/use-condense-conversation.d.ts +12 -0
  484. package/dist/hooks/mutation/use-condense-conversation.js +17 -0
  485. package/dist/hooks/mutation/use-condense-conversation.js.map +1 -0
  486. package/dist/hooks/mutation/use-create-conversation.cjs +1 -1
  487. package/dist/hooks/mutation/use-create-conversation.cjs.map +1 -1
  488. package/dist/hooks/mutation/use-create-conversation.js +30 -21
  489. package/dist/hooks/mutation/use-create-conversation.js.map +1 -1
  490. package/dist/hooks/mutation/use-new-conversation-command.cjs +1 -1
  491. package/dist/hooks/mutation/use-new-conversation-command.cjs.map +1 -1
  492. package/dist/hooks/mutation/use-new-conversation-command.js +1 -1
  493. package/dist/hooks/mutation/use-new-conversation-command.js.map +1 -1
  494. package/dist/hooks/query/use-commit-changes.cjs +1 -1
  495. package/dist/hooks/query/use-commit-changes.js +4 -4
  496. package/dist/hooks/query/use-conversation-metrics.cjs +1 -1
  497. package/dist/hooks/query/use-conversation-metrics.cjs.map +1 -1
  498. package/dist/hooks/query/use-conversation-metrics.js +1 -1
  499. package/dist/hooks/query/use-conversation-metrics.js.map +1 -1
  500. package/dist/hooks/query/use-git-sync.d.ts +19 -0
  501. package/dist/hooks/query/use-llm-balance.cjs +2 -0
  502. package/dist/hooks/query/use-llm-balance.cjs.map +1 -0
  503. package/dist/hooks/query/use-llm-balance.d.ts +10 -0
  504. package/dist/hooks/query/use-llm-balance.js +24 -0
  505. package/dist/hooks/query/use-llm-balance.js.map +1 -0
  506. package/dist/hooks/query/use-search-providers.cjs +1 -1
  507. package/dist/hooks/query/use-search-providers.cjs.map +1 -1
  508. package/dist/hooks/query/use-search-providers.js +1 -1
  509. package/dist/hooks/query/use-search-providers.js.map +1 -1
  510. package/dist/hooks/query/use-task-polling.cjs +1 -1
  511. package/dist/hooks/query/use-task-polling.cjs.map +1 -1
  512. package/dist/hooks/query/use-task-polling.js +12 -10
  513. package/dist/hooks/query/use-task-polling.js.map +1 -1
  514. package/dist/hooks/query/use-unified-get-git-changes.cjs +1 -1
  515. package/dist/hooks/query/use-unified-get-git-changes.js +4 -4
  516. package/dist/hooks/query/use-unified-git-commits.cjs +1 -1
  517. package/dist/hooks/query/use-unified-git-commits.js +4 -4
  518. package/dist/hooks/query/use-unified-git-diff.cjs +1 -1
  519. package/dist/hooks/query/use-unified-git-diff.js +4 -4
  520. package/dist/hooks/query/use-workspace-files.cjs +1 -1
  521. package/dist/hooks/query/use-workspace-files.cjs.map +1 -1
  522. package/dist/hooks/query/use-workspace-files.d.ts +14 -4
  523. package/dist/hooks/query/use-workspace-files.js +45 -32
  524. package/dist/hooks/query/use-workspace-files.js.map +1 -1
  525. package/dist/hooks/use-await-context-compaction.cjs +2 -0
  526. package/dist/hooks/use-await-context-compaction.cjs.map +1 -0
  527. package/dist/hooks/use-await-context-compaction.d.ts +38 -0
  528. package/dist/hooks/use-await-context-compaction.js +61 -0
  529. package/dist/hooks/use-await-context-compaction.js.map +1 -0
  530. package/dist/hooks/use-compact-context-action.cjs +2 -0
  531. package/dist/hooks/use-compact-context-action.cjs.map +1 -0
  532. package/dist/hooks/use-compact-context-action.d.ts +10 -0
  533. package/dist/hooks/use-compact-context-action.js +61 -0
  534. package/dist/hooks/use-compact-context-action.js.map +1 -0
  535. package/dist/hooks/use-context-window-usage.cjs +2 -0
  536. package/dist/hooks/use-context-window-usage.cjs.map +1 -0
  537. package/dist/hooks/use-context-window-usage.d.ts +5 -0
  538. package/dist/hooks/use-context-window-usage.js +20 -0
  539. package/dist/hooks/use-context-window-usage.js.map +1 -0
  540. package/dist/hooks/use-conversation-name-context-menu.cjs +1 -1
  541. package/dist/hooks/use-conversation-name-context-menu.cjs.map +1 -1
  542. package/dist/hooks/use-conversation-name-context-menu.d.ts +0 -3
  543. package/dist/hooks/use-conversation-name-context-menu.js +54 -57
  544. package/dist/hooks/use-conversation-name-context-menu.js.map +1 -1
  545. package/dist/hooks/use-drag-resize.cjs +1 -1
  546. package/dist/hooks/use-drag-resize.cjs.map +1 -1
  547. package/dist/hooks/use-drag-resize.d.ts +8 -0
  548. package/dist/hooks/use-drag-resize.js +21 -18
  549. package/dist/hooks/use-drag-resize.js.map +1 -1
  550. package/dist/hooks/use-live-conversation-metrics.cjs +2 -0
  551. package/dist/hooks/use-live-conversation-metrics.cjs.map +1 -0
  552. package/dist/hooks/use-live-conversation-metrics.d.ts +18 -0
  553. package/dist/hooks/use-live-conversation-metrics.js +37 -0
  554. package/dist/hooks/use-live-conversation-metrics.js.map +1 -0
  555. package/dist/hooks/use-responder-url-secret.d.ts +5 -0
  556. package/dist/hooks/use-select-conversation-tab.cjs +1 -1
  557. package/dist/hooks/use-select-conversation-tab.cjs.map +1 -1
  558. package/dist/hooks/use-select-conversation-tab.js +2 -2
  559. package/dist/hooks/use-select-conversation-tab.js.map +1 -1
  560. package/dist/hooks/use-tracking.cjs +1 -1
  561. package/dist/hooks/use-tracking.cjs.map +1 -1
  562. package/dist/hooks/use-tracking.d.ts +6 -0
  563. package/dist/hooks/use-tracking.js +6 -0
  564. package/dist/hooks/use-tracking.js.map +1 -1
  565. package/dist/i18n/declaration.cjs +1 -1
  566. package/dist/i18n/declaration.cjs.map +1 -1
  567. package/dist/i18n/declaration.d.ts +86 -1
  568. package/dist/i18n/declaration.js +1 -1
  569. package/dist/i18n/declaration.js.map +1 -1
  570. package/dist/i18n/translation.cjs +2 -2
  571. package/dist/i18n/translation.cjs.map +1 -1
  572. package/dist/i18n/translation.js +1475 -30
  573. package/dist/i18n/translation.js.map +1 -1
  574. package/dist/index.cjs +1 -1
  575. package/dist/index.js +28 -29
  576. package/dist/lib/index.cjs +1 -1
  577. package/dist/lib/index.js +0 -1
  578. package/dist/locales/ar/openhands.json +88 -3
  579. package/dist/locales/ca/openhands.json +88 -3
  580. package/dist/locales/de/openhands.json +88 -3
  581. package/dist/locales/en/openhands.json +88 -3
  582. package/dist/locales/es/openhands.json +88 -3
  583. package/dist/locales/fr/openhands.json +88 -3
  584. package/dist/locales/it/openhands.json +88 -3
  585. package/dist/locales/ja/openhands.json +88 -3
  586. package/dist/locales/ko-KR/openhands.json +88 -3
  587. package/dist/locales/no/openhands.json +88 -3
  588. package/dist/locales/pt/openhands.json +88 -3
  589. package/dist/locales/tr/openhands.json +88 -3
  590. package/dist/locales/uk/openhands.json +88 -3
  591. package/dist/locales/zh-CN/openhands.json +88 -3
  592. package/dist/locales/zh-TW/openhands.json +88 -3
  593. package/dist/node_modules/@microlink/react-json-view/dist/main.cjs +5 -5
  594. package/dist/node_modules/@microlink/react-json-view/dist/main.cjs.map +1 -1
  595. package/dist/node_modules/@microlink/react-json-view/dist/main.js +5194 -5187
  596. package/dist/node_modules/@microlink/react-json-view/dist/main.js.map +1 -1
  597. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs +2 -1
  598. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.cjs.map +1 -0
  599. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js +21 -1
  600. package/dist/node_modules/@openhands/typescript-client/dist/client/desktop-client.js.map +1 -0
  601. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.cjs +2 -1
  602. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.cjs.map +1 -0
  603. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.js +22 -1
  604. package/dist/node_modules/@openhands/typescript-client/dist/client/hooks-client.js.map +1 -0
  605. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.cjs +2 -1
  606. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.cjs.map +1 -0
  607. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.js +33 -1
  608. package/dist/node_modules/@openhands/typescript-client/dist/client/meta-profiles-client.js.map +1 -0
  609. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs +1 -1
  610. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.cjs.map +1 -1
  611. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js +56 -22
  612. package/dist/node_modules/@openhands/typescript-client/dist/client/openhands-client.js.map +1 -1
  613. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.cjs +1 -1
  614. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.cjs.map +1 -1
  615. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.js +3 -0
  616. package/dist/node_modules/@openhands/typescript-client/dist/client/profiles-client.js.map +1 -1
  617. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.cjs +2 -1
  618. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.cjs.map +1 -0
  619. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.js +31 -1
  620. package/dist/node_modules/@openhands/typescript-client/dist/client/shared-client.js.map +1 -0
  621. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.cjs +2 -1
  622. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.cjs.map +1 -0
  623. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.js +21 -1
  624. package/dist/node_modules/@openhands/typescript-client/dist/client/sub-agents-client.js.map +1 -0
  625. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.cjs +2 -1
  626. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.cjs.map +1 -0
  627. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.js +21 -1
  628. package/dist/node_modules/@openhands/typescript-client/dist/client/tool-client.js.map +1 -0
  629. package/dist/node_modules/@openhands/typescript-client/dist/conversation/local-conversation.cjs.map +1 -1
  630. package/dist/node_modules/@openhands/typescript-client/dist/conversation/local-conversation.js.map +1 -1
  631. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs +1 -1
  632. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.cjs.map +1 -1
  633. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js +9 -5
  634. package/dist/node_modules/@openhands/typescript-client/dist/models/acp-providers.js.map +1 -1
  635. package/dist/node_modules/i18next-http-backend/esm/index.cjs +1 -1
  636. package/dist/node_modules/i18next-http-backend/esm/index.cjs.map +1 -1
  637. package/dist/node_modules/i18next-http-backend/esm/index.js +2 -2
  638. package/dist/node_modules/i18next-http-backend/esm/index.js.map +1 -1
  639. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.cjs +2 -0
  640. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.cjs.map +1 -0
  641. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.js +31 -0
  642. package/dist/node_modules/lucide-react/dist/esm/icons/archive-restore.js.map +1 -0
  643. package/dist/node_modules/lucide-react/dist/esm/icons/archive.cjs +2 -0
  644. package/dist/node_modules/lucide-react/dist/esm/icons/archive.cjs.map +1 -0
  645. package/dist/node_modules/lucide-react/dist/esm/icons/archive.js +23 -0
  646. package/dist/node_modules/lucide-react/dist/esm/icons/archive.js.map +1 -0
  647. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.cjs +2 -0
  648. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.cjs.map +1 -0
  649. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.js +27 -0
  650. package/dist/node_modules/lucide-react/dist/esm/icons/circle-alert.js.map +1 -0
  651. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.cjs +2 -0
  652. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.cjs.map +1 -0
  653. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.js +12 -0
  654. package/dist/node_modules/lucide-react/dist/esm/icons/gauge.js.map +1 -0
  655. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.cjs +2 -0
  656. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.cjs.map +1 -0
  657. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.js +23 -0
  658. package/dist/node_modules/lucide-react/dist/esm/icons/minimize.js.map +1 -0
  659. package/dist/node_modules/zustand/esm/middleware.cjs +3 -3
  660. package/dist/node_modules/zustand/esm/middleware.cjs.map +1 -1
  661. package/dist/node_modules/zustand/esm/middleware.js +33 -31
  662. package/dist/node_modules/zustand/esm/middleware.js.map +1 -1
  663. package/dist/package.cjs +1 -1
  664. package/dist/package.cjs.map +1 -1
  665. package/dist/package.js +17 -16
  666. package/dist/package.js.map +1 -1
  667. package/dist/routes/files-tab.cjs +1 -1
  668. package/dist/routes/files-tab.js +11 -11
  669. package/dist/routes/files-tab.js.map +1 -1
  670. package/dist/routes/usage-tab.cjs +2 -0
  671. package/dist/routes/usage-tab.cjs.map +1 -0
  672. package/dist/routes/usage-tab.d.ts +2 -0
  673. package/dist/routes/usage-tab.js +10 -0
  674. package/dist/routes/usage-tab.js.map +1 -0
  675. package/dist/services/child-conversation-launch.cjs +1 -1
  676. package/dist/services/child-conversation-launch.cjs.map +1 -1
  677. package/dist/services/child-conversation-launch.js +11 -5
  678. package/dist/services/child-conversation-launch.js.map +1 -1
  679. package/dist/services/settings.cjs +1 -1
  680. package/dist/services/settings.cjs.map +1 -1
  681. package/dist/services/settings.js +2 -2
  682. package/dist/services/settings.js.map +1 -1
  683. package/dist/services/telemetry.cjs +1 -1
  684. package/dist/services/telemetry.cjs.map +1 -1
  685. package/dist/services/telemetry.js +47 -51
  686. package/dist/services/telemetry.js.map +1 -1
  687. package/dist/stores/archived-conversations-store.cjs +2 -0
  688. package/dist/stores/archived-conversations-store.cjs.map +1 -0
  689. package/dist/stores/archived-conversations-store.d.ts +24 -0
  690. package/dist/stores/archived-conversations-store.js +33 -0
  691. package/dist/stores/archived-conversations-store.js.map +1 -0
  692. package/dist/stores/conversation-panel-preferences-store.cjs +1 -1
  693. package/dist/stores/conversation-panel-preferences-store.cjs.map +1 -1
  694. package/dist/stores/conversation-panel-preferences-store.d.ts +3 -0
  695. package/dist/stores/conversation-panel-preferences-store.js +4 -0
  696. package/dist/stores/conversation-panel-preferences-store.js.map +1 -1
  697. package/dist/stores/conversation-store.cjs.map +1 -1
  698. package/dist/stores/conversation-store.d.ts +1 -1
  699. package/dist/stores/conversation-store.js.map +1 -1
  700. package/dist/stores/error-message-store.cjs +1 -1
  701. package/dist/stores/error-message-store.cjs.map +1 -1
  702. package/dist/stores/error-message-store.d.ts +3 -1
  703. package/dist/stores/error-message-store.js +5 -3
  704. package/dist/stores/error-message-store.js.map +1 -1
  705. package/dist/stores/metrics-store.cjs +1 -1
  706. package/dist/stores/metrics-store.cjs.map +1 -1
  707. package/dist/stores/metrics-store.d.ts +1 -0
  708. package/dist/stores/metrics-store.js +7 -4
  709. package/dist/stores/metrics-store.js.map +1 -1
  710. package/dist/types/agent-server/core/events/condensation-event.d.ts +3 -0
  711. package/dist/types/agent-server/core/events/conversation-state-event.d.ts +4 -6
  712. package/dist/types/agent-server/type-guards.cjs +1 -1
  713. package/dist/types/agent-server/type-guards.cjs.map +1 -1
  714. package/dist/types/agent-server/type-guards.d.ts +5 -0
  715. package/dist/types/agent-server/type-guards.js +3 -3
  716. package/dist/types/agent-server/type-guards.js.map +1 -1
  717. package/dist/types/git-sync.d.ts +51 -0
  718. package/dist/utils/conversation-local-storage.cjs +1 -1
  719. package/dist/utils/conversation-local-storage.cjs.map +1 -1
  720. package/dist/utils/conversation-local-storage.js +2 -1
  721. package/dist/utils/conversation-local-storage.js.map +1 -1
  722. package/dist/utils/error-handler.cjs +1 -1
  723. package/dist/utils/error-handler.cjs.map +1 -1
  724. package/dist/utils/error-handler.d.ts +3 -2
  725. package/dist/utils/error-handler.js +18 -5
  726. package/dist/utils/error-handler.js.map +1 -1
  727. package/dist/utils/format-model-name.cjs +1 -1
  728. package/dist/utils/format-model-name.cjs.map +1 -1
  729. package/dist/utils/format-model-name.d.ts +1 -0
  730. package/dist/utils/format-model-name.js +1 -0
  731. package/dist/utils/format-model-name.js.map +1 -1
  732. package/dist/utils/format-token-count.cjs +2 -0
  733. package/dist/utils/format-token-count.cjs.map +1 -0
  734. package/dist/utils/format-token-count.d.ts +3 -0
  735. package/dist/utils/format-token-count.js +19 -0
  736. package/dist/utils/format-token-count.js.map +1 -0
  737. package/dist/utils/hover-reveal-classes.cjs +2 -0
  738. package/dist/utils/hover-reveal-classes.cjs.map +1 -0
  739. package/dist/utils/hover-reveal-classes.d.ts +34 -0
  740. package/dist/utils/hover-reveal-classes.js +41 -0
  741. package/dist/utils/hover-reveal-classes.js.map +1 -0
  742. package/dist/utils/is-markdown-file-path.cjs +2 -0
  743. package/dist/utils/is-markdown-file-path.cjs.map +1 -0
  744. package/dist/utils/is-markdown-file-path.d.ts +4 -0
  745. package/dist/utils/is-markdown-file-path.js +14 -0
  746. package/dist/utils/is-markdown-file-path.js.map +1 -0
  747. package/dist/utils/mcp-marketplace-utils.cjs +1 -1
  748. package/dist/utils/mcp-marketplace-utils.cjs.map +1 -1
  749. package/dist/utils/mcp-marketplace-utils.d.ts +7 -0
  750. package/dist/utils/mcp-marketplace-utils.js +19 -16
  751. package/dist/utils/mcp-marketplace-utils.js.map +1 -1
  752. package/dist/utils/transcript-export/index.cjs +3 -3
  753. package/dist/utils/transcript-export/index.cjs.map +1 -1
  754. package/dist/utils/transcript-export/index.js +12 -12
  755. package/dist/utils/transcript-export/index.js.map +1 -1
  756. package/dist/utils/transcript-export/load-complete-events.cjs +1 -1
  757. package/dist/utils/transcript-export/load-complete-events.cjs.map +1 -1
  758. package/dist/utils/transcript-export/load-complete-events.js +25 -17
  759. package/dist/utils/transcript-export/load-complete-events.js.map +1 -1
  760. package/dist/utils/workspace-relative-path.cjs +2 -0
  761. package/dist/utils/workspace-relative-path.cjs.map +1 -0
  762. package/dist/utils/workspace-relative-path.d.ts +10 -0
  763. package/dist/utils/workspace-relative-path.js +22 -0
  764. package/dist/utils/workspace-relative-path.js.map +1 -0
  765. package/package.json +17 -16
  766. package/scripts/check-sdk-version-sync.mjs +6 -6
  767. package/scripts/check-translation-completeness.cjs +3 -0
  768. package/scripts/dev-process-utils.mjs +6 -1
  769. package/scripts/dev-safe.mjs +57 -2
  770. package/scripts/dev-with-automation.mjs +98 -9
  771. package/build/assets/active-backend-context-B_bg5h9O.js +0 -2
  772. package/build/assets/agent-profiles-settings-hd2cAIZM.js +0 -1
  773. package/build/assets/agent-server-conversation-service.api-BheU4HX3.js +0 -86
  774. package/build/assets/agent-settings-D_SVrWhI.js +0 -1
  775. package/build/assets/automation-detail-C0SxWTnQ.js +0 -3
  776. package/build/assets/automation-export-BjQa_fT6.js +0 -2
  777. package/build/assets/automation-setup-route-BzCNpljP.js +0 -1
  778. package/build/assets/automations-list-dZg8zOCl.js +0 -1
  779. package/build/assets/bot-DuN83i3p.js +0 -1
  780. package/build/assets/browser--eoQOo4M.js +0 -6
  781. package/build/assets/check-DLOKoFU9.js +0 -1
  782. package/build/assets/chevron-down-CnIbtM6i.js +0 -1
  783. package/build/assets/circle-check-CxCYJrpZ.js +0 -1
  784. package/build/assets/cloud-funnel-analytics-CcSez21r.js +0 -1
  785. package/build/assets/conversation-Cmyw9bFh.js +0 -90
  786. package/build/assets/conversation-CvoGZ5vC.js +0 -1
  787. package/build/assets/conversation-panel-CvoGZ5vC.js +0 -1
  788. package/build/assets/conversation-state-store-CtgILxWF.js +0 -1
  789. package/build/assets/conversation-store-B8Eahyeg.js +0 -6
  790. package/build/assets/external-link-zWbg5KXS.js +0 -1
  791. package/build/assets/files-tab-D5zuU5TY.js +0 -1
  792. package/build/assets/format-model-name-BN_rx1Y3.js +0 -1
  793. package/build/assets/format-relative-time-BerVamGl.js +0 -1
  794. package/build/assets/git-provider-icon-zuKz2cwo.js +0 -1
  795. package/build/assets/git-repo-dropdown-Bo7ciSNX.js +0 -1
  796. package/build/assets/home-DqyeeFA_.js +0 -1
  797. package/build/assets/home-Y0aDou-a.js +0 -1
  798. package/build/assets/index-home-DqyeeFA_.js +0 -1
  799. package/build/assets/info-C9lpK5pw.js +0 -1
  800. package/build/assets/install-server-modal-DW6dSyGr.js +0 -1
  801. package/build/assets/kebab-menu-item-content-CKydZrbH.js +0 -1
  802. package/build/assets/llm-not-configured-banner-DCE_0B4I.js +0 -27
  803. package/build/assets/llm-settings-8eWBF3Ij.js +0 -1
  804. package/build/assets/llm-settings-D9ko42Pi.js +0 -1
  805. package/build/assets/llm-subscription-service-B3K-97lP.js +0 -1
  806. package/build/assets/loader-circle-C5AnEN8r.js +0 -1
  807. package/build/assets/manifest-e1c3bc3b.js +0 -1
  808. package/build/assets/manifest-subpage-layout-Ds7G5GZ8.js +0 -1
  809. package/build/assets/markdown-renderer-DzvgdZey.js +0 -8
  810. package/build/assets/mcp-marketplace-utils-DSLou4n7.js +0 -1
  811. package/build/assets/messages-B2l0qLVk.js +0 -40
  812. package/build/assets/onboarding-modal-DrrGlEQq.js +0 -1
  813. package/build/assets/pencil-Bl3GWhgq.js +0 -1
  814. package/build/assets/plus-CtzlyAoF.js +0 -1
  815. package/build/assets/profiles-service.api-vcE_jJCe.js +0 -1
  816. package/build/assets/providers-CdrPPm0z.js +0 -1
  817. package/build/assets/recommended-automations-launcher-DIYgUdMm.js +0 -1
  818. package/build/assets/refresh-cw-B9GhC3QC.js +0 -1
  819. package/build/assets/root-CBBi6IAe.js +0 -2
  820. package/build/assets/root-CRK9fOFd.css +0 -1
  821. package/build/assets/root-layout-BFN2rCOy.js +0 -2
  822. package/build/assets/search-CYaiKHRp.js +0 -1
  823. package/build/assets/secrets-service-BHF4fxML.js +0 -1
  824. package/build/assets/secrets-settings-aBGXtQhX.js +0 -1
  825. package/build/assets/shared-conversation-BIAcuORk.js +0 -1
  826. package/build/assets/shield-check-D2cpEpv3.js +0 -1
  827. package/build/assets/trash-2-CzDVN-Iw.js +0 -1
  828. package/build/assets/use-acp-credential-form-DGyo0QvL.js +0 -1
  829. package/build/assets/use-agent-profiles-B37XWDLq.js +0 -1
  830. package/build/assets/use-config-DapX3pjC.js +0 -1
  831. package/build/assets/use-create-conversation-CRf1dCm5.js +0 -1
  832. package/build/assets/use-create-secret-DcpT3RPw.js +0 -1
  833. package/build/assets/use-is-authed-Bka89Xmm.js +0 -1
  834. package/build/assets/use-is-creating-conversation-CVefC9Ns.js +0 -1
  835. package/build/assets/use-llm-profiles-CHxqoHiN.js +0 -1
  836. package/build/assets/use-manifest-capabilities-zihHDbMW.js +0 -1
  837. package/build/assets/use-save-agent-profile-CH9_EXQG.js +0 -1
  838. package/build/assets/use-tracking-salbPJxT.js +0 -3
  839. package/build/assets/utils-DPQfkd-7.js +0 -1
  840. package/build/assets/vendor~index-home~home~conversation-panel~conversation-DiXf0dkj.js +0 -13
  841. package/build/assets/x-RYDBg-e2.js +0 -1
  842. package/build/assets/zap-tbKDzCe3.js +0 -1
  843. package/dist/api/cloud/suggestions-service.api.d.ts +0 -8
  844. package/dist/api/suggestions-service/suggestions-service.api.d.ts +0 -11
  845. package/dist/components/conversation-events/chat/event-message-components/observation-pair-event-message.d.ts +0 -7
  846. package/dist/components/conversation-events/index.d.ts +0 -1
  847. package/dist/components/features/automations/metadata-chip.d.ts +0 -6
  848. package/dist/components/features/chat/task-tracking/status-badge.d.ts +0 -5
  849. package/dist/components/features/chat/task-tracking/status-icon.d.ts +0 -5
  850. package/dist/components/features/chat/task-tracking/task-list-section.d.ts +0 -10
  851. package/dist/components/features/controls/tools.d.ts +0 -2
  852. package/dist/components/features/conversation/metrics-modal/context-window-section.cjs +0 -2
  853. package/dist/components/features/conversation/metrics-modal/context-window-section.cjs.map +0 -1
  854. package/dist/components/features/conversation/metrics-modal/context-window-section.d.ts +0 -6
  855. package/dist/components/features/conversation/metrics-modal/context-window-section.js +0 -46
  856. package/dist/components/features/conversation/metrics-modal/context-window-section.js.map +0 -1
  857. package/dist/components/features/conversation/metrics-modal/empty-state.cjs +0 -2
  858. package/dist/components/features/conversation/metrics-modal/empty-state.cjs.map +0 -1
  859. package/dist/components/features/conversation/metrics-modal/empty-state.d.ts +0 -1
  860. package/dist/components/features/conversation/metrics-modal/empty-state.js +0 -18
  861. package/dist/components/features/conversation/metrics-modal/empty-state.js.map +0 -1
  862. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.cjs +0 -2
  863. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.cjs.map +0 -1
  864. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.d.ts +0 -5
  865. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.js +0 -20
  866. package/dist/components/features/conversation/metrics-modal/metrics-modal-header.js.map +0 -1
  867. package/dist/components/features/conversation/metrics-modal/metrics-modal.cjs +0 -2
  868. package/dist/components/features/conversation/metrics-modal/metrics-modal.cjs.map +0 -1
  869. package/dist/components/features/conversation/metrics-modal/metrics-modal.d.ts +0 -6
  870. package/dist/components/features/conversation/metrics-modal/metrics-modal.js +0 -57
  871. package/dist/components/features/conversation/metrics-modal/metrics-modal.js.map +0 -1
  872. package/dist/components/features/conversation-panel/new-conversation-button.d.ts +0 -23
  873. package/dist/components/features/home/home-header/guide-message.d.ts +0 -1
  874. package/dist/components/features/home/home-header/home-header.d.ts +0 -1
  875. package/dist/components/features/home/new-conversation/create-conversation-button.d.ts +0 -1
  876. package/dist/components/features/home/new-conversation/new-conversation.d.ts +0 -1
  877. package/dist/components/features/home/new-conversation.d.ts +0 -1
  878. package/dist/components/features/home/tasks/task-card.d.ts +0 -6
  879. package/dist/components/features/home/tasks/task-group.d.ts +0 -7
  880. package/dist/components/features/home/tasks/task-issue-number.d.ts +0 -6
  881. package/dist/components/features/home/tasks/task-item-title.d.ts +0 -1
  882. package/dist/components/features/home/tasks/task-suggestions-skeleton.d.ts +0 -1
  883. package/dist/components/features/home/tasks/task-suggestions.d.ts +0 -6
  884. package/dist/components/features/settings/agent-server-onboarding.d.ts +0 -10
  885. package/dist/components/features/settings/mcp-settings/mcp-server-list-item.d.ts +0 -6
  886. package/dist/components/features/settings/mcp-settings/mcp-server-list.d.ts +0 -8
  887. package/dist/hooks/query/use-suggested-tasks.d.ts +0 -1
  888. package/dist/hooks/use-should-show-user-features.d.ts +0 -1
  889. package/dist/icons/u-credit-card.cjs +0 -2
  890. package/dist/icons/u-credit-card.cjs.map +0 -1
  891. package/dist/icons/u-credit-card.js +0 -21
  892. package/dist/icons/u-credit-card.js.map +0 -1
  893. package/dist/ui/card-title.d.ts +0 -16
  894. package/dist/ui/card.d.ts +0 -14
  895. package/dist/utils/group-suggested-tasks.d.ts +0 -7
  896. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~cy4jtuc2-rfV8mQ9m.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~dcc51rps-rfV8mQ9m.js} +0 -0
  897. /package/build/assets/{vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~lq5hicxx-cHFhpUBq.js → vendor~entry.client~root~root-layout~index-home~home~conversation-panel~conversation~launch~ox0jfsa0-cHFhpUBq.js} +0 -0
  898. /package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~9pstowjp-BOVIFA_g.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~eo2nf80n-BOVIFA_g.js} +0 -0
  899. /package/build/assets/{vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~ht43enc2-C0JXQb_q.js → vendor~root~root-layout~index-home~home~conversation-panel~conversation~launch~skills-setti~jz4f6vo2-C0JXQb_q.js} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.cjs","names":[],"sources":["../../src/services/telemetry.ts"],"sourcesContent":["/**\n * Telemetry service for tracking library usage.\n *\n * This module handles anonymous telemetry for the @openhands/agent-canvas package\n * using the PostHog SDK for reliable event delivery with batching, retry logic,\n * and offline support.\n *\n * TRACKING PHILOSOPHY:\n * - Install event (canvas_install): Sent immediately on first use, regardless of consent.\n * This is anonymous and contains no PII - just basic browser info and a random ID.\n * - Session/custom events: Only sent after user grants consent via the consent modal.\n * - Users can opt out of all future tracking by declining consent.\n *\n * AD BLOCKER BYPASS:\n * By default, telemetry is routed through OpenHands' reverse proxy (z.openhands.dev)\n * to avoid being blocked by ad blockers. Library consumers can override this with:\n * - VITE_POSTHOG_HOST: Custom proxy URL or direct PostHog URL\n * - VITE_POSTHOG_UI_HOST: PostHog UI host (defaults to https://us.posthog.com)\n *\n * IMPORTANT: By default, telemetry is sent to the OpenHands PostHog project.\n * Source builds can override this with VITE_POSTHOG_API_KEY. Precompiled\n * library consumers can pass the same settings to configureTelemetry().\n *\n * Users can disable all telemetry (including install tracking) via:\n * - Setting VITE_DO_NOT_TRACK=1 environment variable\n * - Browser's Do Not Track setting\n */\n\nimport type { BootstrapConfig, CaptureResult, PostHog } from \"posthog-js\";\nimport { getLockedCloudAuthMode } from \"#/api/agent-server-config\";\nimport defaults from \"../../config/defaults.json\";\n\nimport packageJson from \"../../package.json\";\nimport {\n AGENT_CANVAS_CLIENT_SOURCE,\n AGENT_CANVAS_CLIENT_VERSION,\n} from \"#/api/client-source\";\nimport {\n getBackendTelemetryProperties,\n getCloudTelemetryProperties,\n type BackendTelemetryContextInput,\n type CloudTelemetryContextInput,\n} from \"#/services/telemetry-context\";\n\nconst TELEMETRY_CONSENT_KEY = \"openhands-telemetry-consent\";\nconst TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY =\n \"openhands-telemetry-consent-pending-cloud-sync\";\nconst TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY =\n \"openhands-telemetry-consent-pending-local-revocation\";\nconst TELEMETRY_CONSENT_CHANGE_EVENT = \"openhands-telemetry-consent-change\";\nconst TELEMETRY_FIRST_USE_KEY = \"openhands-telemetry-first-use\";\nconst TELEMETRY_SESSION_KEY = \"openhands-telemetry-session\";\nconst POSTHOG_INSTANCE_NAME = \"agent-canvas\";\nconst POSTHOG_PAGEVIEW_CAPTURE_MODE = \"history_change\";\n\n// Unconfigured source builds use staging. Production release workflows pass\n// VITE_POSTHOG_API_KEY explicitly for both the app and library artifacts.\nconst DEFAULT_POSTHOG_API_KEY: string =\n (import.meta.env.VITE_POSTHOG_API_KEY as string | undefined) ||\n defaults.telemetry.posthogApiKey;\n\n// Default to OpenHands' reverse proxy to bypass ad blockers.\n// The proxy at z.openhands.dev routes to PostHog's US region.\n// Library consumers can override this with their own proxy or direct PostHog URL.\nconst DEFAULT_POSTHOG_HOST =\n import.meta.env.VITE_POSTHOG_HOST || \"https://z.openhands.dev\";\n\n// UI host is needed for PostHog features like toolbar to work correctly\n// when using a reverse proxy. Defaults to US region.\nconst DEFAULT_POSTHOG_UI_HOST =\n import.meta.env.VITE_POSTHOG_UI_HOST || \"https://us.posthog.com\";\n\nexport interface TelemetryConfig {\n /** PostHog project key. Useful for precompiled library consumers. */\n apiKey?: string;\n /** Event ingestion host or reverse proxy. */\n apiHost?: string;\n /** PostHog UI host used by toolbar links and other UI features. */\n uiHost?: string;\n}\n\nexport type TelemetryConfiguration = TelemetryConfig | false;\n\nexport type TelemetryConsent = \"granted\" | \"denied\" | \"pending\";\nexport type ResolvedTelemetryConsent = Exclude<TelemetryConsent, \"pending\">;\n\nexport interface SetTelemetryConsentOptions {\n /** Do not persist a value mirrored from backend settings back to Cloud. */\n syncToCloud?: boolean;\n}\n\nlet posthogInstance: PostHog | null = null;\nlet initializationPromise: Promise<PostHog | null> | null = null;\nlet pendingBootstrap: BootstrapConfig | undefined;\nlet telemetryConfig: TelemetryConfig = {};\nlet telemetryDisabled = false;\n\ninterface TelemetryIdentity {\n distinctId: string;\n properties: Record<string, unknown>;\n}\n\n// undefined means that Cloud identity has not resolved yet; null means that it\n// resolved without a user. This distinction prevents startup from resetting a\n// persisted identity while the current account is still loading.\nlet desiredTelemetryIdentity: TelemetryIdentity | null | undefined;\nlet desiredIdentityRevision = 0;\nlet appliedIdentityRevision = -1;\n\nconst CANVAS_EVENT_PROPERTIES = Object.freeze({\n client_source: AGENT_CANVAS_CLIENT_SOURCE,\n client_version: AGENT_CANVAS_CLIENT_VERSION,\n package_name: packageJson.name,\n package_version: packageJson.version,\n});\n\nlet telemetryBackendContext = getBackendTelemetryProperties({});\nlet telemetryCloudContext = getCloudTelemetryProperties();\n\nexport function setTelemetryBackendContext(\n context: BackendTelemetryContextInput,\n): void {\n telemetryBackendContext = getBackendTelemetryProperties(context);\n}\n\nexport function setTelemetryCloudContext(\n context: CloudTelemetryContextInput | null,\n): void {\n telemetryCloudContext = getCloudTelemetryProperties(context);\n}\n\nfunction addCanvasEventProperties(\n event: CaptureResult | null,\n): CaptureResult | null {\n if (!event) return null;\n\n return {\n ...event,\n properties: {\n ...telemetryBackendContext,\n ...telemetryCloudContext,\n ...event.properties,\n ...CANVAS_EVENT_PROPERTIES,\n },\n };\n}\n\nfunction restorePostHogConsent(posthog: PostHog): void {\n if (telemetryDisabled || getTelemetryConsent() !== \"granted\") {\n posthog.opt_out_capturing();\n } else {\n posthog.opt_in_capturing();\n }\n}\n\nfunction resetPostHogIdentity(posthog: PostHog, resetDeviceId = false): void {\n posthog.reset(resetDeviceId);\n appliedIdentityRevision = -1;\n // PostHog reset clears its own consent persistence, so immediately restore\n // the canonical Canvas decision kept in localStorage.\n restorePostHogConsent(posthog);\n}\n\nfunction applyDesiredTelemetryIdentity(posthog: PostHog): void {\n if (desiredTelemetryIdentity === undefined || !isTelemetryEnabled()) return;\n\n const desiredId = desiredTelemetryIdentity?.distinctId;\n const currentId = posthog.get_property(\"$user_id\");\n if (currentId != null && currentId !== desiredId) {\n resetPostHogIdentity(posthog);\n }\n\n if (desiredTelemetryIdentity === null) {\n appliedIdentityRevision = desiredIdentityRevision;\n return;\n }\n\n if (\n posthog.get_property(\"$user_id\") !== desiredTelemetryIdentity.distinctId ||\n appliedIdentityRevision !== desiredIdentityRevision\n ) {\n posthog.identify(\n desiredTelemetryIdentity.distinctId,\n desiredTelemetryIdentity.properties,\n );\n appliedIdentityRevision = desiredIdentityRevision;\n }\n}\n\nfunction propertiesEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every((key) => left[key] === right[key])\n );\n}\n\n/**\n * Configure the single Canvas telemetry client before its first use.\n * Passing false disables telemetry and install tracking for embedded hosts.\n */\nexport function configureTelemetry(config: TelemetryConfiguration): void {\n if (config === false) {\n if (telemetryDisabled) return;\n telemetryDisabled = true;\n posthogInstance?.opt_out_capturing();\n notifyTelemetryConsentListeners();\n return;\n }\n\n const wasDisabled = telemetryDisabled;\n telemetryDisabled = false;\n\n if (!posthogInstance && !initializationPromise) {\n const definedConfig = Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as TelemetryConfig;\n telemetryConfig = { ...telemetryConfig, ...definedConfig };\n }\n\n if (wasDisabled) {\n if (posthogInstance) {\n restorePostHogConsent(posthogInstance);\n applyDesiredTelemetryIdentity(posthogInstance);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\nfunction getResolvedTelemetryConfig(): Required<TelemetryConfig> | null {\n if (telemetryDisabled) return null;\n\n return {\n apiKey: telemetryConfig.apiKey || DEFAULT_POSTHOG_API_KEY,\n apiHost: telemetryConfig.apiHost || DEFAULT_POSTHOG_HOST,\n uiHost: telemetryConfig.uiHost || DEFAULT_POSTHOG_UI_HOST,\n };\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof localStorage !== \"undefined\";\n}\n\n/**\n * Lazily load PostHog to avoid SSR/Node.js issues.\n * PostHog is a browser-only library, so we dynamically import it only when needed.\n */\nasync function getPostHog(): Promise<PostHog | null> {\n if (!isBrowser()) {\n return null;\n }\n\n if (posthogInstance) {\n return posthogInstance;\n }\n\n try {\n const { default: posthog } = await import(\"posthog-js\");\n return posthog;\n } catch {\n // Failed to load PostHog - telemetry will be disabled\n return null;\n }\n}\n\n/**\n * Check if telemetry is disabled via environment variable or browser setting.\n * Works in both Node.js and browser (Vite) environments.\n */\nfunction isDoNotTrackEnabled(): boolean {\n if (telemetryDisabled) {\n return true;\n }\n\n // Check Vite environment variable (browser)\n if (\n typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DO_NOT_TRACK === \"1\"\n ) {\n return true;\n }\n\n // Check Node.js environment variable (SSR/testing)\n if (typeof process !== \"undefined\" && process.env?.DO_NOT_TRACK === \"1\") {\n return true;\n }\n\n // Check browser's navigator.doNotTrack standard\n if (\n typeof navigator !== \"undefined\" &&\n (navigator.doNotTrack === \"1\" ||\n // @ts-expect-error - Some browsers use window.doNotTrack\n (typeof window !== \"undefined\" && window.doNotTrack === \"1\"))\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Initialize PostHog SDK.\n *\n * @param enableCapturing - If true, enable capturing immediately (for install tracking).\n * If false, start with capturing disabled (for consent-gated tracking).\n */\nexport function configurePostHogBootstrap(\n bootstrap: BootstrapConfig | undefined,\n): void {\n if (!posthogInstance) {\n pendingBootstrap = bootstrap;\n }\n}\n\nexport async function initializePostHogClient(\n enableCapturing = false,\n): Promise<PostHog | null> {\n if (posthogInstance) {\n return posthogInstance;\n }\n\n if (initializationPromise) {\n return initializationPromise;\n }\n\n initializationPromise = (async () => {\n const config = getResolvedTelemetryConfig();\n if (!config) {\n return null;\n }\n\n const posthog = await getPostHog();\n if (!posthog) {\n return null;\n }\n\n // A named instance isolates Canvas configuration, consent, identity, and\n // persistence from a host application's default PostHog singleton.\n const initializedPostHog = posthog.init(\n config.apiKey,\n {\n api_host: config.apiHost,\n ui_host: config.uiHost,\n opt_out_capturing_by_default: !enableCapturing,\n persistence: \"localStorage\",\n persistence_name: POSTHOG_INSTANCE_NAME,\n consent_persistence_name: `${POSTHOG_INSTANCE_NAME}-consent`,\n person_profiles: \"always\",\n capture_pageview: POSTHOG_PAGEVIEW_CAPTURE_MODE,\n autocapture: true,\n disable_session_recording: true,\n bootstrap: pendingBootstrap,\n before_send: addCanvasEventProperties,\n },\n POSTHOG_INSTANCE_NAME,\n );\n if (!initializedPostHog) return null;\n\n posthogInstance = initializedPostHog;\n pendingBootstrap = undefined;\n\n if (telemetryDisabled) {\n posthogInstance.opt_out_capturing();\n } else if (getTelemetryConsent() === \"granted\") {\n posthogInstance.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthogInstance);\n } else if (!enableCapturing) {\n posthogInstance.opt_out_capturing();\n }\n\n return posthogInstance;\n })();\n\n try {\n return await initializationPromise;\n } finally {\n if (!posthogInstance) {\n initializationPromise = null;\n }\n }\n}\n\n/**\n * Get user's telemetry consent preference\n */\nexport function getTelemetryConsent(): TelemetryConsent {\n if (!isBrowser()) {\n return \"pending\";\n }\n\n // Check environment variable for opt-out\n if (isDoNotTrackEnabled()) {\n return \"denied\";\n }\n\n if (getLockedCloudAuthMode() === \"cookie\") {\n return \"granted\";\n }\n\n try {\n const consent = localStorage.getItem(TELEMETRY_CONSENT_KEY);\n if (consent === \"granted\" || consent === \"denied\") {\n return consent;\n }\n } catch {\n // Ignore storage errors\n }\n\n return \"pending\";\n}\n\n/**\n * Return an explicit browser choice that still needs to survive a Cloud login.\n * It remains pending across local backends so their settings cannot consume a\n * decision that must still be applied after the user connects to Cloud.\n */\nexport function getPendingCloudTelemetryConsent(): ResolvedTelemetryConsent | null {\n if (!isBrowser()) return null;\n\n try {\n const consent = localStorage.getItem(\n TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY,\n );\n return consent === \"granted\" || consent === \"denied\" ? consent : null;\n } catch {\n return null;\n }\n}\n\n/** Return the pre-reset actor whose local Automation consent must be revoked. */\nexport function getPendingLocalTelemetryRevocationId(): string | null {\n if (!isBrowser()) return null;\n\n try {\n return localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n return null;\n }\n}\n\nexport function subscribeTelemetryConsent(listener: () => void): () => void {\n if (!isBrowser()) return () => {};\n const handleStorage = (event: StorageEvent) => {\n if (\n event.key === TELEMETRY_CONSENT_KEY ||\n event.key === TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY\n ) {\n listener();\n }\n };\n window.addEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.addEventListener(\"storage\", handleStorage);\n\n return () => {\n window.removeEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction notifyTelemetryConsentListeners(): void {\n if (isBrowser())\n window.dispatchEvent(new Event(TELEMETRY_CONSENT_CHANGE_EVENT));\n}\n\nfunction markTelemetryConsentForCloudSync(\n consent: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n localStorage.setItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY, consent);\n } catch {\n // Ignore storage errors; the in-browser consent decision still applies.\n }\n}\n\nfunction markPendingLocalTelemetryRevocation(distinctId: string | null): void {\n if (!isBrowser() || !distinctId) return;\n\n try {\n localStorage.setItem(\n TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY,\n distinctId,\n );\n } catch {\n // Ignore storage errors; browser capture is still disabled below.\n }\n}\n\nexport function clearPendingLocalTelemetryRevocation(\n expectedDistinctId: string,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY) !==\n expectedDistinctId\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n // Ignore storage errors.\n }\n}\n\nexport function clearPendingCloudTelemetryConsent(\n expected?: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n expected !== undefined &&\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY) !==\n expected\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY);\n notifyTelemetryConsentListeners();\n } catch {\n // Ignore storage errors.\n }\n}\n\n/**\n * Set user's telemetry consent preference\n */\nexport async function setTelemetryConsent(\n consent: ResolvedTelemetryConsent,\n { syncToCloud = true }: SetTelemetryConsentOptions = {},\n): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n const previousConsent = getTelemetryConsent();\n const unchanged = previousConsent === consent;\n localStorage.setItem(TELEMETRY_CONSENT_KEY, consent);\n if (telemetryDisabled) return;\n if (unchanged) return;\n\n // Reuse an initialized client synchronously so a same-flush identify()\n // cannot run before consent is applied. Only the cold path awaits import.\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (!posthog) {\n return;\n }\n\n if (consent === \"granted\") {\n posthog.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthog);\n } else {\n markPendingLocalTelemetryRevocation(posthog.get_distinct_id?.() ?? null);\n if (posthog.get_property?.(\"$user_id\") != null) {\n resetPostHogIdentity(posthog);\n } else {\n posthog.opt_out_capturing();\n }\n }\n } catch {\n // Ignore storage errors\n } finally {\n // Notify UI/backend/identity reconcilers only after the browser capture state\n // reflects this decision. Otherwise a pre-login grant can trigger an\n // identify while PostHog is still opted out and lose funnel continuity.\n if (syncToCloud) {\n markTelemetryConsentForCloudSync(consent);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\n/**\n * Declare the current Cloud identity. The telemetry service applies it only\n * after consent and owns all reset/account-switch semantics.\n */\nexport async function setTelemetryIdentity(\n distinctId: string | null,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const nextIdentity = distinctId === null ? null : { distinctId, properties };\n const unchanged =\n desiredTelemetryIdentity === nextIdentity ||\n (desiredTelemetryIdentity !== undefined &&\n desiredTelemetryIdentity !== null &&\n nextIdentity !== null &&\n desiredTelemetryIdentity.distinctId === nextIdentity.distinctId &&\n propertiesEqual(desiredTelemetryIdentity.properties, properties));\n if (unchanged) return;\n\n desiredTelemetryIdentity = nextIdentity;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n if (!isTelemetryEnabled()) return;\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (posthog && isTelemetryEnabled()) {\n // Read the desired identity after the await so a newer account always wins\n // if identity changes while the SDK is loading.\n applyDesiredTelemetryIdentity(posthog);\n }\n}\n\n/**\n * Check if telemetry is enabled (user has granted consent)\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryConsent() === \"granted\";\n}\n\n/**\n * Check if first use event has already been sent\n */\nfunction hasFirstUseSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return localStorage.getItem(TELEMETRY_FIRST_USE_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark first use event as sent\n */\nfunction markFirstUseSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n localStorage.setItem(TELEMETRY_FIRST_USE_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Track the initial install of the library.\n *\n * IMPORTANT: This is sent immediately on first use, regardless of consent status.\n * This allows us to track library adoption even if users haven't made a consent choice yet.\n *\n * The event is:\n * - Completely anonymous (no PII, just a random PostHog distinct_id)\n * - Sent only once per installation (tracked via localStorage, persists across sessions)\n * - Still respects DO_NOT_TRACK environment variable and browser setting\n *\n * Users who want complete privacy can:\n * - Set VITE_DO_NOT_TRACK=1 or browser's Do Not Track\n * - Later deny consent to prevent all future tracking\n */\nexport async function trackInstall(): Promise<void> {\n // Respect hard opt-out via environment variable or browser setting\n if (isDoNotTrackEnabled()) {\n return;\n }\n\n // Already sent install event (persisted in localStorage - survives app relaunches)\n if (hasFirstUseSent()) {\n return;\n }\n\n // Initialize PostHog with capturing enabled (for this one event)\n const posthog = await initializePostHogClient(true);\n if (!posthog || isDoNotTrackEnabled()) {\n return;\n }\n\n // Temporarily enable capturing if it was disabled\n const wasOptedOut = posthog.has_opted_out_capturing?.() ?? false;\n if (wasOptedOut) {\n posthog.opt_in_capturing();\n }\n\n // Capture the install event\n posthog.capture(\"canvas_install\", {\n platform: typeof navigator !== \"undefined\" ? navigator.platform : \"unknown\",\n user_agent:\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n url_origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n embedded: typeof window !== \"undefined\" && window.self !== window.top,\n });\n\n // Mark as sent (stored in localStorage - persists across browser sessions)\n markFirstUseSent();\n\n // Restore opt-out state if user hasn't granted consent yet\n // This ensures we only send the install event, not subsequent events\n const currentConsent = getTelemetryConsent();\n if (currentConsent !== \"granted\") {\n posthog.opt_out_capturing();\n }\n}\n\n/**\n * Check if session start event has already been sent (this browser session)\n */\nfunction hasSessionSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return sessionStorage.getItem(TELEMETRY_SESSION_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark session start event as sent (uses sessionStorage so it resets on new tabs/sessions)\n */\nfunction markSessionSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n sessionStorage.setItem(TELEMETRY_SESSION_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/** Return the shared client only when a consented capture is safe to emit. */\nasync function getPostHogForConsentedCapture(): Promise<PostHog | null> {\n if (!isTelemetryEnabled() || getTelemetryConsent() !== \"granted\") return null;\n\n const posthog = await initializePostHogClient();\n if (!posthog || !isTelemetryEnabled()) return null;\n\n // The browser preference is the canonical capture decision. PostHog may\n // still carry an older opt-out marker while backend consent is loading or\n // after a previous backend temporarily reported a stale value. Heal that\n // drift at the event boundary so capture() cannot silently discard an event\n // that the user has explicitly allowed.\n if (posthog.has_opted_out_capturing?.()) {\n posthog.opt_in_capturing();\n }\n\n applyDesiredTelemetryIdentity(posthog);\n\n return posthog;\n}\n\n/**\n * Track a session start event.\n * Called each time a new browser session starts (respects consent).\n * Uses sessionStorage for deduplication - only sends once per browser session.\n */\nexport async function trackSessionStart(): Promise<void> {\n // Already sent session event this browser session\n if (hasSessionSent()) {\n return;\n }\n\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(\"canvas_new_session\", {\n is_first_use: !hasFirstUseSent(),\n });\n\n // Mark as sent for this session\n markSessionSent();\n}\n\n/** Return the active PostHog distinct ID when consent allows capture. */\nexport async function getTelemetryDistinctId(): Promise<string | null> {\n const posthog = await getPostHogForConsentedCapture();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/** Return the PostHog distinct ID for local consent sync without emitting capture. */\nexport async function getTelemetryDistinctIdForConsentSync(): Promise<\n string | null\n> {\n if (!isBrowser()) return null;\n\n if (getTelemetryConsent() !== \"granted\") {\n const pendingRevocationId = getPendingLocalTelemetryRevocationId();\n if (pendingRevocationId) return pendingRevocationId;\n }\n\n if (telemetryDisabled || isDoNotTrackEnabled()) return null;\n\n const posthog = await initializePostHogClient();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/**\n * Track a custom event (respects consent).\n */\nexport async function trackEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(eventName, properties);\n}\n\n/** Track an exception through the same consent-aware client as custom events. */\nexport async function trackException(\n error: unknown,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.captureException(error, properties);\n}\n\n/**\n * Clear all telemetry data (for privacy/GDPR requests)\n */\nexport async function clearTelemetryData(): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n markPendingLocalTelemetryRevocation(\n posthogInstance?.get_distinct_id?.() ?? null,\n );\n localStorage.removeItem(TELEMETRY_CONSENT_KEY);\n localStorage.removeItem(TELEMETRY_FIRST_USE_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n clearPendingCloudTelemetryConsent();\n try {\n sessionStorage.removeItem(TELEMETRY_SESSION_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n\n telemetryBackendContext = getBackendTelemetryProperties({});\n telemetryCloudContext = getCloudTelemetryProperties();\n desiredTelemetryIdentity = null;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n try {\n if (posthogInstance) {\n resetPostHogIdentity(posthogInstance, true);\n }\n } catch {\n // A reset failure must not leave capture enabled after a privacy clear.\n try {\n posthogInstance?.opt_out_capturing();\n } catch {\n // Telemetry failures must not break the application.\n }\n } finally {\n notifyTelemetryConsentListeners();\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"telemetry.cjs","names":[],"sources":["../../src/services/telemetry.ts"],"sourcesContent":["/**\n * Telemetry service for tracking library usage.\n *\n * This module handles anonymous telemetry for the @openhands/agent-canvas package\n * using the PostHog SDK for reliable event delivery with batching, retry logic,\n * and offline support.\n *\n * TRACKING PHILOSOPHY:\n * - Install event (canvas_install): Sent immediately on first use, regardless of consent.\n * This is anonymous and contains no PII - just basic browser info and a random ID.\n * - Session/custom events: Only sent after user grants consent via the consent modal.\n * - Users can opt out of all future tracking by declining consent.\n *\n * AD BLOCKER BYPASS:\n * By default, telemetry is routed through OpenHands' reverse proxy (z.openhands.dev)\n * to avoid being blocked by ad blockers. Library consumers can override this with:\n * - VITE_POSTHOG_HOST: Custom proxy URL or direct PostHog URL\n * - VITE_POSTHOG_UI_HOST: PostHog UI host (defaults to https://us.posthog.com)\n *\n * IMPORTANT: By default, telemetry is sent to the OpenHands PostHog project.\n * Source builds can override this with VITE_POSTHOG_API_KEY. Precompiled\n * library consumers can pass the same settings to configureTelemetry().\n *\n * Users can disable all telemetry (including install tracking) via:\n * - Setting VITE_DO_NOT_TRACK=1 environment variable\n * - Browser's Do Not Track setting\n */\n\nimport type { BootstrapConfig, CaptureResult, PostHog } from \"posthog-js\";\nimport { getLockedCloudAuthMode } from \"#/api/agent-server-config\";\nimport defaults from \"../../config/defaults.json\";\n\nimport packageJson from \"../../package.json\";\nimport {\n AGENT_CANVAS_CLIENT_SOURCE,\n AGENT_CANVAS_CLIENT_VERSION,\n} from \"#/api/client-source\";\nimport {\n getBackendTelemetryProperties,\n getCloudTelemetryProperties,\n type BackendTelemetryContextInput,\n type CloudTelemetryContextInput,\n} from \"#/services/telemetry-context\";\n\nconst TELEMETRY_CONSENT_KEY = \"openhands-telemetry-consent\";\nconst TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY =\n \"openhands-telemetry-consent-pending-cloud-sync\";\nconst TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY =\n \"openhands-telemetry-consent-pending-local-revocation\";\nconst TELEMETRY_CONSENT_CHANGE_EVENT = \"openhands-telemetry-consent-change\";\nconst TELEMETRY_FIRST_USE_KEY = \"openhands-telemetry-first-use\";\nconst TELEMETRY_SESSION_KEY = \"openhands-telemetry-session\";\nconst POSTHOG_INSTANCE_NAME = \"agent-canvas\";\nconst POSTHOG_PAGEVIEW_CAPTURE_MODE = \"history_change\";\n\n// Unconfigured source builds use staging. Production release workflows pass\n// VITE_POSTHOG_API_KEY explicitly for both the app and library artifacts.\nconst DEFAULT_POSTHOG_API_KEY: string =\n (import.meta.env.VITE_POSTHOG_API_KEY as string | undefined) ||\n defaults.telemetry.posthogApiKey;\n\n// Default to OpenHands' reverse proxy to bypass ad blockers.\n// The proxy at z.openhands.dev routes to PostHog's US region.\n// Library consumers can override this with their own proxy or direct PostHog URL.\nconst DEFAULT_POSTHOG_HOST =\n import.meta.env.VITE_POSTHOG_HOST || \"https://z.openhands.dev\";\n\n// UI host is needed for PostHog features like toolbar to work correctly\n// when using a reverse proxy. Defaults to US region.\nconst DEFAULT_POSTHOG_UI_HOST =\n import.meta.env.VITE_POSTHOG_UI_HOST || \"https://us.posthog.com\";\n\nexport interface TelemetryConfig {\n /** PostHog project key. Useful for precompiled library consumers. */\n apiKey?: string;\n /** Event ingestion host or reverse proxy. */\n apiHost?: string;\n /** PostHog UI host used by toolbar links and other UI features. */\n uiHost?: string;\n}\n\nexport type TelemetryConfiguration = TelemetryConfig | false;\n\nexport type TelemetryConsent = \"granted\" | \"denied\" | \"pending\";\nexport type ResolvedTelemetryConsent = Exclude<TelemetryConsent, \"pending\">;\n\nexport interface SetTelemetryConsentOptions {\n /** Do not persist a value mirrored from backend settings back to Cloud. */\n syncToCloud?: boolean;\n}\n\nlet posthogInstance: PostHog | null = null;\nlet initializationPromise: Promise<PostHog | null> | null = null;\nlet pendingBootstrap: BootstrapConfig | undefined;\nlet telemetryConfig: TelemetryConfig = {};\nlet telemetryDisabled = false;\n\ninterface TelemetryIdentity {\n distinctId: string;\n properties: Record<string, unknown>;\n}\n\n// undefined means that Cloud identity has not resolved yet; null means that it\n// resolved without a user. This distinction prevents startup from resetting a\n// persisted identity while the current account is still loading.\nlet desiredTelemetryIdentity: TelemetryIdentity | null | undefined;\nlet desiredIdentityRevision = 0;\nlet appliedIdentityRevision = -1;\n\nconst CANVAS_EVENT_PROPERTIES = Object.freeze({\n client_source: AGENT_CANVAS_CLIENT_SOURCE,\n client_version: AGENT_CANVAS_CLIENT_VERSION,\n package_name: packageJson.name,\n package_version: packageJson.version,\n});\n\nlet telemetryBackendContext = getBackendTelemetryProperties({});\nlet telemetryCloudContext = getCloudTelemetryProperties();\n\nexport function setTelemetryBackendContext(\n context: BackendTelemetryContextInput,\n): void {\n telemetryBackendContext = getBackendTelemetryProperties(context);\n}\n\nexport function setTelemetryCloudContext(\n context: CloudTelemetryContextInput | null,\n): void {\n telemetryCloudContext = getCloudTelemetryProperties(context);\n}\n\nfunction addCanvasEventProperties(\n event: CaptureResult | null,\n): CaptureResult | null {\n if (!event) return null;\n\n return {\n ...event,\n properties: {\n ...telemetryBackendContext,\n ...telemetryCloudContext,\n ...event.properties,\n ...CANVAS_EVENT_PROPERTIES,\n },\n };\n}\n\nfunction restorePostHogConsent(posthog: PostHog): void {\n if (telemetryDisabled || getTelemetryConsent() !== \"granted\") {\n posthog.opt_out_capturing();\n } else {\n posthog.opt_in_capturing();\n }\n}\n\nfunction resetPostHogIdentity(posthog: PostHog, resetDeviceId = false): void {\n posthog.reset(resetDeviceId);\n appliedIdentityRevision = -1;\n // PostHog reset clears its own consent persistence, so immediately restore\n // the canonical Canvas decision kept in localStorage.\n restorePostHogConsent(posthog);\n}\n\nfunction applyDesiredTelemetryIdentity(posthog: PostHog): void {\n if (desiredTelemetryIdentity === undefined || !isTelemetryEnabled()) return;\n\n const desiredId = desiredTelemetryIdentity?.distinctId;\n const currentId = posthog.get_property(\"$user_id\");\n if (currentId != null && currentId !== desiredId) {\n resetPostHogIdentity(posthog);\n }\n\n if (desiredTelemetryIdentity === null) {\n appliedIdentityRevision = desiredIdentityRevision;\n return;\n }\n\n if (\n posthog.get_property(\"$user_id\") !== desiredTelemetryIdentity.distinctId ||\n appliedIdentityRevision !== desiredIdentityRevision\n ) {\n posthog.identify(\n desiredTelemetryIdentity.distinctId,\n desiredTelemetryIdentity.properties,\n );\n appliedIdentityRevision = desiredIdentityRevision;\n }\n}\n\nfunction propertiesEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every((key) => left[key] === right[key])\n );\n}\n\n/**\n * Configure the single Canvas telemetry client before its first use.\n * Passing false disables telemetry and install tracking for embedded hosts.\n */\nexport function configureTelemetry(config: TelemetryConfiguration): void {\n if (config === false) {\n if (telemetryDisabled) return;\n telemetryDisabled = true;\n posthogInstance?.opt_out_capturing();\n notifyTelemetryConsentListeners();\n return;\n }\n\n const wasDisabled = telemetryDisabled;\n telemetryDisabled = false;\n\n if (!posthogInstance && !initializationPromise) {\n const definedConfig = Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as TelemetryConfig;\n telemetryConfig = { ...telemetryConfig, ...definedConfig };\n }\n\n if (wasDisabled) {\n if (posthogInstance) {\n restorePostHogConsent(posthogInstance);\n applyDesiredTelemetryIdentity(posthogInstance);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\nfunction getResolvedTelemetryConfig(): Required<TelemetryConfig> | null {\n if (telemetryDisabled) return null;\n\n return {\n apiKey: telemetryConfig.apiKey || DEFAULT_POSTHOG_API_KEY,\n apiHost: telemetryConfig.apiHost || DEFAULT_POSTHOG_HOST,\n uiHost: telemetryConfig.uiHost || DEFAULT_POSTHOG_UI_HOST,\n };\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof localStorage !== \"undefined\";\n}\n\n/**\n * Lazily load PostHog to avoid SSR/Node.js issues.\n * PostHog is a browser-only library, so we dynamically import it only when needed.\n */\nasync function getPostHog(): Promise<PostHog | null> {\n if (!isBrowser()) {\n return null;\n }\n\n if (posthogInstance) {\n return posthogInstance;\n }\n\n try {\n const { default: posthog } = await import(\"posthog-js\");\n return posthog;\n } catch {\n // Failed to load PostHog - telemetry will be disabled\n return null;\n }\n}\n\n/**\n * Check if telemetry is disabled via environment variable or browser setting.\n * Works in both Node.js and browser (Vite) environments.\n */\nfunction isDoNotTrackEnabled(): boolean {\n if (telemetryDisabled) {\n return true;\n }\n\n // Check Vite environment variable (browser)\n if (\n typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DO_NOT_TRACK === \"1\"\n ) {\n return true;\n }\n\n // Check Node.js environment variable (SSR/testing)\n if (typeof process !== \"undefined\" && process.env?.DO_NOT_TRACK === \"1\") {\n return true;\n }\n\n // Check browser's navigator.doNotTrack standard\n if (\n typeof navigator !== \"undefined\" &&\n (navigator.doNotTrack === \"1\" ||\n // @ts-expect-error - Some browsers use window.doNotTrack\n (typeof window !== \"undefined\" && window.doNotTrack === \"1\"))\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Initialize PostHog SDK.\n *\n * @param enableCapturing - If true, enable capturing immediately (for install tracking).\n * If false, start with capturing disabled (for consent-gated tracking).\n */\nexport function configurePostHogBootstrap(\n bootstrap: BootstrapConfig | undefined,\n): void {\n if (!posthogInstance) {\n pendingBootstrap = bootstrap;\n }\n}\n\nexport async function initializePostHogClient(\n enableCapturing = false,\n): Promise<PostHog | null> {\n if (posthogInstance) {\n return posthogInstance;\n }\n\n if (initializationPromise) {\n return initializationPromise;\n }\n\n initializationPromise = (async () => {\n const config = getResolvedTelemetryConfig();\n if (!config) {\n return null;\n }\n\n const posthog = await getPostHog();\n if (!posthog) {\n return null;\n }\n\n // A named instance isolates Canvas configuration, consent, identity, and\n // persistence from a host application's default PostHog singleton.\n const initializedPostHog = posthog.init(\n config.apiKey,\n {\n api_host: config.apiHost,\n ui_host: config.uiHost,\n opt_out_capturing_by_default: !enableCapturing,\n persistence: \"localStorage\",\n persistence_name: POSTHOG_INSTANCE_NAME,\n consent_persistence_name: `${POSTHOG_INSTANCE_NAME}-consent`,\n person_profiles: \"always\",\n capture_pageview: POSTHOG_PAGEVIEW_CAPTURE_MODE,\n autocapture: true,\n disable_session_recording: true,\n bootstrap: pendingBootstrap,\n before_send: addCanvasEventProperties,\n },\n POSTHOG_INSTANCE_NAME,\n );\n if (!initializedPostHog) return null;\n\n posthogInstance = initializedPostHog;\n pendingBootstrap = undefined;\n\n if (telemetryDisabled) {\n posthogInstance.opt_out_capturing();\n } else if (getTelemetryConsent() === \"granted\") {\n posthogInstance.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthogInstance);\n } else if (!enableCapturing) {\n posthogInstance.opt_out_capturing();\n }\n\n return posthogInstance;\n })();\n\n try {\n return await initializationPromise;\n } finally {\n if (!posthogInstance) {\n initializationPromise = null;\n }\n }\n}\n\n/**\n * Get user's telemetry consent preference\n */\nexport function getTelemetryConsent(): TelemetryConsent {\n if (!isBrowser()) {\n return \"pending\";\n }\n\n // Check environment variable for opt-out\n if (isDoNotTrackEnabled()) {\n return \"denied\";\n }\n\n if (getLockedCloudAuthMode() === \"cookie\") {\n return \"granted\";\n }\n\n try {\n const consent = localStorage.getItem(TELEMETRY_CONSENT_KEY);\n if (consent === \"granted\" || consent === \"denied\") {\n return consent;\n }\n } catch {\n // Ignore storage errors\n }\n\n return \"pending\";\n}\n\n/**\n * Return an explicit browser choice that still needs to survive a Cloud login.\n * It remains pending across local backends so their settings cannot consume a\n * decision that must still be applied after the user connects to Cloud.\n */\nexport function getPendingCloudTelemetryConsent(): ResolvedTelemetryConsent | null {\n if (!isBrowser()) return null;\n\n try {\n const consent = localStorage.getItem(\n TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY,\n );\n return consent === \"granted\" || consent === \"denied\" ? consent : null;\n } catch {\n return null;\n }\n}\n\n/** Return the pre-reset actor whose local Automation consent must be revoked. */\nexport function getPendingLocalTelemetryRevocationId(): string | null {\n if (!isBrowser()) return null;\n\n try {\n return localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n return null;\n }\n}\n\nexport function subscribeTelemetryConsent(listener: () => void): () => void {\n if (!isBrowser()) return () => {};\n const handleStorage = (event: StorageEvent) => {\n if (\n event.key === TELEMETRY_CONSENT_KEY ||\n event.key === TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY\n ) {\n listener();\n }\n };\n window.addEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.addEventListener(\"storage\", handleStorage);\n\n return () => {\n window.removeEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction notifyTelemetryConsentListeners(): void {\n if (isBrowser())\n window.dispatchEvent(new Event(TELEMETRY_CONSENT_CHANGE_EVENT));\n}\n\nfunction markTelemetryConsentForCloudSync(\n consent: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n localStorage.setItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY, consent);\n } catch {\n // Ignore storage errors; the in-browser consent decision still applies.\n }\n}\n\nfunction markPendingLocalTelemetryRevocation(distinctId: string | null): void {\n if (!isBrowser() || !distinctId) return;\n\n try {\n localStorage.setItem(\n TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY,\n distinctId,\n );\n } catch {\n // Ignore storage errors; browser capture is still disabled below.\n }\n}\n\nexport function clearPendingLocalTelemetryRevocation(\n expectedDistinctId: string,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY) !==\n expectedDistinctId\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n // Ignore storage errors.\n }\n}\n\nexport function clearPendingCloudTelemetryConsent(\n expected?: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n expected !== undefined &&\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY) !==\n expected\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY);\n notifyTelemetryConsentListeners();\n } catch {\n // Ignore storage errors.\n }\n}\n\n/**\n * Set user's telemetry consent preference\n */\nexport async function setTelemetryConsent(\n consent: ResolvedTelemetryConsent,\n { syncToCloud = true }: SetTelemetryConsentOptions = {},\n): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n const previousConsent = getTelemetryConsent();\n const unchanged = previousConsent === consent;\n localStorage.setItem(TELEMETRY_CONSENT_KEY, consent);\n if (telemetryDisabled) return;\n if (unchanged) return;\n\n // Reuse an initialized client synchronously so a same-flush identify()\n // cannot run before consent is applied. Only the cold path awaits import.\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (!posthog) {\n return;\n }\n\n if (consent === \"granted\") {\n posthog.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthog);\n } else {\n markPendingLocalTelemetryRevocation(posthog.get_distinct_id?.() ?? null);\n if (posthog.get_property?.(\"$user_id\") != null) {\n resetPostHogIdentity(posthog);\n } else {\n posthog.opt_out_capturing();\n }\n }\n } catch {\n // Ignore storage errors\n } finally {\n // Notify UI/backend/identity reconcilers only after the browser capture state\n // reflects this decision. Otherwise a pre-login grant can trigger an\n // identify while PostHog is still opted out and lose funnel continuity.\n if (syncToCloud) {\n markTelemetryConsentForCloudSync(consent);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\n/**\n * Declare the current Cloud identity. The telemetry service applies it only\n * after consent and owns all reset/account-switch semantics.\n */\nexport async function setTelemetryIdentity(\n distinctId: string | null,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const nextIdentity = distinctId === null ? null : { distinctId, properties };\n const unchanged =\n desiredTelemetryIdentity === nextIdentity ||\n (desiredTelemetryIdentity !== undefined &&\n desiredTelemetryIdentity !== null &&\n nextIdentity !== null &&\n desiredTelemetryIdentity.distinctId === nextIdentity.distinctId &&\n propertiesEqual(desiredTelemetryIdentity.properties, properties));\n if (unchanged) return;\n\n desiredTelemetryIdentity = nextIdentity;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n if (!isTelemetryEnabled()) return;\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (posthog && isTelemetryEnabled()) {\n // Read the desired identity after the await so a newer account always wins\n // if identity changes while the SDK is loading.\n applyDesiredTelemetryIdentity(posthog);\n }\n}\n\n/**\n * Check if telemetry is enabled (user has granted consent)\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryConsent() === \"granted\";\n}\n\n/**\n * Check if first use event has already been sent\n */\nfunction hasFirstUseSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return localStorage.getItem(TELEMETRY_FIRST_USE_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark first use event as sent\n */\nfunction markFirstUseSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n localStorage.setItem(TELEMETRY_FIRST_USE_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Track the initial install of the library.\n *\n * IMPORTANT: This is sent immediately on first use, regardless of consent status.\n * This allows us to track library adoption even if users haven't made a consent choice yet.\n *\n * The event is:\n * - Completely anonymous (no PII, just a random PostHog distinct_id)\n * - Sent only once per installation (tracked via localStorage, persists across sessions)\n * - Still respects DO_NOT_TRACK environment variable and browser setting\n *\n * Users who want complete privacy can:\n * - Set VITE_DO_NOT_TRACK=1 or browser's Do Not Track\n * - Later deny consent to prevent all future tracking\n */\nexport async function trackInstall(): Promise<void> {\n // Respect hard opt-out via environment variable or browser setting\n if (isDoNotTrackEnabled()) {\n return;\n }\n\n // Already sent install event (persisted in localStorage - survives app relaunches)\n if (hasFirstUseSent()) {\n return;\n }\n\n // Initialize PostHog with capturing enabled (for this one event)\n const posthog = await initializePostHogClient(true);\n if (!posthog || isDoNotTrackEnabled()) {\n return;\n }\n\n // Temporarily enable capturing if it was disabled\n const wasOptedOut = posthog.has_opted_out_capturing?.() ?? false;\n if (wasOptedOut) {\n posthog.opt_in_capturing();\n }\n\n // Capture the install event\n posthog.capture(\"canvas_install\", {\n platform: typeof navigator !== \"undefined\" ? navigator.platform : \"unknown\",\n user_agent:\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n url_origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n embedded: typeof window !== \"undefined\" && window.self !== window.top,\n });\n\n // Mark as sent (stored in localStorage - persists across browser sessions)\n markFirstUseSent();\n\n // Restore opt-out state if user hasn't granted consent yet\n // This ensures we only send the install event, not subsequent events\n const currentConsent = getTelemetryConsent();\n if (currentConsent !== \"granted\") {\n posthog.opt_out_capturing();\n }\n}\n\n/**\n * Check if session start event has already been sent (this browser session)\n */\nfunction hasSessionSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return sessionStorage.getItem(TELEMETRY_SESSION_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark session start event as sent (uses sessionStorage so it resets on new tabs/sessions)\n */\nfunction markSessionSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n sessionStorage.setItem(TELEMETRY_SESSION_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/** Return the shared client only when a consented capture is safe to emit. */\nasync function getPostHogForConsentedCapture(): Promise<PostHog | null> {\n if (!isTelemetryEnabled() || getTelemetryConsent() !== \"granted\") return null;\n\n const posthog = await initializePostHogClient();\n if (!posthog || !isTelemetryEnabled()) return null;\n\n // The browser preference is the canonical capture decision. PostHog may\n // still carry an older opt-out marker while backend consent is loading or\n // after a previous backend temporarily reported a stale value. Heal that\n // drift at the event boundary so capture() cannot silently discard an event\n // that the user has explicitly allowed.\n if (posthog.has_opted_out_capturing?.()) {\n posthog.opt_in_capturing();\n }\n\n applyDesiredTelemetryIdentity(posthog);\n\n return posthog;\n}\n\n/**\n * Track a session start event.\n * Called each time a new browser session starts (respects consent).\n * Uses sessionStorage for deduplication - only sends once per browser session.\n */\nexport async function trackSessionStart(): Promise<void> {\n // Already sent session event this browser session\n if (hasSessionSent()) {\n return;\n }\n\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(\"canvas_new_session\", {\n is_first_use: !hasFirstUseSent(),\n });\n\n // Mark as sent for this session\n markSessionSent();\n}\n\n/** Return the active PostHog distinct ID when consent allows capture. */\nexport async function getTelemetryDistinctId(): Promise<string | null> {\n const posthog = await getPostHogForConsentedCapture();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/** Return the PostHog distinct ID for local consent sync without emitting capture. */\nexport async function getTelemetryDistinctIdForConsentSync(): Promise<\n string | null\n> {\n if (!isBrowser()) return null;\n\n if (getTelemetryConsent() !== \"granted\") {\n const pendingRevocationId = getPendingLocalTelemetryRevocationId();\n if (pendingRevocationId) return pendingRevocationId;\n }\n\n if (telemetryDisabled || isDoNotTrackEnabled()) return null;\n\n const posthog = await initializePostHogClient();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/**\n * Track a custom event (respects consent).\n */\nexport async function trackEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(eventName, properties);\n}\n\n/** Track an exception through the same consent-aware client as custom events. */\nexport async function trackException(\n error: unknown,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.captureException(error, properties);\n}\n\n/**\n * Clear all telemetry data (for privacy/GDPR requests)\n */\nexport async function clearTelemetryData(): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n markPendingLocalTelemetryRevocation(\n posthogInstance?.get_distinct_id?.() ?? null,\n );\n localStorage.removeItem(TELEMETRY_CONSENT_KEY);\n localStorage.removeItem(TELEMETRY_FIRST_USE_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n clearPendingCloudTelemetryConsent();\n try {\n sessionStorage.removeItem(TELEMETRY_SESSION_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n\n telemetryBackendContext = getBackendTelemetryProperties({});\n telemetryCloudContext = getCloudTelemetryProperties();\n desiredTelemetryIdentity = null;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n try {\n if (posthogInstance) {\n resetPostHogIdentity(posthogInstance, true);\n }\n } catch {\n // A reset failure must not leave capture enabled after a privacy clear.\n try {\n posthogInstance?.opt_out_capturing();\n } catch {\n // Telemetry failures must not break the application.\n }\n } finally {\n notifyTelemetryConsentListeners();\n }\n}\n"],"mappings":"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"}
@@ -3,43 +3,43 @@ import t from "../package.js";
3
3
  import { AGENT_CANVAS_CLIENT_SOURCE as n, AGENT_CANVAS_CLIENT_VERSION as r } from "../api/client-source.js";
4
4
  import { getBackendTelemetryProperties as i, getCloudTelemetryProperties as a } from "./telemetry-context.js";
5
5
  //#region src/services/telemetry.ts
6
- var o = "openhands-telemetry-consent", s = "openhands-telemetry-consent-pending-cloud-sync", c = "openhands-telemetry-consent-pending-local-revocation", l = "openhands-telemetry-consent-change", u = "openhands-telemetry-first-use", d = "openhands-telemetry-session", f = "agent-canvas", p = "history_change", ee = "phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK", m = "https://z.openhands.dev", h = "https://us.posthog.com", g = null, _ = null, v, y = {}, b = !1, x, S = 0, C = -1, w = Object.freeze({
6
+ var o = "openhands-telemetry-consent", s = "openhands-telemetry-consent-pending-cloud-sync", c = "openhands-telemetry-consent-pending-local-revocation", l = "openhands-telemetry-consent-change", u = "openhands-telemetry-first-use", d = "openhands-telemetry-session", f = "agent-canvas", p = "history_change", m = "phc_BgzfxKdgsYMLFTmJqt424ZoyVHvKFfrwttLimzdYTKFK", h = "https://z.openhands.dev", g = "https://us.posthog.com", _ = null, v = null, y, b = {}, x = !1, S, C = 0, w = -1, T = Object.freeze({
7
7
  client_source: n,
8
8
  client_version: r,
9
9
  package_name: t.name,
10
10
  package_version: t.version
11
- }), T = i({}), E = a();
12
- function D(e) {
13
- T = i(e);
14
- }
11
+ }), E = i({}), D = a();
15
12
  function O(e) {
16
- E = a(e);
13
+ E = i(e);
17
14
  }
18
15
  function k(e) {
16
+ D = a(e);
17
+ }
18
+ function ee(e) {
19
19
  return e ? {
20
20
  ...e,
21
21
  properties: {
22
- ...T,
23
22
  ...E,
23
+ ...D,
24
24
  ...e.properties,
25
- ...w
25
+ ...T
26
26
  }
27
27
  } : null;
28
28
  }
29
29
  function A(e) {
30
- b || V() !== "granted" ? e.opt_out_capturing() : e.opt_in_capturing();
30
+ x || V() !== "granted" ? e.opt_out_capturing() : e.opt_in_capturing();
31
31
  }
32
32
  function j(e, t = !1) {
33
- e.reset(t), C = -1, A(e);
33
+ e.reset(t), w = -1, A(e);
34
34
  }
35
35
  function M(e) {
36
- if (x === void 0 || !Z()) return;
37
- let t = x?.distinctId, n = e.get_property("$user_id");
38
- if (n != null && n !== t && j(e), x === null) {
39
- C = S;
36
+ if (S === void 0 || !Z()) return;
37
+ let t = S?.distinctId, n = e.get_property("$user_id");
38
+ if (n != null && n !== t && j(e), S === null) {
39
+ w = C;
40
40
  return;
41
41
  }
42
- (e.get_property("$user_id") !== x.distinctId || C !== S) && (e.identify(x.distinctId, x.properties), C = S);
42
+ (e.get_property("$user_id") !== S.distinctId || w !== C) && (e.identify(S.distinctId, S.properties), w = C);
43
43
  }
44
44
  function N(e, t) {
45
45
  let n = Object.keys(e);
@@ -47,25 +47,25 @@ function N(e, t) {
47
47
  }
48
48
  function P(e) {
49
49
  if (e === !1) {
50
- if (b) return;
51
- b = !0, g?.opt_out_capturing(), W();
50
+ if (x) return;
51
+ x = !0, _?.opt_out_capturing(), W();
52
52
  return;
53
53
  }
54
- let t = b;
55
- if (b = !1, !g && !_) {
54
+ let t = x;
55
+ if (x = !1, !_ && !v) {
56
56
  let t = Object.fromEntries(Object.entries(e).filter(([, e]) => e !== void 0));
57
- y = {
58
- ...y,
57
+ b = {
58
+ ...b,
59
59
  ...t
60
60
  };
61
61
  }
62
- t && (g && (A(g), M(g)), W());
62
+ t && (_ && (A(_), M(_)), W());
63
63
  }
64
64
  function F() {
65
- return b ? null : {
66
- apiKey: y.apiKey || ee,
67
- apiHost: y.apiHost || m,
68
- uiHost: y.uiHost || h
65
+ return x ? null : {
66
+ apiKey: b.apiKey || m,
67
+ apiHost: b.apiHost || h,
68
+ uiHost: b.uiHost || g
69
69
  };
70
70
  }
71
71
  function I() {
@@ -73,7 +73,7 @@ function I() {
73
73
  }
74
74
  async function L() {
75
75
  if (!I()) return null;
76
- if (g) return g;
76
+ if (_) return _;
77
77
  try {
78
78
  let { default: e } = await import("../node_modules/posthog-js/dist/module.js");
79
79
  return e;
@@ -82,15 +82,15 @@ async function L() {
82
82
  }
83
83
  }
84
84
  function R() {
85
- return !!(b || typeof process < "u" && process.env?.DO_NOT_TRACK === "1" || typeof navigator < "u" && (navigator.doNotTrack === "1" || typeof window < "u" && window.doNotTrack === "1"));
85
+ return !!(x || typeof process < "u" && process.env?.DO_NOT_TRACK === "1" || typeof navigator < "u" && (navigator.doNotTrack === "1" || typeof window < "u" && window.doNotTrack === "1"));
86
86
  }
87
87
  function z(e) {
88
- g || (v = e);
88
+ _ || (y = e);
89
89
  }
90
90
  async function B(e = !1) {
91
- if (g) return g;
92
91
  if (_) return _;
93
- _ = (async () => {
92
+ if (v) return v;
93
+ v = (async () => {
94
94
  let t = F();
95
95
  if (!t) return null;
96
96
  let n = await L();
@@ -106,15 +106,15 @@ async function B(e = !1) {
106
106
  capture_pageview: p,
107
107
  autocapture: !0,
108
108
  disable_session_recording: !0,
109
- bootstrap: v,
110
- before_send: k
109
+ bootstrap: y,
110
+ before_send: ee
111
111
  }, f);
112
- return r ? (g = r, v = void 0, b ? g.opt_out_capturing() : V() === "granted" ? (g.opt_in_capturing(), M(g)) : e || g.opt_out_capturing(), g) : null;
112
+ return r ? (_ = r, y = void 0, x ? _.opt_out_capturing() : V() === "granted" ? (_.opt_in_capturing(), M(_)) : e || _.opt_out_capturing(), _) : null;
113
113
  })();
114
114
  try {
115
- return await _;
115
+ return await v;
116
116
  } finally {
117
- g || (_ = null);
117
+ _ || (v = null);
118
118
  }
119
119
  }
120
120
  function V() {
@@ -172,8 +172,8 @@ function J(e) {
172
172
  async function Y(e, { syncToCloud: t = !0 } = {}) {
173
173
  if (I()) try {
174
174
  let t = V() === e;
175
- if (localStorage.setItem(o, e), b || t) return;
176
- let n = g ?? await B();
175
+ if (localStorage.setItem(o, e), x || t) return;
176
+ let n = _ ?? await B();
177
177
  if (!n) return;
178
178
  e === "granted" ? (n.opt_in_capturing(), M(n)) : (K(n.get_distinct_id?.() ?? null), n.get_property?.("$user_id") == null ? n.opt_out_capturing() : j(n));
179
179
  } catch {} finally {
@@ -185,8 +185,8 @@ async function X(e, t = {}) {
185
185
  distinctId: e,
186
186
  properties: t
187
187
  };
188
- if (x === n || x != null && n !== null && x.distinctId === n.distinctId && N(x.properties, t) || (x = n, S += 1, C = -1, !Z())) return;
189
- let r = g ?? await B();
188
+ if (S === n || S != null && n !== null && S.distinctId === n.distinctId && N(S.properties, t) || (S = n, C += 1, w = -1, !Z())) return;
189
+ let r = _ ?? await B();
190
190
  r && Z() && M(r);
191
191
  }
192
192
  function Z() {
@@ -248,31 +248,27 @@ async function se() {
248
248
  let e = H();
249
249
  if (e) return e;
250
250
  }
251
- return b || R() ? null : (await B())?.get_distinct_id?.() ?? null;
251
+ return x || R() ? null : (await B())?.get_distinct_id?.() ?? null;
252
252
  }
253
253
  async function ce(e, t = {}) {
254
254
  let n = await $();
255
255
  n && n.capture(e, t);
256
256
  }
257
- async function le(e, t = {}) {
258
- let n = await $();
259
- n && n.captureException(e, t);
260
- }
261
- async function ue() {
257
+ async function le() {
262
258
  if (I()) {
263
259
  try {
264
- K(g?.get_distinct_id?.() ?? null), localStorage.removeItem(o), localStorage.removeItem(u);
260
+ K(_?.get_distinct_id?.() ?? null), localStorage.removeItem(o), localStorage.removeItem(u);
265
261
  } catch {}
266
262
  J();
267
263
  try {
268
264
  sessionStorage.removeItem(d);
269
265
  } catch {}
270
- T = i({}), E = a(), x = null, S += 1, C = -1;
266
+ E = i({}), D = a(), S = null, C += 1, w = -1;
271
267
  try {
272
- g && j(g, !0);
268
+ _ && j(_, !0);
273
269
  } catch {
274
270
  try {
275
- g?.opt_out_capturing();
271
+ _?.opt_out_capturing();
276
272
  } catch {}
277
273
  } finally {
278
274
  W();
@@ -280,6 +276,6 @@ async function ue() {
280
276
  }
281
277
  }
282
278
  //#endregion
283
- export { q as clearPendingLocalTelemetryRevocation, ue as clearTelemetryData, z as configurePostHogBootstrap, P as configureTelemetry, V as getTelemetryConsent, oe as getTelemetryDistinctId, se as getTelemetryDistinctIdForConsentSync, B as initializePostHogClient, Z as isTelemetryEnabled, D as setTelemetryBackendContext, O as setTelemetryCloudContext, Y as setTelemetryConsent, X as setTelemetryIdentity, U as subscribeTelemetryConsent, ce as trackEvent, le as trackException, ne as trackInstall, ae as trackSessionStart };
279
+ export { q as clearPendingLocalTelemetryRevocation, le as clearTelemetryData, z as configurePostHogBootstrap, P as configureTelemetry, V as getTelemetryConsent, oe as getTelemetryDistinctId, se as getTelemetryDistinctIdForConsentSync, B as initializePostHogClient, Z as isTelemetryEnabled, O as setTelemetryBackendContext, k as setTelemetryCloudContext, Y as setTelemetryConsent, X as setTelemetryIdentity, U as subscribeTelemetryConsent, ce as trackEvent, ne as trackInstall, ae as trackSessionStart };
284
280
 
285
281
  //# sourceMappingURL=telemetry.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"telemetry.js","names":[],"sources":["../../src/services/telemetry.ts"],"sourcesContent":["/**\n * Telemetry service for tracking library usage.\n *\n * This module handles anonymous telemetry for the @openhands/agent-canvas package\n * using the PostHog SDK for reliable event delivery with batching, retry logic,\n * and offline support.\n *\n * TRACKING PHILOSOPHY:\n * - Install event (canvas_install): Sent immediately on first use, regardless of consent.\n * This is anonymous and contains no PII - just basic browser info and a random ID.\n * - Session/custom events: Only sent after user grants consent via the consent modal.\n * - Users can opt out of all future tracking by declining consent.\n *\n * AD BLOCKER BYPASS:\n * By default, telemetry is routed through OpenHands' reverse proxy (z.openhands.dev)\n * to avoid being blocked by ad blockers. Library consumers can override this with:\n * - VITE_POSTHOG_HOST: Custom proxy URL or direct PostHog URL\n * - VITE_POSTHOG_UI_HOST: PostHog UI host (defaults to https://us.posthog.com)\n *\n * IMPORTANT: By default, telemetry is sent to the OpenHands PostHog project.\n * Source builds can override this with VITE_POSTHOG_API_KEY. Precompiled\n * library consumers can pass the same settings to configureTelemetry().\n *\n * Users can disable all telemetry (including install tracking) via:\n * - Setting VITE_DO_NOT_TRACK=1 environment variable\n * - Browser's Do Not Track setting\n */\n\nimport type { BootstrapConfig, CaptureResult, PostHog } from \"posthog-js\";\nimport { getLockedCloudAuthMode } from \"#/api/agent-server-config\";\nimport defaults from \"../../config/defaults.json\";\n\nimport packageJson from \"../../package.json\";\nimport {\n AGENT_CANVAS_CLIENT_SOURCE,\n AGENT_CANVAS_CLIENT_VERSION,\n} from \"#/api/client-source\";\nimport {\n getBackendTelemetryProperties,\n getCloudTelemetryProperties,\n type BackendTelemetryContextInput,\n type CloudTelemetryContextInput,\n} from \"#/services/telemetry-context\";\n\nconst TELEMETRY_CONSENT_KEY = \"openhands-telemetry-consent\";\nconst TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY =\n \"openhands-telemetry-consent-pending-cloud-sync\";\nconst TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY =\n \"openhands-telemetry-consent-pending-local-revocation\";\nconst TELEMETRY_CONSENT_CHANGE_EVENT = \"openhands-telemetry-consent-change\";\nconst TELEMETRY_FIRST_USE_KEY = \"openhands-telemetry-first-use\";\nconst TELEMETRY_SESSION_KEY = \"openhands-telemetry-session\";\nconst POSTHOG_INSTANCE_NAME = \"agent-canvas\";\nconst POSTHOG_PAGEVIEW_CAPTURE_MODE = \"history_change\";\n\n// Unconfigured source builds use staging. Production release workflows pass\n// VITE_POSTHOG_API_KEY explicitly for both the app and library artifacts.\nconst DEFAULT_POSTHOG_API_KEY: string =\n (import.meta.env.VITE_POSTHOG_API_KEY as string | undefined) ||\n defaults.telemetry.posthogApiKey;\n\n// Default to OpenHands' reverse proxy to bypass ad blockers.\n// The proxy at z.openhands.dev routes to PostHog's US region.\n// Library consumers can override this with their own proxy or direct PostHog URL.\nconst DEFAULT_POSTHOG_HOST =\n import.meta.env.VITE_POSTHOG_HOST || \"https://z.openhands.dev\";\n\n// UI host is needed for PostHog features like toolbar to work correctly\n// when using a reverse proxy. Defaults to US region.\nconst DEFAULT_POSTHOG_UI_HOST =\n import.meta.env.VITE_POSTHOG_UI_HOST || \"https://us.posthog.com\";\n\nexport interface TelemetryConfig {\n /** PostHog project key. Useful for precompiled library consumers. */\n apiKey?: string;\n /** Event ingestion host or reverse proxy. */\n apiHost?: string;\n /** PostHog UI host used by toolbar links and other UI features. */\n uiHost?: string;\n}\n\nexport type TelemetryConfiguration = TelemetryConfig | false;\n\nexport type TelemetryConsent = \"granted\" | \"denied\" | \"pending\";\nexport type ResolvedTelemetryConsent = Exclude<TelemetryConsent, \"pending\">;\n\nexport interface SetTelemetryConsentOptions {\n /** Do not persist a value mirrored from backend settings back to Cloud. */\n syncToCloud?: boolean;\n}\n\nlet posthogInstance: PostHog | null = null;\nlet initializationPromise: Promise<PostHog | null> | null = null;\nlet pendingBootstrap: BootstrapConfig | undefined;\nlet telemetryConfig: TelemetryConfig = {};\nlet telemetryDisabled = false;\n\ninterface TelemetryIdentity {\n distinctId: string;\n properties: Record<string, unknown>;\n}\n\n// undefined means that Cloud identity has not resolved yet; null means that it\n// resolved without a user. This distinction prevents startup from resetting a\n// persisted identity while the current account is still loading.\nlet desiredTelemetryIdentity: TelemetryIdentity | null | undefined;\nlet desiredIdentityRevision = 0;\nlet appliedIdentityRevision = -1;\n\nconst CANVAS_EVENT_PROPERTIES = Object.freeze({\n client_source: AGENT_CANVAS_CLIENT_SOURCE,\n client_version: AGENT_CANVAS_CLIENT_VERSION,\n package_name: packageJson.name,\n package_version: packageJson.version,\n});\n\nlet telemetryBackendContext = getBackendTelemetryProperties({});\nlet telemetryCloudContext = getCloudTelemetryProperties();\n\nexport function setTelemetryBackendContext(\n context: BackendTelemetryContextInput,\n): void {\n telemetryBackendContext = getBackendTelemetryProperties(context);\n}\n\nexport function setTelemetryCloudContext(\n context: CloudTelemetryContextInput | null,\n): void {\n telemetryCloudContext = getCloudTelemetryProperties(context);\n}\n\nfunction addCanvasEventProperties(\n event: CaptureResult | null,\n): CaptureResult | null {\n if (!event) return null;\n\n return {\n ...event,\n properties: {\n ...telemetryBackendContext,\n ...telemetryCloudContext,\n ...event.properties,\n ...CANVAS_EVENT_PROPERTIES,\n },\n };\n}\n\nfunction restorePostHogConsent(posthog: PostHog): void {\n if (telemetryDisabled || getTelemetryConsent() !== \"granted\") {\n posthog.opt_out_capturing();\n } else {\n posthog.opt_in_capturing();\n }\n}\n\nfunction resetPostHogIdentity(posthog: PostHog, resetDeviceId = false): void {\n posthog.reset(resetDeviceId);\n appliedIdentityRevision = -1;\n // PostHog reset clears its own consent persistence, so immediately restore\n // the canonical Canvas decision kept in localStorage.\n restorePostHogConsent(posthog);\n}\n\nfunction applyDesiredTelemetryIdentity(posthog: PostHog): void {\n if (desiredTelemetryIdentity === undefined || !isTelemetryEnabled()) return;\n\n const desiredId = desiredTelemetryIdentity?.distinctId;\n const currentId = posthog.get_property(\"$user_id\");\n if (currentId != null && currentId !== desiredId) {\n resetPostHogIdentity(posthog);\n }\n\n if (desiredTelemetryIdentity === null) {\n appliedIdentityRevision = desiredIdentityRevision;\n return;\n }\n\n if (\n posthog.get_property(\"$user_id\") !== desiredTelemetryIdentity.distinctId ||\n appliedIdentityRevision !== desiredIdentityRevision\n ) {\n posthog.identify(\n desiredTelemetryIdentity.distinctId,\n desiredTelemetryIdentity.properties,\n );\n appliedIdentityRevision = desiredIdentityRevision;\n }\n}\n\nfunction propertiesEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every((key) => left[key] === right[key])\n );\n}\n\n/**\n * Configure the single Canvas telemetry client before its first use.\n * Passing false disables telemetry and install tracking for embedded hosts.\n */\nexport function configureTelemetry(config: TelemetryConfiguration): void {\n if (config === false) {\n if (telemetryDisabled) return;\n telemetryDisabled = true;\n posthogInstance?.opt_out_capturing();\n notifyTelemetryConsentListeners();\n return;\n }\n\n const wasDisabled = telemetryDisabled;\n telemetryDisabled = false;\n\n if (!posthogInstance && !initializationPromise) {\n const definedConfig = Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as TelemetryConfig;\n telemetryConfig = { ...telemetryConfig, ...definedConfig };\n }\n\n if (wasDisabled) {\n if (posthogInstance) {\n restorePostHogConsent(posthogInstance);\n applyDesiredTelemetryIdentity(posthogInstance);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\nfunction getResolvedTelemetryConfig(): Required<TelemetryConfig> | null {\n if (telemetryDisabled) return null;\n\n return {\n apiKey: telemetryConfig.apiKey || DEFAULT_POSTHOG_API_KEY,\n apiHost: telemetryConfig.apiHost || DEFAULT_POSTHOG_HOST,\n uiHost: telemetryConfig.uiHost || DEFAULT_POSTHOG_UI_HOST,\n };\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof localStorage !== \"undefined\";\n}\n\n/**\n * Lazily load PostHog to avoid SSR/Node.js issues.\n * PostHog is a browser-only library, so we dynamically import it only when needed.\n */\nasync function getPostHog(): Promise<PostHog | null> {\n if (!isBrowser()) {\n return null;\n }\n\n if (posthogInstance) {\n return posthogInstance;\n }\n\n try {\n const { default: posthog } = await import(\"posthog-js\");\n return posthog;\n } catch {\n // Failed to load PostHog - telemetry will be disabled\n return null;\n }\n}\n\n/**\n * Check if telemetry is disabled via environment variable or browser setting.\n * Works in both Node.js and browser (Vite) environments.\n */\nfunction isDoNotTrackEnabled(): boolean {\n if (telemetryDisabled) {\n return true;\n }\n\n // Check Vite environment variable (browser)\n if (\n typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DO_NOT_TRACK === \"1\"\n ) {\n return true;\n }\n\n // Check Node.js environment variable (SSR/testing)\n if (typeof process !== \"undefined\" && process.env?.DO_NOT_TRACK === \"1\") {\n return true;\n }\n\n // Check browser's navigator.doNotTrack standard\n if (\n typeof navigator !== \"undefined\" &&\n (navigator.doNotTrack === \"1\" ||\n // @ts-expect-error - Some browsers use window.doNotTrack\n (typeof window !== \"undefined\" && window.doNotTrack === \"1\"))\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Initialize PostHog SDK.\n *\n * @param enableCapturing - If true, enable capturing immediately (for install tracking).\n * If false, start with capturing disabled (for consent-gated tracking).\n */\nexport function configurePostHogBootstrap(\n bootstrap: BootstrapConfig | undefined,\n): void {\n if (!posthogInstance) {\n pendingBootstrap = bootstrap;\n }\n}\n\nexport async function initializePostHogClient(\n enableCapturing = false,\n): Promise<PostHog | null> {\n if (posthogInstance) {\n return posthogInstance;\n }\n\n if (initializationPromise) {\n return initializationPromise;\n }\n\n initializationPromise = (async () => {\n const config = getResolvedTelemetryConfig();\n if (!config) {\n return null;\n }\n\n const posthog = await getPostHog();\n if (!posthog) {\n return null;\n }\n\n // A named instance isolates Canvas configuration, consent, identity, and\n // persistence from a host application's default PostHog singleton.\n const initializedPostHog = posthog.init(\n config.apiKey,\n {\n api_host: config.apiHost,\n ui_host: config.uiHost,\n opt_out_capturing_by_default: !enableCapturing,\n persistence: \"localStorage\",\n persistence_name: POSTHOG_INSTANCE_NAME,\n consent_persistence_name: `${POSTHOG_INSTANCE_NAME}-consent`,\n person_profiles: \"always\",\n capture_pageview: POSTHOG_PAGEVIEW_CAPTURE_MODE,\n autocapture: true,\n disable_session_recording: true,\n bootstrap: pendingBootstrap,\n before_send: addCanvasEventProperties,\n },\n POSTHOG_INSTANCE_NAME,\n );\n if (!initializedPostHog) return null;\n\n posthogInstance = initializedPostHog;\n pendingBootstrap = undefined;\n\n if (telemetryDisabled) {\n posthogInstance.opt_out_capturing();\n } else if (getTelemetryConsent() === \"granted\") {\n posthogInstance.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthogInstance);\n } else if (!enableCapturing) {\n posthogInstance.opt_out_capturing();\n }\n\n return posthogInstance;\n })();\n\n try {\n return await initializationPromise;\n } finally {\n if (!posthogInstance) {\n initializationPromise = null;\n }\n }\n}\n\n/**\n * Get user's telemetry consent preference\n */\nexport function getTelemetryConsent(): TelemetryConsent {\n if (!isBrowser()) {\n return \"pending\";\n }\n\n // Check environment variable for opt-out\n if (isDoNotTrackEnabled()) {\n return \"denied\";\n }\n\n if (getLockedCloudAuthMode() === \"cookie\") {\n return \"granted\";\n }\n\n try {\n const consent = localStorage.getItem(TELEMETRY_CONSENT_KEY);\n if (consent === \"granted\" || consent === \"denied\") {\n return consent;\n }\n } catch {\n // Ignore storage errors\n }\n\n return \"pending\";\n}\n\n/**\n * Return an explicit browser choice that still needs to survive a Cloud login.\n * It remains pending across local backends so their settings cannot consume a\n * decision that must still be applied after the user connects to Cloud.\n */\nexport function getPendingCloudTelemetryConsent(): ResolvedTelemetryConsent | null {\n if (!isBrowser()) return null;\n\n try {\n const consent = localStorage.getItem(\n TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY,\n );\n return consent === \"granted\" || consent === \"denied\" ? consent : null;\n } catch {\n return null;\n }\n}\n\n/** Return the pre-reset actor whose local Automation consent must be revoked. */\nexport function getPendingLocalTelemetryRevocationId(): string | null {\n if (!isBrowser()) return null;\n\n try {\n return localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n return null;\n }\n}\n\nexport function subscribeTelemetryConsent(listener: () => void): () => void {\n if (!isBrowser()) return () => {};\n const handleStorage = (event: StorageEvent) => {\n if (\n event.key === TELEMETRY_CONSENT_KEY ||\n event.key === TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY\n ) {\n listener();\n }\n };\n window.addEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.addEventListener(\"storage\", handleStorage);\n\n return () => {\n window.removeEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction notifyTelemetryConsentListeners(): void {\n if (isBrowser())\n window.dispatchEvent(new Event(TELEMETRY_CONSENT_CHANGE_EVENT));\n}\n\nfunction markTelemetryConsentForCloudSync(\n consent: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n localStorage.setItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY, consent);\n } catch {\n // Ignore storage errors; the in-browser consent decision still applies.\n }\n}\n\nfunction markPendingLocalTelemetryRevocation(distinctId: string | null): void {\n if (!isBrowser() || !distinctId) return;\n\n try {\n localStorage.setItem(\n TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY,\n distinctId,\n );\n } catch {\n // Ignore storage errors; browser capture is still disabled below.\n }\n}\n\nexport function clearPendingLocalTelemetryRevocation(\n expectedDistinctId: string,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY) !==\n expectedDistinctId\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n // Ignore storage errors.\n }\n}\n\nexport function clearPendingCloudTelemetryConsent(\n expected?: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n expected !== undefined &&\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY) !==\n expected\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY);\n notifyTelemetryConsentListeners();\n } catch {\n // Ignore storage errors.\n }\n}\n\n/**\n * Set user's telemetry consent preference\n */\nexport async function setTelemetryConsent(\n consent: ResolvedTelemetryConsent,\n { syncToCloud = true }: SetTelemetryConsentOptions = {},\n): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n const previousConsent = getTelemetryConsent();\n const unchanged = previousConsent === consent;\n localStorage.setItem(TELEMETRY_CONSENT_KEY, consent);\n if (telemetryDisabled) return;\n if (unchanged) return;\n\n // Reuse an initialized client synchronously so a same-flush identify()\n // cannot run before consent is applied. Only the cold path awaits import.\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (!posthog) {\n return;\n }\n\n if (consent === \"granted\") {\n posthog.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthog);\n } else {\n markPendingLocalTelemetryRevocation(posthog.get_distinct_id?.() ?? null);\n if (posthog.get_property?.(\"$user_id\") != null) {\n resetPostHogIdentity(posthog);\n } else {\n posthog.opt_out_capturing();\n }\n }\n } catch {\n // Ignore storage errors\n } finally {\n // Notify UI/backend/identity reconcilers only after the browser capture state\n // reflects this decision. Otherwise a pre-login grant can trigger an\n // identify while PostHog is still opted out and lose funnel continuity.\n if (syncToCloud) {\n markTelemetryConsentForCloudSync(consent);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\n/**\n * Declare the current Cloud identity. The telemetry service applies it only\n * after consent and owns all reset/account-switch semantics.\n */\nexport async function setTelemetryIdentity(\n distinctId: string | null,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const nextIdentity = distinctId === null ? null : { distinctId, properties };\n const unchanged =\n desiredTelemetryIdentity === nextIdentity ||\n (desiredTelemetryIdentity !== undefined &&\n desiredTelemetryIdentity !== null &&\n nextIdentity !== null &&\n desiredTelemetryIdentity.distinctId === nextIdentity.distinctId &&\n propertiesEqual(desiredTelemetryIdentity.properties, properties));\n if (unchanged) return;\n\n desiredTelemetryIdentity = nextIdentity;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n if (!isTelemetryEnabled()) return;\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (posthog && isTelemetryEnabled()) {\n // Read the desired identity after the await so a newer account always wins\n // if identity changes while the SDK is loading.\n applyDesiredTelemetryIdentity(posthog);\n }\n}\n\n/**\n * Check if telemetry is enabled (user has granted consent)\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryConsent() === \"granted\";\n}\n\n/**\n * Check if first use event has already been sent\n */\nfunction hasFirstUseSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return localStorage.getItem(TELEMETRY_FIRST_USE_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark first use event as sent\n */\nfunction markFirstUseSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n localStorage.setItem(TELEMETRY_FIRST_USE_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Track the initial install of the library.\n *\n * IMPORTANT: This is sent immediately on first use, regardless of consent status.\n * This allows us to track library adoption even if users haven't made a consent choice yet.\n *\n * The event is:\n * - Completely anonymous (no PII, just a random PostHog distinct_id)\n * - Sent only once per installation (tracked via localStorage, persists across sessions)\n * - Still respects DO_NOT_TRACK environment variable and browser setting\n *\n * Users who want complete privacy can:\n * - Set VITE_DO_NOT_TRACK=1 or browser's Do Not Track\n * - Later deny consent to prevent all future tracking\n */\nexport async function trackInstall(): Promise<void> {\n // Respect hard opt-out via environment variable or browser setting\n if (isDoNotTrackEnabled()) {\n return;\n }\n\n // Already sent install event (persisted in localStorage - survives app relaunches)\n if (hasFirstUseSent()) {\n return;\n }\n\n // Initialize PostHog with capturing enabled (for this one event)\n const posthog = await initializePostHogClient(true);\n if (!posthog || isDoNotTrackEnabled()) {\n return;\n }\n\n // Temporarily enable capturing if it was disabled\n const wasOptedOut = posthog.has_opted_out_capturing?.() ?? false;\n if (wasOptedOut) {\n posthog.opt_in_capturing();\n }\n\n // Capture the install event\n posthog.capture(\"canvas_install\", {\n platform: typeof navigator !== \"undefined\" ? navigator.platform : \"unknown\",\n user_agent:\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n url_origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n embedded: typeof window !== \"undefined\" && window.self !== window.top,\n });\n\n // Mark as sent (stored in localStorage - persists across browser sessions)\n markFirstUseSent();\n\n // Restore opt-out state if user hasn't granted consent yet\n // This ensures we only send the install event, not subsequent events\n const currentConsent = getTelemetryConsent();\n if (currentConsent !== \"granted\") {\n posthog.opt_out_capturing();\n }\n}\n\n/**\n * Check if session start event has already been sent (this browser session)\n */\nfunction hasSessionSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return sessionStorage.getItem(TELEMETRY_SESSION_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark session start event as sent (uses sessionStorage so it resets on new tabs/sessions)\n */\nfunction markSessionSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n sessionStorage.setItem(TELEMETRY_SESSION_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/** Return the shared client only when a consented capture is safe to emit. */\nasync function getPostHogForConsentedCapture(): Promise<PostHog | null> {\n if (!isTelemetryEnabled() || getTelemetryConsent() !== \"granted\") return null;\n\n const posthog = await initializePostHogClient();\n if (!posthog || !isTelemetryEnabled()) return null;\n\n // The browser preference is the canonical capture decision. PostHog may\n // still carry an older opt-out marker while backend consent is loading or\n // after a previous backend temporarily reported a stale value. Heal that\n // drift at the event boundary so capture() cannot silently discard an event\n // that the user has explicitly allowed.\n if (posthog.has_opted_out_capturing?.()) {\n posthog.opt_in_capturing();\n }\n\n applyDesiredTelemetryIdentity(posthog);\n\n return posthog;\n}\n\n/**\n * Track a session start event.\n * Called each time a new browser session starts (respects consent).\n * Uses sessionStorage for deduplication - only sends once per browser session.\n */\nexport async function trackSessionStart(): Promise<void> {\n // Already sent session event this browser session\n if (hasSessionSent()) {\n return;\n }\n\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(\"canvas_new_session\", {\n is_first_use: !hasFirstUseSent(),\n });\n\n // Mark as sent for this session\n markSessionSent();\n}\n\n/** Return the active PostHog distinct ID when consent allows capture. */\nexport async function getTelemetryDistinctId(): Promise<string | null> {\n const posthog = await getPostHogForConsentedCapture();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/** Return the PostHog distinct ID for local consent sync without emitting capture. */\nexport async function getTelemetryDistinctIdForConsentSync(): Promise<\n string | null\n> {\n if (!isBrowser()) return null;\n\n if (getTelemetryConsent() !== \"granted\") {\n const pendingRevocationId = getPendingLocalTelemetryRevocationId();\n if (pendingRevocationId) return pendingRevocationId;\n }\n\n if (telemetryDisabled || isDoNotTrackEnabled()) return null;\n\n const posthog = await initializePostHogClient();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/**\n * Track a custom event (respects consent).\n */\nexport async function trackEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(eventName, properties);\n}\n\n/** Track an exception through the same consent-aware client as custom events. */\nexport async function trackException(\n error: unknown,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.captureException(error, properties);\n}\n\n/**\n * Clear all telemetry data (for privacy/GDPR requests)\n */\nexport async function clearTelemetryData(): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n markPendingLocalTelemetryRevocation(\n posthogInstance?.get_distinct_id?.() ?? null,\n );\n localStorage.removeItem(TELEMETRY_CONSENT_KEY);\n localStorage.removeItem(TELEMETRY_FIRST_USE_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n clearPendingCloudTelemetryConsent();\n try {\n sessionStorage.removeItem(TELEMETRY_SESSION_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n\n telemetryBackendContext = getBackendTelemetryProperties({});\n telemetryCloudContext = getCloudTelemetryProperties();\n desiredTelemetryIdentity = null;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n try {\n if (posthogInstance) {\n resetPostHogIdentity(posthogInstance, true);\n }\n } catch {\n // A reset failure must not leave capture enabled after a privacy clear.\n try {\n posthogInstance?.opt_out_capturing();\n } catch {\n // Telemetry failures must not break the application.\n }\n } finally {\n notifyTelemetryConsentListeners();\n }\n}\n"],"mappings":";;;;;AA4CA,IAAM,IAAwB,+BACxB,IACJ,kDACI,IACJ,wDACI,IAAiC,sCACjC,IAA0B,iCAC1B,IAAwB,+BACxB,IAAwB,gBACxB,IAAgC,kBAIhC,KAAA,oDAOA,IACiC,2BAIjC,IACoC,0BAqBtC,IAAkC,MAClC,IAAwD,MACxD,GACA,IAAmC,CAAC,GACpC,IAAoB,IAUpB,GACA,IAA0B,GAC1B,IAA0B,IAExB,IAA0B,OAAO,OAAO;CAC5C,eAAe;CACf,gBAAgB;CAChB,cAAc,EAAY;CAC1B,iBAAiB,EAAY;AAC/B,CAAC,GAEG,IAA0B,EAA8B,CAAC,CAAC,GAC1D,IAAwB,EAA4B;AAExD,SAAgB,EACd,GACM;CACN,IAA0B,EAA8B,CAAO;AACjE;AAEA,SAAgB,EACd,GACM;CACN,IAAwB,EAA4B,CAAO;AAC7D;AAEA,SAAS,EACP,GACsB;CAGtB,OAFK,IAEE;EACL,GAAG;EACH,YAAY;GACV,GAAG;GACH,GAAG;GACH,GAAG,EAAM;GACT,GAAG;EACL;CACF,IAVmB;AAWrB;AAEA,SAAS,EAAsB,GAAwB;CACrD,AAAI,KAAqB,EAAoB,MAAM,YACjD,EAAQ,kBAAkB,IAE1B,EAAQ,iBAAiB;AAE7B;AAEA,SAAS,EAAqB,GAAkB,IAAgB,IAAa;CAK3E,AAJA,EAAQ,MAAM,CAAa,GAC3B,IAA0B,IAG1B,EAAsB,CAAO;AAC/B;AAEA,SAAS,EAA8B,GAAwB;CAC7D,IAAI,MAA6B,KAAA,KAAa,CAAC,EAAmB,GAAG;CAErE,IAAM,IAAY,GAA0B,YACtC,IAAY,EAAQ,aAAa,UAAU;CAKjD,IAJI,KAAa,QAAQ,MAAc,KACrC,EAAqB,CAAO,GAG1B,MAA6B,MAAM;EACrC,IAA0B;EAC1B;CACF;CAEA,CACE,EAAQ,aAAa,UAAU,MAAM,EAAyB,cAC9D,MAA4B,OAE5B,EAAQ,SACN,EAAyB,YACzB,EAAyB,UAC3B,GACA,IAA0B;AAE9B;AAEA,SAAS,EACP,GACA,GACS;CACT,IAAM,IAAO,OAAO,KAAK,CAAI;CAC7B,OACE,EAAK,WAAW,OAAO,KAAK,CAAK,EAAE,UACnC,EAAK,OAAO,MAAQ,EAAK,OAAS,EAAM,EAAI;AAEhD;AAMA,SAAgB,EAAmB,GAAsC;CACvE,IAAI,MAAW,IAAO;EACpB,IAAI,GAAmB;EAGvB,AAFA,IAAoB,IACpB,GAAiB,kBAAkB,GACnC,EAAgC;EAChC;CACF;CAEA,IAAM,IAAc;CAGpB,IAFA,IAAoB,IAEhB,CAAC,KAAmB,CAAC,GAAuB;EAC9C,IAAM,IAAgB,OAAO,YAC3B,OAAO,QAAQ,CAAM,EAAE,QAAQ,GAAG,OAAW,MAAU,KAAA,CAAS,CAClE;EACA,IAAkB;GAAE,GAAG;GAAiB,GAAG;EAAc;CAC3D;CAEA,AAAI,MACE,MACF,EAAsB,CAAe,GACrC,EAA8B,CAAe,IAE/C,EAAgC;AAEpC;AAEA,SAAS,IAA+D;CAGtE,OAFI,IAA0B,OAEvB;EACL,QAAQ,EAAgB,UAAU;EAClC,SAAS,EAAgB,WAAW;EACpC,QAAQ,EAAgB,UAAU;CACpC;AACF;AAKA,SAAS,IAAqB;CAC5B,OAAO,OAAO,SAAW,OAAe,OAAO,eAAiB;AAClE;AAMA,eAAe,IAAsC;CACnD,IAAI,CAAC,EAAU,GACb,OAAO;CAGT,IAAI,GACF,OAAO;CAGT,IAAI;EACF,IAAM,EAAE,SAAS,MAAY,MAAM,OAAO;EAC1C,OAAO;CACT,QAAQ;EAEN,OAAO;CACT;AACF;AAMA,SAAS,IAA+B;CA4BtC,OATA,GAlBI,KAaA,OAAO,UAAY,OAAe,QAAQ,KAAK,iBAAiB,OAMlE,OAAO,YAAc,QACpB,UAAU,eAAe,OAEvB,OAAO,SAAW,OAAe,OAAO,eAAe;AAM9D;AAQA,SAAgB,EACd,GACM;CACN,AAAK,MACH,IAAmB;AAEvB;AAEA,eAAsB,EACpB,IAAkB,IACO;CACzB,IAAI,GACF,OAAO;CAGT,IAAI,GACF,OAAO;CAGT,KAAyB,YAAY;EACnC,IAAM,IAAS,EAA2B;EAC1C,IAAI,CAAC,GACH,OAAO;EAGT,IAAM,IAAU,MAAM,EAAW;EACjC,IAAI,CAAC,GACH,OAAO;EAKT,IAAM,IAAqB,EAAQ,KACjC,EAAO,QACP;GACE,UAAU,EAAO;GACjB,SAAS,EAAO;GAChB,8BAA8B,CAAC;GAC/B,aAAa;GACb,kBAAkB;GAClB,0BAA0B,GAAG,EAAsB;GACnD,iBAAiB;GACjB,kBAAkB;GAClB,aAAa;GACb,2BAA2B;GAC3B,WAAW;GACX,aAAa;EACf,GACA,CACF;EAeA,OAdK,KAEL,IAAkB,GAClB,IAAmB,KAAA,GAEf,IACF,EAAgB,kBAAkB,IACzB,EAAoB,MAAM,aACnC,EAAgB,iBAAiB,GACjC,EAA8B,CAAe,KACnC,KACV,EAAgB,kBAAkB,GAG7B,KAdyB;CAelC,GAAG;CAEH,IAAI;EACF,OAAO,MAAM;CACf,UAAU;EACR,AAAK,MACH,IAAwB;CAE5B;AACF;AAKA,SAAgB,IAAwC;CACtD,IAAI,CAAC,EAAU,GACb,OAAO;CAIT,IAAI,EAAoB,GACtB,OAAO;CAGT,IAAI,EAAuB,MAAM,UAC/B,OAAO;CAGT,IAAI;EACF,IAAM,IAAU,aAAa,QAAQ,CAAqB;EAC1D,IAAI,MAAY,aAAa,MAAY,UACvC,OAAO;CAEX,QAAQ,CAER;CAEA,OAAO;AACT;AAqBA,SAAgB,IAAsD;CACpE,IAAI,CAAC,EAAU,GAAG,OAAO;CAEzB,IAAI;EACF,OAAO,aAAa,QAAQ,CAA8C;CAC5E,QAAQ;EACN,OAAO;CACT;AACF;AAEA,SAAgB,EAA0B,GAAkC;CAC1E,IAAI,CAAC,EAAU,GAAG,aAAa,CAAC;CAChC,IAAM,KAAiB,MAAwB;EAC7C,CACE,EAAM,QAAQ,KACd,EAAM,QAAQ,MAEd,EAAS;CAEb;CAIA,OAHA,OAAO,iBAAiB,GAAgC,CAAQ,GAChE,OAAO,iBAAiB,WAAW,CAAa,SAEnC;EAEX,AADA,OAAO,oBAAoB,GAAgC,CAAQ,GACnE,OAAO,oBAAoB,WAAW,CAAa;CACrD;AACF;AAEA,SAAS,IAAwC;CAC/C,AAAI,EAAU,KACZ,OAAO,cAAc,IAAI,MAAM,CAA8B,CAAC;AAClE;AAEA,SAAS,EACP,GACM;CACD,MAAU,GAEf,IAAI;EACF,aAAa,QAAQ,GAA0C,CAAO;CACxE,QAAQ,CAER;AACF;AAEA,SAAS,EAAoC,GAAiC;CACxE,OAAC,EAAU,KAAK,CAAC,IAErB,IAAI;EACF,aAAa,QACX,GACA,CACF;CACF,QAAQ,CAER;AACF;AAEA,SAAgB,EACd,GACM;CACD,MAAU,GAEf,IAAI;EACF,IACE,aAAa,QAAQ,CAA8C,MACnE,GAEA;EAEF,aAAa,WAAW,CAA8C;CACxE,QAAQ,CAER;AACF;AAEA,SAAgB,EACd,GACM;CACD,MAAU,GAEf,IAAI;EACF,IACE,MAAa,KAAA,KACb,aAAa,QAAQ,CAAwC,MAC3D,GAEF;EAGF,AADA,aAAa,WAAW,CAAwC,GAChE,EAAgC;CAClC,QAAQ,CAER;AACF;AAKA,eAAsB,EACpB,GACA,EAAE,iBAAc,OAAqC,CAAC,GACvC;CACV,MAAU,GAIf,IAAI;EAEF,IAAM,IADkB,EACN,MAAoB;EAGtC,IAFA,aAAa,QAAQ,GAAuB,CAAO,GAC/C,KACA,GAAW;EAIf,IAAM,IAAU,KAAoB,MAAM,EAAwB;EAClE,IAAI,CAAC,GACH;EAGF,AAAI,MAAY,aACd,EAAQ,iBAAiB,GACzB,EAA8B,CAAO,MAErC,EAAoC,EAAQ,kBAAkB,KAAK,IAAI,GACnE,EAAQ,eAAe,UAAU,KAAK,OAGxC,EAAQ,kBAAkB,IAF1B,EAAqB,CAAO;CAKlC,QAAQ,CAER,UAAU;EAOR,AAHI,KACF,EAAiC,CAAO,GAE1C,EAAgC;CAClC;AACF;AAMA,eAAsB,EACpB,GACA,IAAsC,CAAC,GACxB;CACf,IAAM,IAAe,MAAe,OAAO,OAAO;EAAE;EAAY;CAAW;CAc3E,IAZE,MAA6B,KAC5B,KAC8B,QAC7B,MAAiB,QACjB,EAAyB,eAAe,EAAa,cACrD,EAAgB,EAAyB,YAAY,CAAU,MAGnE,IAA2B,GAC3B,KAA2B,GAC3B,IAA0B,IAEtB,CAAC,EAAmB,IAAG;CAC3B,IAAM,IAAU,KAAoB,MAAM,EAAwB;CAClE,AAAI,KAAW,EAAmB,KAGhC,EAA8B,CAAO;AAEzC;AAKA,SAAgB,IAA8B;CAC5C,OAAO,EAAoB,MAAM;AACnC;AAKA,SAAS,IAA2B;CAClC,IAAI,CAAC,EAAU,GACb,OAAO;CAGT,IAAI;EACF,OAAO,aAAa,QAAQ,CAAuB,MAAM;CAC3D,QAAQ;EACN,OAAO;CACT;AACF;AAKA,SAAS,KAAyB;CAC3B,MAAU,GAIf,IAAI;EACF,aAAa,QAAQ,GAAyB,MAAM;CACtD,QAAQ,CAER;AACF;AAiBA,eAAsB,KAA8B;CAOlD,IALI,EAAoB,KAKpB,EAAgB,GAClB;CAIF,IAAM,IAAU,MAAM,EAAwB,EAAI;CAC9C,CAAC,KAAW,EAAoB,OAKhB,EAAQ,0BAA0B,KAAK,OAEzD,EAAQ,iBAAiB,GAI3B,EAAQ,QAAQ,kBAAkB;EAChC,UAAU,OAAO,YAAc,MAAc,UAAU,WAAW;EAClE,YACE,OAAO,YAAc,MAAc,UAAU,YAAY;EAC3D,UAAU,OAAO,WAAa,MAAc,SAAS,WAAW;EAChE,YAAY,OAAO,SAAW,MAAc,OAAO,SAAS,SAAS;EACrE,UAAU,OAAO,SAAW,OAAe,OAAO,SAAS,OAAO;CACpE,CAAC,GAGD,GAAiB,GAIM,EACnB,MAAmB,aACrB,EAAQ,kBAAkB;AAE9B;AAKA,SAAS,KAA0B;CACjC,IAAI,CAAC,EAAU,GACb,OAAO;CAGT,IAAI;EACF,OAAO,eAAe,QAAQ,CAAqB,MAAM;CAC3D,QAAQ;EACN,OAAO;CACT;AACF;AAKA,SAAS,KAAwB;CAC1B,MAAU,GAIf,IAAI;EACF,eAAe,QAAQ,GAAuB,MAAM;CACtD,QAAQ,CAER;AACF;AAGA,eAAe,IAAyD;CACtE,IAAI,CAAC,EAAmB,KAAK,EAAoB,MAAM,WAAW,OAAO;CAEzE,IAAM,IAAU,MAAM,EAAwB;CAc9C,OAbI,CAAC,KAAW,CAAC,EAAmB,IAAU,QAO1C,EAAQ,0BAA0B,KACpC,EAAQ,iBAAiB,GAG3B,EAA8B,CAAO,GAE9B;AACT;AAOA,eAAsB,KAAmC;CAEvD,IAAI,GAAe,GACjB;CAGF,IAAM,IAAU,MAAM,EAA8B;CAC/C,MAEL,EAAQ,QAAQ,sBAAsB,EACpC,cAAc,CAAC,EAAgB,EACjC,CAAC,GAGD,GAAgB;AAClB;AAGA,eAAsB,KAAiD;CAErE,QAAO,MADe,EAA8B,IACpC,kBAAkB,KAAK;AACzC;AAGA,eAAsB,KAEpB;CACA,IAAI,CAAC,EAAU,GAAG,OAAO;CAEzB,IAAI,EAAoB,MAAM,WAAW;EACvC,IAAM,IAAsB,EAAqC;EACjE,IAAI,GAAqB,OAAO;CAClC;CAKA,OAHI,KAAqB,EAAoB,IAAU,QAGhD,MADe,EAAwB,IAC9B,kBAAkB,KAAK;AACzC;AAKA,eAAsB,GACpB,GACA,IAAsC,CAAC,GACxB;CACf,IAAM,IAAU,MAAM,EAA8B;CAC/C,KAEL,EAAQ,QAAQ,GAAW,CAAU;AACvC;AAGA,eAAsB,GACpB,GACA,IAAsC,CAAC,GACxB;CACf,IAAM,IAAU,MAAM,EAA8B;CAC/C,KAEL,EAAQ,iBAAiB,GAAO,CAAU;AAC5C;AAKA,eAAsB,KAAoC;CACnD,MAAU,GAIf;MAAI;GAKF,AAJA,EACE,GAAiB,kBAAkB,KAAK,IAC1C,GACA,aAAa,WAAW,CAAqB,GAC7C,aAAa,WAAW,CAAuB;EACjD,QAAQ,CAER;EACA,EAAkC;EAClC,IAAI;GACF,eAAe,WAAW,CAAqB;EACjD,QAAQ,CAER;EAMA,AAJA,IAA0B,EAA8B,CAAC,CAAC,GAC1D,IAAwB,EAA4B,GACpD,IAA2B,MAC3B,KAA2B,GAC3B,IAA0B;EAE1B,IAAI;GACF,AAAI,KACF,EAAqB,GAAiB,EAAI;EAE9C,QAAQ;GAEN,IAAI;IACF,GAAiB,kBAAkB;GACrC,QAAQ,CAER;EACF,UAAU;GACR,EAAgC;EAClC;CA3BA;AA4BF"}
1
+ {"version":3,"file":"telemetry.js","names":[],"sources":["../../src/services/telemetry.ts"],"sourcesContent":["/**\n * Telemetry service for tracking library usage.\n *\n * This module handles anonymous telemetry for the @openhands/agent-canvas package\n * using the PostHog SDK for reliable event delivery with batching, retry logic,\n * and offline support.\n *\n * TRACKING PHILOSOPHY:\n * - Install event (canvas_install): Sent immediately on first use, regardless of consent.\n * This is anonymous and contains no PII - just basic browser info and a random ID.\n * - Session/custom events: Only sent after user grants consent via the consent modal.\n * - Users can opt out of all future tracking by declining consent.\n *\n * AD BLOCKER BYPASS:\n * By default, telemetry is routed through OpenHands' reverse proxy (z.openhands.dev)\n * to avoid being blocked by ad blockers. Library consumers can override this with:\n * - VITE_POSTHOG_HOST: Custom proxy URL or direct PostHog URL\n * - VITE_POSTHOG_UI_HOST: PostHog UI host (defaults to https://us.posthog.com)\n *\n * IMPORTANT: By default, telemetry is sent to the OpenHands PostHog project.\n * Source builds can override this with VITE_POSTHOG_API_KEY. Precompiled\n * library consumers can pass the same settings to configureTelemetry().\n *\n * Users can disable all telemetry (including install tracking) via:\n * - Setting VITE_DO_NOT_TRACK=1 environment variable\n * - Browser's Do Not Track setting\n */\n\nimport type { BootstrapConfig, CaptureResult, PostHog } from \"posthog-js\";\nimport { getLockedCloudAuthMode } from \"#/api/agent-server-config\";\nimport defaults from \"../../config/defaults.json\";\n\nimport packageJson from \"../../package.json\";\nimport {\n AGENT_CANVAS_CLIENT_SOURCE,\n AGENT_CANVAS_CLIENT_VERSION,\n} from \"#/api/client-source\";\nimport {\n getBackendTelemetryProperties,\n getCloudTelemetryProperties,\n type BackendTelemetryContextInput,\n type CloudTelemetryContextInput,\n} from \"#/services/telemetry-context\";\n\nconst TELEMETRY_CONSENT_KEY = \"openhands-telemetry-consent\";\nconst TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY =\n \"openhands-telemetry-consent-pending-cloud-sync\";\nconst TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY =\n \"openhands-telemetry-consent-pending-local-revocation\";\nconst TELEMETRY_CONSENT_CHANGE_EVENT = \"openhands-telemetry-consent-change\";\nconst TELEMETRY_FIRST_USE_KEY = \"openhands-telemetry-first-use\";\nconst TELEMETRY_SESSION_KEY = \"openhands-telemetry-session\";\nconst POSTHOG_INSTANCE_NAME = \"agent-canvas\";\nconst POSTHOG_PAGEVIEW_CAPTURE_MODE = \"history_change\";\n\n// Unconfigured source builds use staging. Production release workflows pass\n// VITE_POSTHOG_API_KEY explicitly for both the app and library artifacts.\nconst DEFAULT_POSTHOG_API_KEY: string =\n (import.meta.env.VITE_POSTHOG_API_KEY as string | undefined) ||\n defaults.telemetry.posthogApiKey;\n\n// Default to OpenHands' reverse proxy to bypass ad blockers.\n// The proxy at z.openhands.dev routes to PostHog's US region.\n// Library consumers can override this with their own proxy or direct PostHog URL.\nconst DEFAULT_POSTHOG_HOST =\n import.meta.env.VITE_POSTHOG_HOST || \"https://z.openhands.dev\";\n\n// UI host is needed for PostHog features like toolbar to work correctly\n// when using a reverse proxy. Defaults to US region.\nconst DEFAULT_POSTHOG_UI_HOST =\n import.meta.env.VITE_POSTHOG_UI_HOST || \"https://us.posthog.com\";\n\nexport interface TelemetryConfig {\n /** PostHog project key. Useful for precompiled library consumers. */\n apiKey?: string;\n /** Event ingestion host or reverse proxy. */\n apiHost?: string;\n /** PostHog UI host used by toolbar links and other UI features. */\n uiHost?: string;\n}\n\nexport type TelemetryConfiguration = TelemetryConfig | false;\n\nexport type TelemetryConsent = \"granted\" | \"denied\" | \"pending\";\nexport type ResolvedTelemetryConsent = Exclude<TelemetryConsent, \"pending\">;\n\nexport interface SetTelemetryConsentOptions {\n /** Do not persist a value mirrored from backend settings back to Cloud. */\n syncToCloud?: boolean;\n}\n\nlet posthogInstance: PostHog | null = null;\nlet initializationPromise: Promise<PostHog | null> | null = null;\nlet pendingBootstrap: BootstrapConfig | undefined;\nlet telemetryConfig: TelemetryConfig = {};\nlet telemetryDisabled = false;\n\ninterface TelemetryIdentity {\n distinctId: string;\n properties: Record<string, unknown>;\n}\n\n// undefined means that Cloud identity has not resolved yet; null means that it\n// resolved without a user. This distinction prevents startup from resetting a\n// persisted identity while the current account is still loading.\nlet desiredTelemetryIdentity: TelemetryIdentity | null | undefined;\nlet desiredIdentityRevision = 0;\nlet appliedIdentityRevision = -1;\n\nconst CANVAS_EVENT_PROPERTIES = Object.freeze({\n client_source: AGENT_CANVAS_CLIENT_SOURCE,\n client_version: AGENT_CANVAS_CLIENT_VERSION,\n package_name: packageJson.name,\n package_version: packageJson.version,\n});\n\nlet telemetryBackendContext = getBackendTelemetryProperties({});\nlet telemetryCloudContext = getCloudTelemetryProperties();\n\nexport function setTelemetryBackendContext(\n context: BackendTelemetryContextInput,\n): void {\n telemetryBackendContext = getBackendTelemetryProperties(context);\n}\n\nexport function setTelemetryCloudContext(\n context: CloudTelemetryContextInput | null,\n): void {\n telemetryCloudContext = getCloudTelemetryProperties(context);\n}\n\nfunction addCanvasEventProperties(\n event: CaptureResult | null,\n): CaptureResult | null {\n if (!event) return null;\n\n return {\n ...event,\n properties: {\n ...telemetryBackendContext,\n ...telemetryCloudContext,\n ...event.properties,\n ...CANVAS_EVENT_PROPERTIES,\n },\n };\n}\n\nfunction restorePostHogConsent(posthog: PostHog): void {\n if (telemetryDisabled || getTelemetryConsent() !== \"granted\") {\n posthog.opt_out_capturing();\n } else {\n posthog.opt_in_capturing();\n }\n}\n\nfunction resetPostHogIdentity(posthog: PostHog, resetDeviceId = false): void {\n posthog.reset(resetDeviceId);\n appliedIdentityRevision = -1;\n // PostHog reset clears its own consent persistence, so immediately restore\n // the canonical Canvas decision kept in localStorage.\n restorePostHogConsent(posthog);\n}\n\nfunction applyDesiredTelemetryIdentity(posthog: PostHog): void {\n if (desiredTelemetryIdentity === undefined || !isTelemetryEnabled()) return;\n\n const desiredId = desiredTelemetryIdentity?.distinctId;\n const currentId = posthog.get_property(\"$user_id\");\n if (currentId != null && currentId !== desiredId) {\n resetPostHogIdentity(posthog);\n }\n\n if (desiredTelemetryIdentity === null) {\n appliedIdentityRevision = desiredIdentityRevision;\n return;\n }\n\n if (\n posthog.get_property(\"$user_id\") !== desiredTelemetryIdentity.distinctId ||\n appliedIdentityRevision !== desiredIdentityRevision\n ) {\n posthog.identify(\n desiredTelemetryIdentity.distinctId,\n desiredTelemetryIdentity.properties,\n );\n appliedIdentityRevision = desiredIdentityRevision;\n }\n}\n\nfunction propertiesEqual(\n left: Record<string, unknown>,\n right: Record<string, unknown>,\n): boolean {\n const keys = Object.keys(left);\n return (\n keys.length === Object.keys(right).length &&\n keys.every((key) => left[key] === right[key])\n );\n}\n\n/**\n * Configure the single Canvas telemetry client before its first use.\n * Passing false disables telemetry and install tracking for embedded hosts.\n */\nexport function configureTelemetry(config: TelemetryConfiguration): void {\n if (config === false) {\n if (telemetryDisabled) return;\n telemetryDisabled = true;\n posthogInstance?.opt_out_capturing();\n notifyTelemetryConsentListeners();\n return;\n }\n\n const wasDisabled = telemetryDisabled;\n telemetryDisabled = false;\n\n if (!posthogInstance && !initializationPromise) {\n const definedConfig = Object.fromEntries(\n Object.entries(config).filter(([, value]) => value !== undefined),\n ) as TelemetryConfig;\n telemetryConfig = { ...telemetryConfig, ...definedConfig };\n }\n\n if (wasDisabled) {\n if (posthogInstance) {\n restorePostHogConsent(posthogInstance);\n applyDesiredTelemetryIdentity(posthogInstance);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\nfunction getResolvedTelemetryConfig(): Required<TelemetryConfig> | null {\n if (telemetryDisabled) return null;\n\n return {\n apiKey: telemetryConfig.apiKey || DEFAULT_POSTHOG_API_KEY,\n apiHost: telemetryConfig.apiHost || DEFAULT_POSTHOG_HOST,\n uiHost: telemetryConfig.uiHost || DEFAULT_POSTHOG_UI_HOST,\n };\n}\n\n/**\n * Check if we're in a browser environment\n */\nfunction isBrowser(): boolean {\n return typeof window !== \"undefined\" && typeof localStorage !== \"undefined\";\n}\n\n/**\n * Lazily load PostHog to avoid SSR/Node.js issues.\n * PostHog is a browser-only library, so we dynamically import it only when needed.\n */\nasync function getPostHog(): Promise<PostHog | null> {\n if (!isBrowser()) {\n return null;\n }\n\n if (posthogInstance) {\n return posthogInstance;\n }\n\n try {\n const { default: posthog } = await import(\"posthog-js\");\n return posthog;\n } catch {\n // Failed to load PostHog - telemetry will be disabled\n return null;\n }\n}\n\n/**\n * Check if telemetry is disabled via environment variable or browser setting.\n * Works in both Node.js and browser (Vite) environments.\n */\nfunction isDoNotTrackEnabled(): boolean {\n if (telemetryDisabled) {\n return true;\n }\n\n // Check Vite environment variable (browser)\n if (\n typeof import.meta !== \"undefined\" &&\n import.meta.env?.VITE_DO_NOT_TRACK === \"1\"\n ) {\n return true;\n }\n\n // Check Node.js environment variable (SSR/testing)\n if (typeof process !== \"undefined\" && process.env?.DO_NOT_TRACK === \"1\") {\n return true;\n }\n\n // Check browser's navigator.doNotTrack standard\n if (\n typeof navigator !== \"undefined\" &&\n (navigator.doNotTrack === \"1\" ||\n // @ts-expect-error - Some browsers use window.doNotTrack\n (typeof window !== \"undefined\" && window.doNotTrack === \"1\"))\n ) {\n return true;\n }\n\n return false;\n}\n\n/**\n * Initialize PostHog SDK.\n *\n * @param enableCapturing - If true, enable capturing immediately (for install tracking).\n * If false, start with capturing disabled (for consent-gated tracking).\n */\nexport function configurePostHogBootstrap(\n bootstrap: BootstrapConfig | undefined,\n): void {\n if (!posthogInstance) {\n pendingBootstrap = bootstrap;\n }\n}\n\nexport async function initializePostHogClient(\n enableCapturing = false,\n): Promise<PostHog | null> {\n if (posthogInstance) {\n return posthogInstance;\n }\n\n if (initializationPromise) {\n return initializationPromise;\n }\n\n initializationPromise = (async () => {\n const config = getResolvedTelemetryConfig();\n if (!config) {\n return null;\n }\n\n const posthog = await getPostHog();\n if (!posthog) {\n return null;\n }\n\n // A named instance isolates Canvas configuration, consent, identity, and\n // persistence from a host application's default PostHog singleton.\n const initializedPostHog = posthog.init(\n config.apiKey,\n {\n api_host: config.apiHost,\n ui_host: config.uiHost,\n opt_out_capturing_by_default: !enableCapturing,\n persistence: \"localStorage\",\n persistence_name: POSTHOG_INSTANCE_NAME,\n consent_persistence_name: `${POSTHOG_INSTANCE_NAME}-consent`,\n person_profiles: \"always\",\n capture_pageview: POSTHOG_PAGEVIEW_CAPTURE_MODE,\n autocapture: true,\n disable_session_recording: true,\n bootstrap: pendingBootstrap,\n before_send: addCanvasEventProperties,\n },\n POSTHOG_INSTANCE_NAME,\n );\n if (!initializedPostHog) return null;\n\n posthogInstance = initializedPostHog;\n pendingBootstrap = undefined;\n\n if (telemetryDisabled) {\n posthogInstance.opt_out_capturing();\n } else if (getTelemetryConsent() === \"granted\") {\n posthogInstance.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthogInstance);\n } else if (!enableCapturing) {\n posthogInstance.opt_out_capturing();\n }\n\n return posthogInstance;\n })();\n\n try {\n return await initializationPromise;\n } finally {\n if (!posthogInstance) {\n initializationPromise = null;\n }\n }\n}\n\n/**\n * Get user's telemetry consent preference\n */\nexport function getTelemetryConsent(): TelemetryConsent {\n if (!isBrowser()) {\n return \"pending\";\n }\n\n // Check environment variable for opt-out\n if (isDoNotTrackEnabled()) {\n return \"denied\";\n }\n\n if (getLockedCloudAuthMode() === \"cookie\") {\n return \"granted\";\n }\n\n try {\n const consent = localStorage.getItem(TELEMETRY_CONSENT_KEY);\n if (consent === \"granted\" || consent === \"denied\") {\n return consent;\n }\n } catch {\n // Ignore storage errors\n }\n\n return \"pending\";\n}\n\n/**\n * Return an explicit browser choice that still needs to survive a Cloud login.\n * It remains pending across local backends so their settings cannot consume a\n * decision that must still be applied after the user connects to Cloud.\n */\nexport function getPendingCloudTelemetryConsent(): ResolvedTelemetryConsent | null {\n if (!isBrowser()) return null;\n\n try {\n const consent = localStorage.getItem(\n TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY,\n );\n return consent === \"granted\" || consent === \"denied\" ? consent : null;\n } catch {\n return null;\n }\n}\n\n/** Return the pre-reset actor whose local Automation consent must be revoked. */\nexport function getPendingLocalTelemetryRevocationId(): string | null {\n if (!isBrowser()) return null;\n\n try {\n return localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n return null;\n }\n}\n\nexport function subscribeTelemetryConsent(listener: () => void): () => void {\n if (!isBrowser()) return () => {};\n const handleStorage = (event: StorageEvent) => {\n if (\n event.key === TELEMETRY_CONSENT_KEY ||\n event.key === TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY\n ) {\n listener();\n }\n };\n window.addEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.addEventListener(\"storage\", handleStorage);\n\n return () => {\n window.removeEventListener(TELEMETRY_CONSENT_CHANGE_EVENT, listener);\n window.removeEventListener(\"storage\", handleStorage);\n };\n}\n\nfunction notifyTelemetryConsentListeners(): void {\n if (isBrowser())\n window.dispatchEvent(new Event(TELEMETRY_CONSENT_CHANGE_EVENT));\n}\n\nfunction markTelemetryConsentForCloudSync(\n consent: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n localStorage.setItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY, consent);\n } catch {\n // Ignore storage errors; the in-browser consent decision still applies.\n }\n}\n\nfunction markPendingLocalTelemetryRevocation(distinctId: string | null): void {\n if (!isBrowser() || !distinctId) return;\n\n try {\n localStorage.setItem(\n TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY,\n distinctId,\n );\n } catch {\n // Ignore storage errors; browser capture is still disabled below.\n }\n}\n\nexport function clearPendingLocalTelemetryRevocation(\n expectedDistinctId: string,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY) !==\n expectedDistinctId\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_LOCAL_REVOCATION_KEY);\n } catch {\n // Ignore storage errors.\n }\n}\n\nexport function clearPendingCloudTelemetryConsent(\n expected?: ResolvedTelemetryConsent,\n): void {\n if (!isBrowser()) return;\n\n try {\n if (\n expected !== undefined &&\n localStorage.getItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY) !==\n expected\n ) {\n return;\n }\n localStorage.removeItem(TELEMETRY_CONSENT_PENDING_CLOUD_SYNC_KEY);\n notifyTelemetryConsentListeners();\n } catch {\n // Ignore storage errors.\n }\n}\n\n/**\n * Set user's telemetry consent preference\n */\nexport async function setTelemetryConsent(\n consent: ResolvedTelemetryConsent,\n { syncToCloud = true }: SetTelemetryConsentOptions = {},\n): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n const previousConsent = getTelemetryConsent();\n const unchanged = previousConsent === consent;\n localStorage.setItem(TELEMETRY_CONSENT_KEY, consent);\n if (telemetryDisabled) return;\n if (unchanged) return;\n\n // Reuse an initialized client synchronously so a same-flush identify()\n // cannot run before consent is applied. Only the cold path awaits import.\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (!posthog) {\n return;\n }\n\n if (consent === \"granted\") {\n posthog.opt_in_capturing();\n applyDesiredTelemetryIdentity(posthog);\n } else {\n markPendingLocalTelemetryRevocation(posthog.get_distinct_id?.() ?? null);\n if (posthog.get_property?.(\"$user_id\") != null) {\n resetPostHogIdentity(posthog);\n } else {\n posthog.opt_out_capturing();\n }\n }\n } catch {\n // Ignore storage errors\n } finally {\n // Notify UI/backend/identity reconcilers only after the browser capture state\n // reflects this decision. Otherwise a pre-login grant can trigger an\n // identify while PostHog is still opted out and lose funnel continuity.\n if (syncToCloud) {\n markTelemetryConsentForCloudSync(consent);\n }\n notifyTelemetryConsentListeners();\n }\n}\n\n/**\n * Declare the current Cloud identity. The telemetry service applies it only\n * after consent and owns all reset/account-switch semantics.\n */\nexport async function setTelemetryIdentity(\n distinctId: string | null,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const nextIdentity = distinctId === null ? null : { distinctId, properties };\n const unchanged =\n desiredTelemetryIdentity === nextIdentity ||\n (desiredTelemetryIdentity !== undefined &&\n desiredTelemetryIdentity !== null &&\n nextIdentity !== null &&\n desiredTelemetryIdentity.distinctId === nextIdentity.distinctId &&\n propertiesEqual(desiredTelemetryIdentity.properties, properties));\n if (unchanged) return;\n\n desiredTelemetryIdentity = nextIdentity;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n if (!isTelemetryEnabled()) return;\n const posthog = posthogInstance ?? (await initializePostHogClient());\n if (posthog && isTelemetryEnabled()) {\n // Read the desired identity after the await so a newer account always wins\n // if identity changes while the SDK is loading.\n applyDesiredTelemetryIdentity(posthog);\n }\n}\n\n/**\n * Check if telemetry is enabled (user has granted consent)\n */\nexport function isTelemetryEnabled(): boolean {\n return getTelemetryConsent() === \"granted\";\n}\n\n/**\n * Check if first use event has already been sent\n */\nfunction hasFirstUseSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return localStorage.getItem(TELEMETRY_FIRST_USE_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark first use event as sent\n */\nfunction markFirstUseSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n localStorage.setItem(TELEMETRY_FIRST_USE_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/**\n * Track the initial install of the library.\n *\n * IMPORTANT: This is sent immediately on first use, regardless of consent status.\n * This allows us to track library adoption even if users haven't made a consent choice yet.\n *\n * The event is:\n * - Completely anonymous (no PII, just a random PostHog distinct_id)\n * - Sent only once per installation (tracked via localStorage, persists across sessions)\n * - Still respects DO_NOT_TRACK environment variable and browser setting\n *\n * Users who want complete privacy can:\n * - Set VITE_DO_NOT_TRACK=1 or browser's Do Not Track\n * - Later deny consent to prevent all future tracking\n */\nexport async function trackInstall(): Promise<void> {\n // Respect hard opt-out via environment variable or browser setting\n if (isDoNotTrackEnabled()) {\n return;\n }\n\n // Already sent install event (persisted in localStorage - survives app relaunches)\n if (hasFirstUseSent()) {\n return;\n }\n\n // Initialize PostHog with capturing enabled (for this one event)\n const posthog = await initializePostHogClient(true);\n if (!posthog || isDoNotTrackEnabled()) {\n return;\n }\n\n // Temporarily enable capturing if it was disabled\n const wasOptedOut = posthog.has_opted_out_capturing?.() ?? false;\n if (wasOptedOut) {\n posthog.opt_in_capturing();\n }\n\n // Capture the install event\n posthog.capture(\"canvas_install\", {\n platform: typeof navigator !== \"undefined\" ? navigator.platform : \"unknown\",\n user_agent:\n typeof navigator !== \"undefined\" ? navigator.userAgent : \"unknown\",\n referrer: typeof document !== \"undefined\" ? document.referrer : \"\",\n url_origin: typeof window !== \"undefined\" ? window.location.origin : \"\",\n embedded: typeof window !== \"undefined\" && window.self !== window.top,\n });\n\n // Mark as sent (stored in localStorage - persists across browser sessions)\n markFirstUseSent();\n\n // Restore opt-out state if user hasn't granted consent yet\n // This ensures we only send the install event, not subsequent events\n const currentConsent = getTelemetryConsent();\n if (currentConsent !== \"granted\") {\n posthog.opt_out_capturing();\n }\n}\n\n/**\n * Check if session start event has already been sent (this browser session)\n */\nfunction hasSessionSent(): boolean {\n if (!isBrowser()) {\n return false;\n }\n\n try {\n return sessionStorage.getItem(TELEMETRY_SESSION_KEY) === \"true\";\n } catch {\n return false;\n }\n}\n\n/**\n * Mark session start event as sent (uses sessionStorage so it resets on new tabs/sessions)\n */\nfunction markSessionSent(): void {\n if (!isBrowser()) {\n return;\n }\n\n try {\n sessionStorage.setItem(TELEMETRY_SESSION_KEY, \"true\");\n } catch {\n // Ignore storage errors\n }\n}\n\n/** Return the shared client only when a consented capture is safe to emit. */\nasync function getPostHogForConsentedCapture(): Promise<PostHog | null> {\n if (!isTelemetryEnabled() || getTelemetryConsent() !== \"granted\") return null;\n\n const posthog = await initializePostHogClient();\n if (!posthog || !isTelemetryEnabled()) return null;\n\n // The browser preference is the canonical capture decision. PostHog may\n // still carry an older opt-out marker while backend consent is loading or\n // after a previous backend temporarily reported a stale value. Heal that\n // drift at the event boundary so capture() cannot silently discard an event\n // that the user has explicitly allowed.\n if (posthog.has_opted_out_capturing?.()) {\n posthog.opt_in_capturing();\n }\n\n applyDesiredTelemetryIdentity(posthog);\n\n return posthog;\n}\n\n/**\n * Track a session start event.\n * Called each time a new browser session starts (respects consent).\n * Uses sessionStorage for deduplication - only sends once per browser session.\n */\nexport async function trackSessionStart(): Promise<void> {\n // Already sent session event this browser session\n if (hasSessionSent()) {\n return;\n }\n\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(\"canvas_new_session\", {\n is_first_use: !hasFirstUseSent(),\n });\n\n // Mark as sent for this session\n markSessionSent();\n}\n\n/** Return the active PostHog distinct ID when consent allows capture. */\nexport async function getTelemetryDistinctId(): Promise<string | null> {\n const posthog = await getPostHogForConsentedCapture();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/** Return the PostHog distinct ID for local consent sync without emitting capture. */\nexport async function getTelemetryDistinctIdForConsentSync(): Promise<\n string | null\n> {\n if (!isBrowser()) return null;\n\n if (getTelemetryConsent() !== \"granted\") {\n const pendingRevocationId = getPendingLocalTelemetryRevocationId();\n if (pendingRevocationId) return pendingRevocationId;\n }\n\n if (telemetryDisabled || isDoNotTrackEnabled()) return null;\n\n const posthog = await initializePostHogClient();\n return posthog?.get_distinct_id?.() ?? null;\n}\n\n/**\n * Track a custom event (respects consent).\n */\nexport async function trackEvent(\n eventName: string,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.capture(eventName, properties);\n}\n\n/** Track an exception through the same consent-aware client as custom events. */\nexport async function trackException(\n error: unknown,\n properties: Record<string, unknown> = {},\n): Promise<void> {\n const posthog = await getPostHogForConsentedCapture();\n if (!posthog) return;\n\n posthog.captureException(error, properties);\n}\n\n/**\n * Clear all telemetry data (for privacy/GDPR requests)\n */\nexport async function clearTelemetryData(): Promise<void> {\n if (!isBrowser()) {\n return;\n }\n\n try {\n markPendingLocalTelemetryRevocation(\n posthogInstance?.get_distinct_id?.() ?? null,\n );\n localStorage.removeItem(TELEMETRY_CONSENT_KEY);\n localStorage.removeItem(TELEMETRY_FIRST_USE_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n clearPendingCloudTelemetryConsent();\n try {\n sessionStorage.removeItem(TELEMETRY_SESSION_KEY);\n } catch {\n // Continue clearing the in-memory and SDK identity if storage is blocked.\n }\n\n telemetryBackendContext = getBackendTelemetryProperties({});\n telemetryCloudContext = getCloudTelemetryProperties();\n desiredTelemetryIdentity = null;\n desiredIdentityRevision += 1;\n appliedIdentityRevision = -1;\n\n try {\n if (posthogInstance) {\n resetPostHogIdentity(posthogInstance, true);\n }\n } catch {\n // A reset failure must not leave capture enabled after a privacy clear.\n try {\n posthogInstance?.opt_out_capturing();\n } catch {\n // Telemetry failures must not break the application.\n }\n } finally {\n notifyTelemetryConsentListeners();\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ require("../_virtual/_rolldown/runtime.cjs");const e=require("../node_modules/zustand/esm/react.cjs"),t=require("../node_modules/zustand/esm/middleware.cjs");var n=`archived-conversations`,r={archivesByBackendId:{}};function i(e,t){return e[t]??[]}var a=e.create()(t.persist((e,t)=>({...r,archiveConversation:(n,r)=>{let a=i(t().archivesByBackendId,n);a.includes(r)||e(e=>({archivesByBackendId:{...e.archivesByBackendId,[n]:[r,...a]}}))},removeArchivedConversation:(n,r)=>{let a=i(t().archivesByBackendId,n);a.includes(r)&&e(e=>({archivesByBackendId:{...e.archivesByBackendId,[n]:a.filter(e=>e!==r)}}))},isArchived:(e,n)=>i(t().archivesByBackendId,e).includes(n)}),{name:n,storage:t.createJSONStorage(()=>localStorage),partialize:e=>({archivesByBackendId:e.archivesByBackendId})}));exports.useArchivedConversationsStore=a;
2
+ //# sourceMappingURL=archived-conversations-store.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"archived-conversations-store.cjs","names":[],"sources":["../../src/stores/archived-conversations-store.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { persist, createJSONStorage } from \"zustand/middleware\";\n\nexport const ARCHIVED_CONVERSATIONS_STORAGE_KEY = \"archived-conversations\";\n\ninterface ArchivedConversationsState {\n archivesByBackendId: Record<string, string[]>;\n}\n\ninterface ArchivedConversationsActions {\n archiveConversation: (backendId: string, conversationId: string) => void;\n removeArchivedConversation: (\n backendId: string,\n conversationId: string,\n ) => void;\n isArchived: (backendId: string, conversationId: string) => boolean;\n}\n\ntype ArchivedConversationsStore = ArchivedConversationsState &\n ArchivedConversationsActions;\n\nconst initialState: ArchivedConversationsState = {\n archivesByBackendId: {},\n};\n\nfunction getArchivesForBackend(\n archivesByBackendId: Record<string, string[]>,\n backendId: string,\n): string[] {\n return archivesByBackendId[backendId] ?? [];\n}\n\nexport const useArchivedConversationsStore =\n create<ArchivedConversationsStore>()(\n persist(\n (set, get) => ({\n ...initialState,\n\n archiveConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: [conversationId, ...current],\n },\n }));\n },\n\n removeArchivedConversation: (backendId, conversationId) => {\n const current = getArchivesForBackend(\n get().archivesByBackendId,\n backendId,\n );\n if (!current.includes(conversationId)) {\n return;\n }\n set((state) => ({\n archivesByBackendId: {\n ...state.archivesByBackendId,\n [backendId]: current.filter((id) => id !== conversationId),\n },\n }));\n },\n\n isArchived: (backendId, conversationId) =>\n getArchivesForBackend(get().archivesByBackendId, backendId).includes(\n conversationId,\n ),\n }),\n {\n name: ARCHIVED_CONVERSATIONS_STORAGE_KEY,\n storage: createJSONStorage(() => localStorage),\n partialize: (state): ArchivedConversationsState => ({\n archivesByBackendId: state.archivesByBackendId,\n }),\n },\n ),\n );\n"],"mappings":"8JAGA,IAAa,EAAqC,yBAkB5C,EAA2C,CAC/C,oBAAqB,CAAC,CACxB,EAEA,SAAS,EACP,EACA,EACU,CACV,OAAO,EAAoB,IAAc,CAAC,CAC5C,CAEA,IAAa,EACX,EAAA,OAAmC,EACjC,EAAA,SACG,EAAK,KAAS,CACb,GAAG,EAEH,qBAAsB,EAAW,IAAmB,CAClD,IAAM,EAAU,EACd,EAAI,EAAE,oBACN,CACF,EACI,EAAQ,SAAS,CAAc,GAGnC,EAAK,IAAW,CACd,oBAAqB,CACnB,GAAG,EAAM,qBACR,GAAY,CAAC,EAAgB,GAAG,CAAO,CAC1C,CACF,EAAE,CACJ,EAEA,4BAA6B,EAAW,IAAmB,CACzD,IAAM,EAAU,EACd,EAAI,EAAE,oBACN,CACF,EACK,EAAQ,SAAS,CAAc,GAGpC,EAAK,IAAW,CACd,oBAAqB,CACnB,GAAG,EAAM,qBACR,GAAY,EAAQ,OAAQ,GAAO,IAAO,CAAc,CAC3D,CACF,EAAE,CACJ,EAEA,YAAa,EAAW,IACtB,EAAsB,EAAI,EAAE,oBAAqB,CAAS,EAAE,SAC1D,CACF,CACJ,GACA,CACE,KAAM,EACN,QAAS,EAAA,sBAAwB,YAAY,EAC7C,WAAa,IAAuC,CAClD,oBAAqB,EAAM,mBAC7B,EACF,CACF,CACF"}
@@ -0,0 +1,24 @@
1
+ export declare const ARCHIVED_CONVERSATIONS_STORAGE_KEY = "archived-conversations";
2
+ interface ArchivedConversationsState {
3
+ archivesByBackendId: Record<string, string[]>;
4
+ }
5
+ interface ArchivedConversationsActions {
6
+ archiveConversation: (backendId: string, conversationId: string) => void;
7
+ removeArchivedConversation: (backendId: string, conversationId: string) => void;
8
+ isArchived: (backendId: string, conversationId: string) => boolean;
9
+ }
10
+ type ArchivedConversationsStore = ArchivedConversationsState & ArchivedConversationsActions;
11
+ export declare const useArchivedConversationsStore: import("zustand").UseBoundStore<Omit<import("zustand").StoreApi<ArchivedConversationsStore>, "setState" | "persist"> & {
12
+ setState(partial: ArchivedConversationsStore | Partial<ArchivedConversationsStore> | ((state: ArchivedConversationsStore) => ArchivedConversationsStore | Partial<ArchivedConversationsStore>), replace?: false | undefined): unknown;
13
+ setState(state: ArchivedConversationsStore | ((state: ArchivedConversationsStore) => ArchivedConversationsStore), replace: true): unknown;
14
+ persist: {
15
+ setOptions: (options: Partial<import("zustand/middleware").PersistOptions<ArchivedConversationsStore, unknown, unknown>>) => void;
16
+ clearStorage: () => void;
17
+ rehydrate: () => Promise<void> | void;
18
+ hasHydrated: () => boolean;
19
+ onHydrate: (fn: (state: ArchivedConversationsStore) => void) => () => void;
20
+ onFinishHydration: (fn: (state: ArchivedConversationsStore) => void) => () => void;
21
+ getOptions: () => Partial<import("zustand/middleware").PersistOptions<ArchivedConversationsStore, unknown, unknown>>;
22
+ };
23
+ }>;
24
+ export {};