@dxos/plugin-thread 0.8.4-main.28f8d3d → 0.8.4-main.406dc2a

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 (246) hide show
  1. package/dist/lib/browser/ChannelContainer-BGDGZHV4.mjs +16 -0
  2. package/dist/lib/browser/ChatContainer-75KJZ4XR.mjs +15 -0
  3. package/dist/lib/browser/{app-graph-builder-AF3M7UWN.mjs → app-graph-builder-ODKIAXR7.mjs} +17 -17
  4. package/dist/lib/browser/app-graph-builder-ODKIAXR7.mjs.map +7 -0
  5. package/dist/lib/browser/{call-manager-XRRCAVLI.mjs → call-manager-BCKLI4QD.mjs} +7 -7
  6. package/dist/lib/browser/{call-manager-XRRCAVLI.mjs.map → call-manager-BCKLI4QD.mjs.map} +1 -1
  7. package/dist/lib/browser/chunk-2MUYQOQE.mjs +23 -0
  8. package/dist/lib/browser/{chunk-2CWJBXBH.mjs.map → chunk-2MUYQOQE.mjs.map} +3 -3
  9. package/dist/lib/browser/chunk-B7YQ7QR2.mjs +376 -0
  10. package/dist/lib/browser/chunk-B7YQ7QR2.mjs.map +7 -0
  11. package/dist/lib/browser/{chunk-ZJ7QBXK6.mjs → chunk-CMCVHULC.mjs} +17 -15
  12. package/dist/lib/browser/chunk-CMCVHULC.mjs.map +7 -0
  13. package/dist/lib/browser/{chunk-GERVMFQW.mjs → chunk-CP7E2HRE.mjs} +2 -2
  14. package/dist/lib/browser/chunk-CP7E2HRE.mjs.map +7 -0
  15. package/dist/lib/browser/{chunk-X5PIP5YR.mjs → chunk-HDEQRBOC.mjs} +37 -5
  16. package/dist/lib/{node-esm/chunk-SED3O2XO.mjs.map → browser/chunk-HDEQRBOC.mjs.map} +4 -4
  17. package/dist/lib/browser/chunk-L326LSNS.mjs +18 -0
  18. package/dist/lib/browser/chunk-L326LSNS.mjs.map +7 -0
  19. package/dist/lib/browser/{chunk-EV4JWI7L.mjs → chunk-T4KJ5JR4.mjs} +5 -7
  20. package/dist/lib/browser/chunk-T4KJ5JR4.mjs.map +7 -0
  21. package/dist/lib/browser/chunk-VJMKS4CZ.mjs +619 -0
  22. package/dist/lib/browser/chunk-VJMKS4CZ.mjs.map +7 -0
  23. package/dist/lib/browser/{chunk-DW7JD64V.mjs → chunk-VTYX3SIM.mjs} +26 -16
  24. package/dist/lib/browser/chunk-VTYX3SIM.mjs.map +7 -0
  25. package/dist/lib/browser/{chunk-JGKMMI65.mjs → chunk-XYUJXHYV.mjs} +54 -4
  26. package/dist/lib/browser/chunk-XYUJXHYV.mjs.map +7 -0
  27. package/dist/lib/browser/{chunk-TUFWPOVS.mjs → chunk-ZCEU5K23.mjs} +460 -268
  28. package/dist/lib/browser/chunk-ZCEU5K23.mjs.map +7 -0
  29. package/dist/lib/browser/index.mjs +22 -13
  30. package/dist/lib/browser/index.mjs.map +3 -3
  31. package/dist/lib/browser/{intent-resolver-DCY4P4OX.mjs → intent-resolver-VOQD3TQ2.mjs} +73 -15
  32. package/dist/lib/browser/intent-resolver-VOQD3TQ2.mjs.map +7 -0
  33. package/dist/lib/browser/{markdown-WZSXCEZJ.mjs → markdown-R6MGRIA6.mjs} +6 -6
  34. package/dist/lib/browser/{markdown-WZSXCEZJ.mjs.map → markdown-R6MGRIA6.mjs.map} +1 -1
  35. package/dist/lib/browser/meta.json +1 -1
  36. package/dist/lib/browser/react-root-UMSBTN6H.mjs +28 -0
  37. package/dist/lib/browser/react-root-UMSBTN6H.mjs.map +7 -0
  38. package/dist/lib/browser/{react-surface-PYAMIADB.mjs → react-surface-SXMQEWKE.mjs} +22 -22
  39. package/dist/lib/browser/react-surface-SXMQEWKE.mjs.map +7 -0
  40. package/dist/lib/browser/{settings-AC6YSASL.mjs → settings-TCNF54PN.mjs} +5 -5
  41. package/dist/lib/browser/{settings-AC6YSASL.mjs.map → settings-TCNF54PN.mjs.map} +1 -1
  42. package/dist/lib/browser/{state-2T4KDWBX.mjs → state-ZQ4IPOJT.mjs} +5 -5
  43. package/dist/lib/browser/{state-2T4KDWBX.mjs.map → state-ZQ4IPOJT.mjs.map} +1 -1
  44. package/dist/lib/browser/toolkit-JF4B4QIO.mjs +84 -0
  45. package/dist/lib/browser/toolkit-JF4B4QIO.mjs.map +7 -0
  46. package/dist/lib/browser/types/index.mjs +2 -2
  47. package/dist/lib/node-esm/ChannelContainer-OXC6BTU7.mjs +17 -0
  48. package/dist/lib/node-esm/{ChatContainer-2CT4PJ7I.mjs → ChatContainer-SH6LTFQL.mjs} +6 -6
  49. package/dist/lib/node-esm/{app-graph-builder-OLSJVCWY.mjs → app-graph-builder-DMPT3MY6.mjs} +17 -17
  50. package/dist/lib/node-esm/app-graph-builder-DMPT3MY6.mjs.map +7 -0
  51. package/dist/lib/node-esm/{call-manager-EI7FXRAU.mjs → call-manager-Q7RY2YP4.mjs} +7 -7
  52. package/dist/lib/node-esm/{call-manager-EI7FXRAU.mjs.map → call-manager-Q7RY2YP4.mjs.map} +1 -1
  53. package/dist/lib/node-esm/{chunk-YIDU3DXK.mjs → chunk-2KZ4ODHU.mjs} +17 -15
  54. package/dist/lib/node-esm/chunk-2KZ4ODHU.mjs.map +7 -0
  55. package/dist/lib/node-esm/{chunk-SED3O2XO.mjs → chunk-2MMJF7DW.mjs} +37 -5
  56. package/dist/lib/node-esm/chunk-2MMJF7DW.mjs.map +7 -0
  57. package/dist/lib/node-esm/{chunk-QBHKLWGR.mjs → chunk-2V3WSLUZ.mjs} +54 -4
  58. package/dist/lib/node-esm/chunk-2V3WSLUZ.mjs.map +7 -0
  59. package/dist/lib/node-esm/{chunk-57XR3LEF.mjs → chunk-ABUFOZVX.mjs} +2 -2
  60. package/dist/lib/node-esm/chunk-ABUFOZVX.mjs.map +7 -0
  61. package/dist/lib/node-esm/{chunk-XVKWHTH4.mjs → chunk-BAMZXJS4.mjs} +26 -16
  62. package/dist/lib/node-esm/chunk-BAMZXJS4.mjs.map +7 -0
  63. package/dist/lib/node-esm/chunk-BTGKI5IK.mjs +377 -0
  64. package/dist/lib/node-esm/chunk-BTGKI5IK.mjs.map +7 -0
  65. package/dist/lib/node-esm/chunk-GZ4WL2IS.mjs +25 -0
  66. package/dist/lib/node-esm/{chunk-6CRHHOST.mjs.map → chunk-GZ4WL2IS.mjs.map} +3 -3
  67. package/dist/lib/node-esm/chunk-LCHTNRK7.mjs +19 -0
  68. package/dist/lib/node-esm/chunk-LCHTNRK7.mjs.map +7 -0
  69. package/dist/lib/node-esm/chunk-NHSIAEDW.mjs +620 -0
  70. package/dist/lib/node-esm/chunk-NHSIAEDW.mjs.map +7 -0
  71. package/dist/lib/node-esm/{chunk-GRLRWTRU.mjs → chunk-OBVAURUU.mjs} +5 -7
  72. package/dist/lib/node-esm/chunk-OBVAURUU.mjs.map +7 -0
  73. package/dist/lib/node-esm/{chunk-4TME3BR4.mjs → chunk-TIPIOH2J.mjs} +460 -268
  74. package/dist/lib/node-esm/chunk-TIPIOH2J.mjs.map +7 -0
  75. package/dist/lib/node-esm/index.mjs +22 -13
  76. package/dist/lib/node-esm/index.mjs.map +3 -3
  77. package/dist/lib/node-esm/{intent-resolver-QGTPU6QB.mjs → intent-resolver-AKVH66PH.mjs} +73 -15
  78. package/dist/lib/node-esm/intent-resolver-AKVH66PH.mjs.map +7 -0
  79. package/dist/lib/node-esm/{markdown-3MZXUGUA.mjs → markdown-MYGTDXT6.mjs} +6 -6
  80. package/dist/lib/node-esm/{markdown-3MZXUGUA.mjs.map → markdown-MYGTDXT6.mjs.map} +1 -1
  81. package/dist/lib/node-esm/meta.json +1 -1
  82. package/dist/lib/node-esm/react-root-W6AVTL5M.mjs +29 -0
  83. package/dist/lib/node-esm/react-root-W6AVTL5M.mjs.map +7 -0
  84. package/dist/lib/node-esm/{react-surface-UJFCSFNO.mjs → react-surface-NLSZJGMO.mjs} +22 -22
  85. package/dist/lib/node-esm/react-surface-NLSZJGMO.mjs.map +7 -0
  86. package/dist/lib/node-esm/{settings-N52O7MLS.mjs → settings-ZQHGVFBY.mjs} +5 -5
  87. package/dist/lib/node-esm/{settings-N52O7MLS.mjs.map → settings-ZQHGVFBY.mjs.map} +1 -1
  88. package/dist/lib/node-esm/{state-62TQKQVB.mjs → state-FSLLJ5TZ.mjs} +5 -5
  89. package/dist/lib/node-esm/{state-62TQKQVB.mjs.map → state-FSLLJ5TZ.mjs.map} +1 -1
  90. package/dist/lib/node-esm/toolkit-DHPKJITO.mjs +85 -0
  91. package/dist/lib/node-esm/toolkit-DHPKJITO.mjs.map +7 -0
  92. package/dist/lib/node-esm/types/index.mjs +2 -2
  93. package/dist/types/src/ThreadPlugin.d.ts +1 -1
  94. package/dist/types/src/ThreadPlugin.d.ts.map +1 -1
  95. package/dist/types/src/calls/media-manager.d.ts +1 -1
  96. package/dist/types/src/calls/media-manager.d.ts.map +1 -1
  97. package/dist/types/src/calls/types.d.ts.map +1 -1
  98. package/dist/types/src/calls/util/calls-service.d.ts +3 -1
  99. package/dist/types/src/calls/util/calls-service.d.ts.map +1 -1
  100. package/dist/types/src/calls/util/stub-media-streams.d.ts.map +1 -1
  101. package/dist/types/src/calls-service.stories.d.ts +10 -9
  102. package/dist/types/src/calls-service.stories.d.ts.map +1 -1
  103. package/dist/types/src/capabilities/app-graph-builder.d.ts +1 -1
  104. package/dist/types/src/capabilities/app-graph-builder.d.ts.map +1 -1
  105. package/dist/types/src/capabilities/call-manager.d.ts +1 -1
  106. package/dist/types/src/capabilities/capabilities.d.ts.map +1 -1
  107. package/dist/types/src/capabilities/index.d.ts +11 -10
  108. package/dist/types/src/capabilities/index.d.ts.map +1 -1
  109. package/dist/types/src/capabilities/intent-resolver.d.ts +1 -1
  110. package/dist/types/src/capabilities/intent-resolver.d.ts.map +1 -1
  111. package/dist/types/src/capabilities/markdown.d.ts +1 -1
  112. package/dist/types/src/capabilities/react-root.d.ts +2 -2
  113. package/dist/types/src/capabilities/react-surface.d.ts +1 -1
  114. package/dist/types/src/capabilities/settings.d.ts +1 -1
  115. package/dist/types/src/capabilities/state.d.ts +2 -2
  116. package/dist/types/src/capabilities/toolkit.d.ts +6 -0
  117. package/dist/types/src/capabilities/toolkit.d.ts.map +1 -0
  118. package/dist/types/src/components/CallDebugPanel.d.ts +7 -0
  119. package/dist/types/src/components/CallDebugPanel.d.ts.map +1 -1
  120. package/dist/types/src/components/ChannelContainer.stories.d.ts +89 -5
  121. package/dist/types/src/components/ChannelContainer.stories.d.ts.map +1 -1
  122. package/dist/types/src/components/Chat.stories.d.ts +91 -5
  123. package/dist/types/src/components/Chat.stories.d.ts.map +1 -1
  124. package/dist/types/src/components/ChatContainer.d.ts.map +1 -1
  125. package/dist/types/src/components/Comments.stories.d.ts +156 -4
  126. package/dist/types/src/components/Comments.stories.d.ts.map +1 -1
  127. package/dist/types/src/components/CommentsThreadContainer.d.ts +2 -2
  128. package/dist/types/src/components/CommentsThreadContainer.d.ts.map +1 -1
  129. package/dist/types/src/components/Media/VideoObject.d.ts +2 -2
  130. package/dist/types/src/components/Media/VideoObject.d.ts.map +1 -1
  131. package/dist/types/src/components/Media/VideoObject.stories.d.ts +18 -4
  132. package/dist/types/src/components/Media/VideoObject.stories.d.ts.map +1 -1
  133. package/dist/types/src/components/MessageContainer.d.ts +4 -1
  134. package/dist/types/src/components/MessageContainer.d.ts.map +1 -1
  135. package/dist/types/src/components/Participant/Participant.d.ts.map +1 -1
  136. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts +155 -3
  137. package/dist/types/src/components/ResponsiveGrid/ResponsiveContainer.stories.d.ts.map +1 -1
  138. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts +157 -4
  139. package/dist/types/src/components/ResponsiveGrid/ResponsiveGrid.stories.d.ts.map +1 -1
  140. package/dist/types/src/components/ThreadComplementary.d.ts.map +1 -1
  141. package/dist/types/src/components/ThreadSettings.d.ts +1 -1
  142. package/dist/types/src/components/ThreadSettings.d.ts.map +1 -1
  143. package/dist/types/src/components/command-extension.d.ts.map +1 -1
  144. package/dist/types/src/components/testing.d.ts +1 -0
  145. package/dist/types/src/components/testing.d.ts.map +1 -1
  146. package/dist/types/src/hooks/index.d.ts +1 -0
  147. package/dist/types/src/hooks/index.d.ts.map +1 -1
  148. package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts +2 -0
  149. package/dist/types/src/hooks/useBlackCanvasStreamTrack.d.ts.map +1 -0
  150. package/dist/types/src/hooks/useVideoStreamTrack.d.ts +1 -1
  151. package/dist/types/src/hooks/useVideoStreamTrack.d.ts.map +1 -1
  152. package/dist/types/src/meta.d.ts +1 -2
  153. package/dist/types/src/meta.d.ts.map +1 -1
  154. package/dist/types/src/translations.d.ts +3 -1
  155. package/dist/types/src/translations.d.ts.map +1 -1
  156. package/dist/types/src/types/schema.d.ts +126 -111
  157. package/dist/types/src/types/schema.d.ts.map +1 -1
  158. package/dist/types/src/types/types.d.ts +259 -174
  159. package/dist/types/src/types/types.d.ts.map +1 -1
  160. package/dist/types/tsconfig.tsbuildinfo +1 -1
  161. package/package.json +60 -58
  162. package/src/ThreadPlugin.tsx +128 -122
  163. package/src/calls/call-manager.ts +1 -1
  164. package/src/calls/media-manager.ts +58 -32
  165. package/src/calls/types.ts +7 -3
  166. package/src/calls/util/calls-service.ts +61 -11
  167. package/src/calls/util/stub-media-streams.ts +60 -3
  168. package/src/calls-service.stories.tsx +76 -63
  169. package/src/capabilities/app-graph-builder.ts +13 -10
  170. package/src/capabilities/capabilities.ts +5 -5
  171. package/src/capabilities/index.ts +1 -0
  172. package/src/capabilities/intent-resolver.ts +39 -6
  173. package/src/capabilities/react-root.tsx +2 -2
  174. package/src/capabilities/react-surface.tsx +9 -9
  175. package/src/capabilities/toolkit.ts +82 -0
  176. package/src/components/Call/Toolbar.tsx +4 -4
  177. package/src/components/CallDebugPanel.tsx +108 -49
  178. package/src/components/ChannelContainer.stories.tsx +9 -9
  179. package/src/components/ChannelContainer.tsx +1 -1
  180. package/src/components/Chat.stories.tsx +31 -29
  181. package/src/components/ChatContainer.tsx +4 -1
  182. package/src/components/Comments.stories.tsx +33 -35
  183. package/src/components/CommentsThreadContainer.tsx +8 -7
  184. package/src/components/Media/VideoObject.stories.tsx +35 -28
  185. package/src/components/MessageContainer.tsx +40 -7
  186. package/src/components/Participant/Participant.tsx +13 -7
  187. package/src/components/ResponsiveGrid/ResponsiveContainer.stories.tsx +5 -6
  188. package/src/components/ResponsiveGrid/ResponsiveGrid.stories.tsx +41 -40
  189. package/src/components/ThreadComplementary.tsx +36 -6
  190. package/src/components/ThreadSettings.tsx +6 -7
  191. package/src/components/command-extension.ts +4 -1
  192. package/src/components/testing.ts +22 -0
  193. package/src/hooks/index.ts +1 -0
  194. package/src/hooks/useBlackCanvasStreamTrack.tsx +27 -0
  195. package/src/hooks/useVideoStreamTrack.tsx +6 -3
  196. package/src/meta.ts +3 -4
  197. package/src/testing/stories.ts +1 -1
  198. package/src/translations.ts +3 -1
  199. package/src/types/schema.ts +1 -1
  200. package/src/types/types.ts +21 -13
  201. package/src/typings/peermetrics-webrtc-stats.d.ts +17 -1
  202. package/src/util.ts +1 -1
  203. package/dist/lib/browser/ChannelContainer-O2SFQJU7.mjs +0 -16
  204. package/dist/lib/browser/ChatContainer-PVT3DUZJ.mjs +0 -15
  205. package/dist/lib/browser/app-graph-builder-AF3M7UWN.mjs.map +0 -7
  206. package/dist/lib/browser/chunk-2CWJBXBH.mjs +0 -21
  207. package/dist/lib/browser/chunk-4APCDRSH.mjs +0 -18
  208. package/dist/lib/browser/chunk-4APCDRSH.mjs.map +0 -7
  209. package/dist/lib/browser/chunk-DW7JD64V.mjs.map +0 -7
  210. package/dist/lib/browser/chunk-EV4JWI7L.mjs.map +0 -7
  211. package/dist/lib/browser/chunk-FY4ZRD5X.mjs +0 -367
  212. package/dist/lib/browser/chunk-FY4ZRD5X.mjs.map +0 -7
  213. package/dist/lib/browser/chunk-GERVMFQW.mjs.map +0 -7
  214. package/dist/lib/browser/chunk-J3JO5NWO.mjs +0 -477
  215. package/dist/lib/browser/chunk-J3JO5NWO.mjs.map +0 -7
  216. package/dist/lib/browser/chunk-JGKMMI65.mjs.map +0 -7
  217. package/dist/lib/browser/chunk-TUFWPOVS.mjs.map +0 -7
  218. package/dist/lib/browser/chunk-X5PIP5YR.mjs.map +0 -7
  219. package/dist/lib/browser/chunk-ZJ7QBXK6.mjs.map +0 -7
  220. package/dist/lib/browser/intent-resolver-DCY4P4OX.mjs.map +0 -7
  221. package/dist/lib/browser/react-root-QXPALOUQ.mjs +0 -28
  222. package/dist/lib/browser/react-root-QXPALOUQ.mjs.map +0 -7
  223. package/dist/lib/browser/react-surface-PYAMIADB.mjs.map +0 -7
  224. package/dist/lib/node-esm/ChannelContainer-B2ZGAE4P.mjs +0 -17
  225. package/dist/lib/node-esm/app-graph-builder-OLSJVCWY.mjs.map +0 -7
  226. package/dist/lib/node-esm/chunk-4TME3BR4.mjs.map +0 -7
  227. package/dist/lib/node-esm/chunk-57XR3LEF.mjs.map +0 -7
  228. package/dist/lib/node-esm/chunk-6CRHHOST.mjs +0 -23
  229. package/dist/lib/node-esm/chunk-BF4UKS55.mjs +0 -368
  230. package/dist/lib/node-esm/chunk-BF4UKS55.mjs.map +0 -7
  231. package/dist/lib/node-esm/chunk-GRLRWTRU.mjs.map +0 -7
  232. package/dist/lib/node-esm/chunk-PYTVBN5J.mjs +0 -19
  233. package/dist/lib/node-esm/chunk-PYTVBN5J.mjs.map +0 -7
  234. package/dist/lib/node-esm/chunk-QBHKLWGR.mjs.map +0 -7
  235. package/dist/lib/node-esm/chunk-VNG6M2IY.mjs +0 -478
  236. package/dist/lib/node-esm/chunk-VNG6M2IY.mjs.map +0 -7
  237. package/dist/lib/node-esm/chunk-XVKWHTH4.mjs.map +0 -7
  238. package/dist/lib/node-esm/chunk-YIDU3DXK.mjs.map +0 -7
  239. package/dist/lib/node-esm/intent-resolver-QGTPU6QB.mjs.map +0 -7
  240. package/dist/lib/node-esm/react-root-7ZFJYVUZ.mjs +0 -29
  241. package/dist/lib/node-esm/react-root-7ZFJYVUZ.mjs.map +0 -7
  242. package/dist/lib/node-esm/react-surface-UJFCSFNO.mjs.map +0 -7
  243. /package/dist/lib/browser/{ChannelContainer-O2SFQJU7.mjs.map → ChannelContainer-BGDGZHV4.mjs.map} +0 -0
  244. /package/dist/lib/browser/{ChatContainer-PVT3DUZJ.mjs.map → ChatContainer-75KJZ4XR.mjs.map} +0 -0
  245. /package/dist/lib/node-esm/{ChannelContainer-B2ZGAE4P.mjs.map → ChannelContainer-OXC6BTU7.mjs.map} +0 -0
  246. /package/dist/lib/node-esm/{ChatContainer-2CT4PJ7I.mjs.map → ChatContainer-SH6LTFQL.mjs.map} +0 -0
@@ -2,7 +2,7 @@
2
2
  // Copyright 2024 DXOS.org
3
3
  //
4
4
 
5
- import { PersistentLifecycle } from '@dxos/async';
5
+ import { PersistentLifecycle, Trigger, scheduleTask } from '@dxos/async';
6
6
  import { type Context, Resource } from '@dxos/context';
7
7
  import { invariant } from '@dxos/invariant';
8
8
  import { log } from '@dxos/log';
@@ -33,12 +33,14 @@ export type CallsServiceConfig = {
33
33
 
34
34
  export type HistoryRecord =
35
35
  | {
36
+ endpoint: string;
36
37
  type: 'request';
37
38
  time: string;
38
39
  method: string;
39
40
  body: any;
40
41
  }
41
42
  | {
43
+ endpoint: string;
42
44
  type: 'response';
43
45
  time: string;
44
46
  status: number;
@@ -116,7 +118,7 @@ export class CallsServicePeer extends Resource {
116
118
  });
117
119
  peerConnection.addEventListener('connectionstatechange', () => {
118
120
  if (peerConnection.connectionState === 'failed' || peerConnection.connectionState === 'closed') {
119
- log.warn('calls connection failed', { state: peerConnection.connectionState });
121
+ log.info('calls connection failed', { state: peerConnection.connectionState });
120
122
  void this._persistentLifecycle.scheduleRestart();
121
123
  }
122
124
  });
@@ -125,7 +127,7 @@ export class CallsServicePeer extends Resource {
125
127
  peerConnection.addEventListener('iceconnectionstatechange', () => {
126
128
  clearTimeout(iceTimeout);
127
129
  if (peerConnection.iceConnectionState === 'failed' || peerConnection.iceConnectionState === 'closed') {
128
- log.warn('calls connection to ICEs failed', { state: peerConnection.iceConnectionState });
130
+ log.info('calls connection to ICEs failed', { state: peerConnection.iceConnectionState });
129
131
  void this._persistentLifecycle.scheduleRestart();
130
132
  } else if (peerConnection.iceConnectionState === 'disconnected') {
131
133
  // TODO(mykola): we should start to inspect the connection stats from here on for
@@ -138,7 +140,7 @@ export class CallsServicePeer extends Resource {
138
140
  return;
139
141
  }
140
142
 
141
- log.warn('calls iceConnectionState timed out', { state: peerConnection.iceConnectionState, timeoutSeconds });
143
+ log.info('calls iceConnectionState timed out', { state: peerConnection.iceConnectionState, timeoutSeconds });
142
144
  void this._persistentLifecycle.scheduleRestart();
143
145
  }, timeoutSeconds * 1_000);
144
146
  }
@@ -155,6 +157,7 @@ export class CallsServicePeer extends Resource {
155
157
 
156
158
  private async _fetch<T extends ErrorResponse>(relativePath: string, requestInit?: RequestInit): Promise<T> {
157
159
  this.history.push({
160
+ endpoint: relativePath,
158
161
  type: 'request',
159
162
  time: new Date().toISOString(),
160
163
  method: requestInit?.method ?? 'GET',
@@ -170,6 +173,7 @@ export class CallsServicePeer extends Resource {
170
173
  try {
171
174
  const data = (await response.clone().json()) as T;
172
175
  this.history.push({
176
+ endpoint: relativePath,
173
177
  type: 'response',
174
178
  time: new Date().toISOString(),
175
179
  status: response.status,
@@ -183,6 +187,7 @@ export class CallsServicePeer extends Resource {
183
187
  } catch (error) {
184
188
  const text = await response.text();
185
189
  this.history.push({
190
+ endpoint: relativePath,
186
191
  type: 'response',
187
192
  time: new Date().toISOString(),
188
193
  status: response.status,
@@ -204,11 +209,11 @@ export class CallsServicePeer extends Resource {
204
209
  sessionId,
205
210
  trackName,
206
211
  }: {
212
+ ctx: Context;
207
213
  peerConnection: RTCPeerConnection;
208
214
  transceiver: RTCRtpTransceiver;
209
215
  sessionId: string;
210
216
  trackName: string;
211
- ctx?: Context;
212
217
  }): Promise<TrackObject | void> {
213
218
  log.verbose('pushing track ', trackName);
214
219
  const pushedTrackPromise = this.pushTrackDispatcher
@@ -246,9 +251,13 @@ export class CallsServicePeer extends Resource {
246
251
  };
247
252
  }),
248
253
  )
249
- .then(({ tracks }) => {
254
+ .then(async ({ tracks }) => {
250
255
  const trackData = tracks.find((t) => t.mid === transceiver.mid);
251
256
  if (trackData) {
257
+ // we wait for the transceiver to start sending data before we emit
258
+ // the track metadata to ensure that the track will be able to be
259
+ // pulled before making the metadata available to anyone else.
260
+ // await waitForTransceiverToSendData(ctx, transceiver);
252
261
  return {
253
262
  ...trackData,
254
263
  sessionId,
@@ -257,9 +266,6 @@ export class CallsServicePeer extends Resource {
257
266
  } else {
258
267
  log.error('missing track data');
259
268
  }
260
- })
261
- .catch((err) => {
262
- log.catch(err);
263
269
  });
264
270
 
265
271
  const onDispose = async () => {
@@ -281,8 +287,8 @@ export class CallsServicePeer extends Resource {
281
287
  previousTrack,
282
288
  }: {
283
289
  track: MediaStreamTrack | null;
290
+ ctx: Context;
284
291
  encodings?: RTCRtpEncodingParameters[];
285
- ctx?: Context;
286
292
  previousTrack?: TrackObject;
287
293
  }): Promise<TrackObject | undefined> {
288
294
  await this.waitUntilOpen();
@@ -405,7 +411,7 @@ export class CallsServicePeer extends Resource {
405
411
  })
406
412
  .catch((err) => log.catch(err));
407
413
  } else {
408
- log.warn('missing track info', { trackData, availableTracks: Array.from(trackMap.keys()) });
414
+ log.info('missing track info', { trackData, availableTracks: Array.from(trackMap.keys()) });
409
415
  }
410
416
  });
411
417
 
@@ -529,3 +535,47 @@ const peerConnectionIsConnected = async (peerConnection: RTCPeerConnection) => {
529
535
  await connected;
530
536
  }
531
537
  };
538
+
539
+ const INITIAL_DELAY = 50;
540
+ const MAX_DELAY = 500;
541
+ const MAX_RETRIES = 20;
542
+ const WAIT_FOR_DATA_TIMEOUT = 10_000;
543
+
544
+ const waitForTransceiverToSendData = async (ctx: Context, transceiver: RTCRtpTransceiver): Promise<void> => {
545
+ let delay = INITIAL_DELAY;
546
+ let checks = 0;
547
+ const waitForData = new Trigger();
548
+ ctx.onDispose(() => waitForData.throw(new Error('Transceiver was closed')));
549
+
550
+ const checkStats = async () => {
551
+ if (ctx.disposed) return;
552
+
553
+ try {
554
+ const stats = await transceiver.sender.getStats();
555
+ let dataFound = false;
556
+ stats.forEach((stat) => {
557
+ if (stat.type === 'outbound-rtp' && stat.bytesSent > 0) {
558
+ dataFound = true;
559
+ }
560
+ });
561
+
562
+ if (dataFound) {
563
+ waitForData.wake();
564
+ return;
565
+ }
566
+ } catch {
567
+ // Stats might not be available yet, continue checking
568
+ }
569
+ checks++;
570
+ if (checks > MAX_RETRIES) {
571
+ waitForData.throw(new Error('Transceiver did not send data'));
572
+ return;
573
+ }
574
+
575
+ delay = Math.min(delay * 2, MAX_DELAY); // Exponential backoff with max cap
576
+ scheduleTask(ctx, checkStats, delay);
577
+ };
578
+
579
+ void checkStats();
580
+ return waitForData.wait({ timeout: WAIT_FOR_DATA_TIMEOUT });
581
+ };
@@ -30,7 +30,7 @@ export const createBlackCanvasStreamTrack = async ({
30
30
  canvasCtx.fillStyle = 'black';
31
31
  canvasCtx.fillRect(0, 0, canvas.width, canvas.height);
32
32
  };
33
- scheduleTaskInterval(ctx, async () => drawFrame(), 1_000);
33
+ scheduleTaskInterval(ctx, async () => drawFrame(), 200);
34
34
 
35
35
  const track = canvas.captureStream().getVideoTracks()[0];
36
36
  drawFrame();
@@ -40,6 +40,20 @@ export const createBlackCanvasStreamTrack = async ({
40
40
  return track;
41
41
  };
42
42
 
43
+ let audioContextStartedPreviously = false;
44
+ const userGestureEvents = [
45
+ 'click',
46
+ 'contextmenu',
47
+ 'auxclick',
48
+ 'dblclick',
49
+ 'mousedown',
50
+ 'mouseup',
51
+ 'pointerup',
52
+ 'touchend',
53
+ 'keydown',
54
+ 'keyup',
55
+ ];
56
+
43
57
  /**
44
58
  * Creates an inaudible audio stream track.
45
59
  * We need this to have audio track available when audio is disabled.
@@ -53,14 +67,57 @@ export const createInaudibleAudioStreamTrack = async ({ ctx }: { ctx: Context })
53
67
 
54
68
  const gainNode = audioContext.createGain();
55
69
  // even w/ gain at 0 some packets are sent
56
- gainNode.gain.setValueAtTime(0.02, audioContext.currentTime);
70
+ gainNode.gain.setValueAtTime(0, audioContext.currentTime);
57
71
  oscillator.connect(gainNode);
58
72
 
59
73
  const destination = audioContext.createMediaStreamDestination();
60
74
  gainNode.connect(destination);
61
- oscillator.start();
75
+
76
+ let oscillatorStarted = false;
77
+ const ensureOscillatorStarted = () => {
78
+ if (oscillatorStarted) return;
79
+ oscillator.start();
80
+ oscillatorStarted = true;
81
+ };
82
+
83
+ const stateChangeHandler = () => {
84
+ if (audioContext.state === 'running') {
85
+ audioContextStartedPreviously = true;
86
+ ensureOscillatorStarted();
87
+ }
88
+ if (audioContext.state === 'suspended' || (audioContext.state as string) === 'interrupted') {
89
+ resumeAudioContext();
90
+ }
91
+ };
92
+
93
+ audioContext.addEventListener('statechange', stateChangeHandler);
94
+
95
+ const resumeAudioContext = () => {
96
+ void audioContext.resume().then(() => {
97
+ cleanUpUserGestureListeners();
98
+ });
99
+ };
100
+
101
+ const cleanUpUserGestureListeners = () => {
102
+ userGestureEvents.forEach((gesture) => {
103
+ document.removeEventListener(gesture, resumeAudioContext, {
104
+ capture: true,
105
+ });
106
+ });
107
+ };
108
+
109
+ if (audioContextStartedPreviously) {
110
+ resumeAudioContext();
111
+ } else {
112
+ userGestureEvents.forEach((gesture) => {
113
+ document.addEventListener(gesture, resumeAudioContext, {
114
+ capture: true,
115
+ });
116
+ });
117
+ }
62
118
 
63
119
  ctx.onDispose(async () => {
120
+ cleanUpUserGestureListeners();
64
121
  oscillator.disconnect();
65
122
  oscillator.stop();
66
123
  await audioContext.close();
@@ -2,8 +2,7 @@
2
2
  // Copyright 2025 DXOS.org
3
3
  //
4
4
 
5
- import '@dxos-theme';
6
-
5
+ import { type Meta, type StoryObj } from '@storybook/react-vite';
7
6
  import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
8
7
 
9
8
  import { scheduleTask, sleep } from '@dxos/async';
@@ -13,14 +12,15 @@ import { log } from '@dxos/log';
13
12
  import { Config, useConfig } from '@dxos/react-client';
14
13
  import { withClientProvider } from '@dxos/react-client/testing';
15
14
  import { Button } from '@dxos/react-ui';
15
+ import { withTheme } from '@dxos/react-ui/testing';
16
16
  import { Json } from '@dxos/react-ui-syntax-highlighter';
17
- import { withLayout, withTheme } from '@dxos/storybook-utils';
18
17
 
19
- import video from '../testing/video.mp4?raw';
18
+ const testVideo = new URL('../testing/video.mp4', import.meta.url).href;
20
19
 
21
20
  import { CALLS_URL, CallsServicePeer, type TrackObject } from './calls';
22
- import { useInaudibleAudioStreamTrack, useVideoStreamTrack } from './hooks';
21
+ import { useBlackCanvasStreamTrack, useInaudibleAudioStreamTrack, useVideoStreamTrack } from './hooks';
23
22
 
23
+ // TODO(burdon): THIS IS TOO COMPLEX FOR A TEST SETUP.
24
24
  const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
25
25
  const config = useConfig();
26
26
  const callsConfig = {
@@ -35,16 +35,18 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
35
35
  const [pulledTrack, setPulledTrack] = useState<MediaStreamTrack | undefined>(undefined);
36
36
  const hadRun = useRef(false);
37
37
 
38
+ const deps: any[] = [];
38
39
  // Push/pull video stream track to cloudflare.
39
40
  useEffect(() => {
40
41
  if (hadRun.current || !mediaStreamTrack || !peerPush || !peerPull) {
41
- log.info('not running', { hadRun: hadRun.current, mediaStreamTrack, peerPush, peerPull });
42
42
  return;
43
43
  }
44
44
  hadRun.current = true;
45
45
  const ctx = Context.default();
46
+ log.info('setting up ctx', { ctx, mediaStreamTrack, peerPush, peerPull });
47
+ deps.push([ctx, mediaStreamTrack, peerPush, peerPull]);
46
48
  ctx.onDispose(() => {
47
- log.info('disposing ctx', { ctx });
49
+ log.info('disposing ctx', {});
48
50
  });
49
51
  scheduleTask(
50
52
  ctx,
@@ -60,15 +62,12 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
60
62
  // Push track to cloudflare.
61
63
  performance.mark('push:begin');
62
64
  log.info('pushing track', { track: mediaStreamTrack });
63
- trackInfo.current = await peerPush.pushTrack({ track: mediaStreamTrack });
65
+ trackInfo.current = await peerPush.pushTrack({ track: mediaStreamTrack, ctx: Context.default() });
64
66
  performance.mark('push:end');
65
67
  const pushTime = performance.measure('push', 'push:begin', 'push:end').duration;
66
68
  log.info('successfully pushed track', { pushTime, trackInfo: trackInfo.current });
67
69
  setMetrics((prev) => ({ ...prev, 'time to push track [ms]': Math.round(pushTime) }));
68
70
 
69
- // Wait for cloudflare to process the track.
70
- await sleep(1000);
71
-
72
71
  // Pull track from cloudflare.
73
72
  performance.mark('pullTrack:begin');
74
73
  pullCtx.current = Context.default();
@@ -104,7 +103,10 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
104
103
 
105
104
  const rePullTrack = useCallback(async () => {
106
105
  performance.mark('rePullVideo:begin');
107
- invariant(pulledTrack);
106
+ if (!pulledTrack) {
107
+ log.info('no pulled track, skipping re-pull', { pulledTrack });
108
+ return;
109
+ }
108
110
  pulledTrack.stop();
109
111
  await pullCtx.current!.dispose();
110
112
  pullCtx.current = Context.default();
@@ -123,12 +125,57 @@ const pushAndPullTrack = (mediaStreamTrack?: MediaStreamTrack) => {
123
125
  pulledTrack,
124
126
  rePullTrack,
125
127
  metrics,
128
+ pushService: peerPush,
129
+ pullService: peerPull,
126
130
  };
127
131
  };
128
132
 
133
+ type StoryProps = { source: string };
134
+
135
+ const DefaultStory = ({ source }: StoryProps) => {
136
+ const pushVideoElement = useRef<HTMLVideoElement>(null);
137
+ const pullVideoElement = useRef<HTMLVideoElement>(null);
138
+ // Get video stream track.
139
+ const videoStreamTrack = useVideoStreamTrack(pushVideoElement, source);
140
+ const { pulledTrack, rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
141
+ const hadRun = useRef(false);
142
+
143
+ // Push/pull video stream track to cloudflare.
144
+ useEffect(() => {
145
+ if (!pulledTrack || hadRun.current) {
146
+ return;
147
+ }
148
+ hadRun.current = true;
149
+ const ctx = Context.default();
150
+ scheduleTask(ctx, async () => {
151
+ pullVideoElement.current!.srcObject = new MediaStream([pulledTrack]);
152
+ await pullVideoElement.current!.play();
153
+ });
154
+
155
+ return () => {
156
+ void ctx.dispose();
157
+ };
158
+ }, [pulledTrack]);
159
+
160
+ return (
161
+ <div className='grid grid-rows-3 gap-4 items-center'>
162
+ <video ref={pushVideoElement} muted autoPlay loop />
163
+ <div className='flex flex-col gap-4'>
164
+ <video ref={pullVideoElement} muted />
165
+ <Button disabled={!pulledTrack} onClick={rePullTrack}>
166
+ Re-pull video
167
+ </Button>
168
+ </div>
169
+ <Json data={metrics} />
170
+ </div>
171
+ );
172
+ };
173
+
129
174
  const meta = {
130
175
  title: 'plugins/plugin-thread/calls-service',
176
+ render: DefaultStory,
131
177
  decorators: [
178
+ withTheme,
132
179
  withClientProvider({
133
180
  config: new Config({
134
181
  runtime: {
@@ -138,72 +185,25 @@ const meta = {
138
185
  },
139
186
  }),
140
187
  }),
141
- withTheme,
142
- withLayout(),
143
188
  ],
144
189
  parameters: {
145
190
  layout: 'centered',
146
191
  },
147
- };
192
+ } satisfies Meta<typeof DefaultStory>;
148
193
 
149
194
  export default meta;
150
195
 
151
- export const Default = {
152
- render: ({ videoSrc }: { videoSrc: string }) => {
153
- const pushVideoElement = useRef<HTMLVideoElement>(null);
154
- const pullVideoElement = useRef<HTMLVideoElement>(null);
155
- // Get video stream track.
156
- const videoStreamTrack = useVideoStreamTrack(pushVideoElement, videoSrc);
157
- const { pulledTrack, rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
158
- const hadRun = useRef(false);
159
-
160
- // Push/pull video stream track to cloudflare.
161
- useEffect(() => {
162
- if (!pulledTrack || hadRun.current) {
163
- return;
164
- }
165
- hadRun.current = true;
166
- const ctx = Context.default();
167
- scheduleTask(ctx, async () => {
168
- pullVideoElement.current!.srcObject = new MediaStream([pulledTrack]);
169
- await pullVideoElement.current!.play();
170
- });
171
-
172
- return () => {
173
- void ctx.dispose();
174
- };
175
- }, [pulledTrack]);
196
+ type Story = StoryObj<typeof meta>;
176
197
 
177
- return (
178
- <div className='grid grid-rows-3 gap-4 items-center'>
179
- <video ref={pushVideoElement} muted autoPlay loop />
180
- <div className='flex flex-col gap-4'>
181
- <video ref={pullVideoElement} muted />
182
- <Button
183
- disabled={
184
- !(
185
- pullVideoElement?.current?.srcObject instanceof MediaStream &&
186
- pullVideoElement.current.srcObject.getTracks()[0]
187
- )
188
- }
189
- onClick={rePullTrack}
190
- >
191
- Re-pull video
192
- </Button>
193
- </div>
194
- <Json data={metrics} />
195
- </div>
196
- );
197
- },
198
+ export const Default: Story = {
198
199
  args: {
199
- videoSrc: video,
200
+ source: testVideo,
200
201
  },
201
202
  };
202
203
 
203
204
  export const InaudibleAudioStreamTrack = {
204
205
  render: () => {
205
206
  const audioStreamTrack = useInaudibleAudioStreamTrack();
206
-
207
207
  const { rePullTrack, metrics } = pushAndPullTrack(audioStreamTrack);
208
208
 
209
209
  return (
@@ -214,3 +214,16 @@ export const InaudibleAudioStreamTrack = {
214
214
  );
215
215
  },
216
216
  };
217
+
218
+ export const BlackVideoStreamTrack = {
219
+ render: () => {
220
+ const videoStreamTrack = useBlackCanvasStreamTrack();
221
+ const { rePullTrack, metrics } = pushAndPullTrack(videoStreamTrack);
222
+ return (
223
+ <div className='flex flex-col gap-4 items-center'>
224
+ <Json data={metrics} />
225
+ <Button onClick={rePullTrack}>Re-pull audio</Button>
226
+ </div>
227
+ );
228
+ },
229
+ };
@@ -3,7 +3,8 @@
3
3
  //
4
4
 
5
5
  import { Rx } from '@effect-rx/rx-react';
6
- import { Option, pipe } from 'effect';
6
+ import * as Function from 'effect/Function';
7
+ import * as Option from 'effect/Option';
7
8
 
8
9
  import { Capabilities, type PluginContext, contributes, createIntent } from '@dxos/app-framework';
9
10
  import { Obj } from '@dxos/echo';
@@ -12,22 +13,24 @@ import { ATTENDABLE_PATH_SEPARATOR, DECK_COMPANION_TYPE, PLANK_COMPANION_TYPE }
12
13
  import { ROOT_ID, createExtension, rxFromSignal } from '@dxos/plugin-graph';
13
14
  import { fullyQualifiedId } from '@dxos/react-client/echo';
14
15
 
15
- import { THREAD_PLUGIN, meta } from '../meta';
16
+ import { meta } from '../meta';
16
17
  import { ChannelType, ThreadAction } from '../types';
17
18
  import { getAnchor } from '../util';
18
19
 
19
20
  import { ThreadCapabilities } from './capabilities';
20
21
 
22
+ // TODO(wittjosiah): Highlight active calls in L1.
23
+ // Track active meetings by subscribing to meetings query and polling the swarms of recent meetings in the space.
21
24
  export default (context: PluginContext) => {
22
25
  const resolve = (typename: string) =>
23
26
  context.getCapabilities(Capabilities.Metadata).find(({ id }) => id === typename)?.metadata ?? {};
24
27
 
25
28
  return contributes(Capabilities.AppGraphBuilder, [
26
29
  createExtension({
27
- id: `${THREAD_PLUGIN}/active-call`,
30
+ id: `${meta.id}/active-call`,
28
31
  connector: (node) =>
29
32
  Rx.make((get) =>
30
- pipe(
33
+ Function.pipe(
31
34
  get(node),
32
35
  Option.flatMap((node) => (node.id === ROOT_ID ? Option.some(node) : Option.none())),
33
36
  Option.map((node) => {
@@ -41,7 +44,7 @@ export default (context: PluginContext) => {
41
44
  type: DECK_COMPANION_TYPE,
42
45
  data: null,
43
46
  properties: {
44
- label: ['call panel label', { ns: THREAD_PLUGIN }],
47
+ label: ['call panel label', { ns: meta.id }],
45
48
  icon: 'ph--video-conference--regular',
46
49
  position: 'hoist',
47
50
  disposition: 'hidden',
@@ -59,10 +62,10 @@ export default (context: PluginContext) => {
59
62
  // TODO(wittjosiah): The channel shouldn't become the companion during a call.
60
63
  // Alternative: the call/meeting/thread should be a child node of the channel and that should be opened.
61
64
  createExtension({
62
- id: `${THREAD_PLUGIN}/channel-chat-companion`,
65
+ id: `${meta.id}/channel-chat-companion`,
63
66
  connector: (node) => {
64
67
  return Rx.make((get) => {
65
- return pipe(
68
+ return Function.pipe(
66
69
  get(node),
67
70
  Option.flatMap((node) => (Obj.instanceOf(ChannelType, node.data) ? Option.some(node.data) : Option.none())),
68
71
  Option.map((channel) => {
@@ -80,7 +83,7 @@ export default (context: PluginContext) => {
80
83
  type: PLANK_COMPANION_TYPE,
81
84
  data: 'chat',
82
85
  properties: {
83
- label: ['channel companion label', { ns: THREAD_PLUGIN }],
86
+ label: ['channel companion label', { ns: meta.id }],
84
87
  icon: 'ph--hash--regular',
85
88
  position: 'hoist',
86
89
  disposition: 'hidden',
@@ -97,7 +100,7 @@ export default (context: PluginContext) => {
97
100
  id: `${meta.id}/comments-companion`,
98
101
  connector: (node) =>
99
102
  Rx.make((get) =>
100
- pipe(
103
+ Function.pipe(
101
104
  get(node),
102
105
  Option.flatMap((node) => {
103
106
  if (!Obj.isObject(node.data) || Obj.instanceOf(ChannelType, node.data)) {
@@ -127,7 +130,7 @@ export default (context: PluginContext) => {
127
130
  id: `${meta.id}/comment-toolbar`,
128
131
  actions: (node) =>
129
132
  Rx.make((get) =>
130
- pipe(
133
+ Function.pipe(
131
134
  get(node),
132
135
  Option.flatMap((node) => {
133
136
  if (!Obj.isObject(node.data) || Obj.instanceOf(ChannelType, node.data)) {
@@ -6,11 +6,11 @@ import { defineCapability } from '@dxos/app-framework';
6
6
  import { type DeepReadonly } from '@dxos/util';
7
7
 
8
8
  import { type CallManager, type CallState, type MediaState } from '../calls';
9
- import { THREAD_PLUGIN } from '../meta';
9
+ import { meta } from '../meta';
10
10
  import { type ChannelType, type ThreadState, type ViewState } from '../types';
11
11
 
12
12
  export namespace ThreadCapabilities {
13
- export const CallManager = defineCapability<CallManager>(`${THREAD_PLUGIN}/capability/call-manager`);
13
+ export const CallManager = defineCapability<CallManager>(`${meta.id}/capability/call-manager`);
14
14
 
15
15
  // TODO(burdon): Better way to define specific extensions for meeting companions.
16
16
  // TODO(burdon): This brings in deps from ../calls; how should we manage/minimize explicit type exposure to other plugins?
@@ -21,13 +21,13 @@ export namespace ThreadCapabilities {
21
21
  onCallStateUpdated: (callState: CallState) => Promise<void>;
22
22
  onMediaStateUpdated: ([mediaState, isSpeaking]: [MediaState, boolean]) => Promise<void>;
23
23
  };
24
- export const CallExtension = defineCapability<CallProperties>(`${THREAD_PLUGIN}/capability/call-extension`);
24
+ export const CallExtension = defineCapability<CallProperties>(`${meta.id}/capability/call-extension`);
25
25
 
26
26
  type GetViewState = (subjectId: string) => ViewState;
27
27
  export const State = defineCapability<{ state: DeepReadonly<ThreadState>; getViewState: GetViewState }>(
28
- `${THREAD_PLUGIN}/capability/state`,
28
+ `${meta.id}/capability/state`,
29
29
  );
30
30
  export const MutableState = defineCapability<{ state: ThreadState; getViewState: GetViewState }>(
31
- `${THREAD_PLUGIN}/capability/state`,
31
+ `${meta.id}/capability/state`,
32
32
  );
33
33
  }
@@ -12,5 +12,6 @@ export const ReactRoot = lazy(() => import('./react-root'));
12
12
  export const ReactSurface = lazy(() => import('./react-surface'));
13
13
  export const ThreadSettings = lazy(() => import('./settings'));
14
14
  export const ThreadState = lazy(() => import('./state'));
15
+ export const Toolkit = lazy(() => import('./toolkit'));
15
16
 
16
17
  export * from './capabilities';