@opengeni/react 4.3.3-canary.39786771332001 → 4.4.1

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 (244) hide show
  1. package/dist/app/app-state.js +10 -10
  2. package/dist/app/components/Composer.js +3 -3
  3. package/dist/app/components/artifacts/artifact-browse-controls.js +1 -1
  4. package/dist/app/components/artifacts/artifact-library.js +6 -6
  5. package/dist/app/components/artifacts/chat-interactive-block.js +1 -1
  6. package/dist/app/components/artifacts/inline-chat-image.js +5 -5
  7. package/dist/app/components/artifacts/retained-file-preview.js +6 -6
  8. package/dist/app/components/artifacts/site-audience.js +2 -2
  9. package/dist/app/components/artifacts/site-conversations.js +5 -5
  10. package/dist/app/components/artifacts/site-data.js +4 -4
  11. package/dist/app/components/artifacts/site-publishing.js +9 -9
  12. package/dist/app/components/artifacts/site-settings.js +13 -13
  13. package/dist/app/components/capabilities/capability-detail-sheet.js +6 -6
  14. package/dist/app/components/capabilities/capability-logo-source.js +4 -4
  15. package/dist/app/components/capabilities/connector-tool-permissions.js +3 -3
  16. package/dist/app/components/capabilities/native-connect-setup.js +4 -4
  17. package/dist/app/components/capabilities/prepared-mcp-setup-card.js +4 -4
  18. package/dist/app/components/capabilities/session-capability-card.js +45 -29
  19. package/dist/app/components/capabilities/session-capability-card.js.map +1 -1
  20. package/dist/app/components/capabilities/session-chrome-card.css +1023 -0
  21. package/dist/app/components/capabilities/session-chrome-card.css.map +1 -0
  22. package/dist/app/components/capabilities/session-chrome-card.d.ts +18 -0
  23. package/dist/app/components/capabilities/session-chrome-card.js +70 -0
  24. package/dist/app/components/capabilities/session-custom-mcp-card.js +5 -5
  25. package/dist/app/components/capabilities/session-github-card.js +8 -8
  26. package/dist/app/components/capabilities/session-machine-card.d.ts +27 -1
  27. package/dist/app/components/capabilities/session-machine-card.js +10 -8
  28. package/dist/app/components/command-menu/chat-status.js +4 -4
  29. package/dist/app/components/command-menu/chat-switcher.js +5 -5
  30. package/dist/app/components/command-menu/command-menu.js +11 -11
  31. package/dist/app/components/command-menu/shortcut-sheet.js +6 -6
  32. package/dist/app/components/composer-mobile-plus-panel.js +5 -5
  33. package/dist/app/components/composer-mobile-plus.js +1 -1
  34. package/dist/app/components/credit-required-gate.js +1 -1
  35. package/dist/app/components/credit-required-prompt.js +3 -3
  36. package/dist/app/components/desktop/app-shots.js +1 -1
  37. package/dist/app/components/follow-up-repository-menu-body.js +2 -2
  38. package/dist/app/components/follow-up-repository-picker.js +1 -1
  39. package/dist/app/components/github-app-connect-launcher.js +1 -1
  40. package/dist/app/components/markdown.js +3 -3
  41. package/dist/app/components/pickers.js +2 -2
  42. package/dist/app/components/rail/rail-context.js +2 -2
  43. package/dist/app/components/rail/rail-sections.js +22 -22
  44. package/dist/app/components/rail/session-header.js +12 -12
  45. package/dist/app/components/rail/session-list.js +20 -20
  46. package/dist/app/components/rail/session-row-hover-details.js +6 -6
  47. package/dist/app/components/rail/session-shell.js +33 -33
  48. package/dist/app/components/repository-picker.js +2 -2
  49. package/dist/app/components/session/agent-configuration-panel.js +10 -10
  50. package/dist/app/components/session/artifact-session-page.js +3 -3
  51. package/dist/app/components/session/banners.js +5 -5
  52. package/dist/app/components/session/codex-account-indicator.js +1 -1
  53. package/dist/app/components/session/editable-artifacts-workspace.js +7 -7
  54. package/dist/app/components/session/failed-session-banner.js +1 -1
  55. package/dist/app/components/session/human-input.js +13 -13
  56. package/dist/app/components/session/inspector.js +28 -28
  57. package/dist/app/components/session/message-resource-attachments.js +4 -4
  58. package/dist/app/components/session/new-session-settings-menu.js +4 -4
  59. package/dist/app/components/session/sandbox-workspace.js +15 -15
  60. package/dist/app/components/session/search-markdown-highlight.js +4 -4
  61. package/dist/app/components/session/search-results-view.js +5 -5
  62. package/dist/app/components/session/session-admin-access.js +3 -3
  63. package/dist/app/components/session/session-search-dialog.js +5 -5
  64. package/dist/app/components/session/session-tenancy-control.js +8 -8
  65. package/dist/app/components/session/session-wait-status.js +1 -1
  66. package/dist/app/components/session/subagents.js +1 -1
  67. package/dist/app/components/settings/row-select.js +3 -3
  68. package/dist/app/components/transcription-settings.js +4 -4
  69. package/dist/app/components/workspace-composer-plus.js +7 -7
  70. package/dist/app/embed/session-app.js +114 -114
  71. package/dist/app/lib/actions/actions-provider.js +3 -3
  72. package/dist/app/lib/composer-connectors.js +5 -5
  73. package/dist/app/lib/events.js +5 -5
  74. package/dist/app/lib/session-branch-cache.js +1 -1
  75. package/dist/app/lib/session-create.js +2 -2
  76. package/dist/app/lib/session-group-pagination.js +1 -1
  77. package/dist/app/lib/session-hover-facts.js +1 -1
  78. package/dist/app/lib/session-rail.js +1 -1
  79. package/dist/app/lib/sessions-group.js +1 -1
  80. package/dist/app/lib/turn-failure-analytics.js +1 -1
  81. package/dist/app/lib/use-workspace-model-catalog.js +1 -1
  82. package/dist/app/routes/artifacts.js +24 -24
  83. package/dist/app/routes/retained-artifact.js +17 -17
  84. package/dist/app/routes/session-unavailable.js +2 -2
  85. package/dist/app/routes/session.js +63 -63
  86. package/dist/app/routes/sessions-index-hydration.js +3 -3
  87. package/dist/app/routes/sessions-index.js +55 -55
  88. package/dist/{chunk-TFK4KFKA.js → chunk-33TPANCR.js} +4 -4
  89. package/dist/{chunk-DFZMKCVP.js → chunk-4432A3HQ.js} +2 -2
  90. package/dist/{chunk-VRGOOVDB.js → chunk-4EONHNJU.js} +2 -2
  91. package/dist/{chunk-DDIBZA4X.js → chunk-4R3JCVID.js} +48 -48
  92. package/dist/{chunk-EDFJBX3V.js → chunk-4VKA5O7T.js} +6 -6
  93. package/dist/{chunk-Q4NM77K7.js → chunk-4W7VISE7.js} +9 -9
  94. package/dist/{chunk-4246UYVG.js → chunk-5JEIDFPD.js} +1 -1
  95. package/dist/{chunk-VU2EWALC.js → chunk-5MZTJMZ3.js} +3 -2
  96. package/dist/chunk-5MZTJMZ3.js.map +1 -0
  97. package/dist/{chunk-OQAAI33Y.js → chunk-63VGE4OH.js} +2 -2
  98. package/dist/{chunk-HJ5XWW62.js → chunk-6NIB2KGJ.js} +10 -10
  99. package/dist/{chunk-NDETTZQA.js → chunk-6PQNLL4E.js} +9 -9
  100. package/dist/{chunk-XVM566II.js → chunk-72J6C2AJ.js} +1 -1
  101. package/dist/{chunk-VW3BBTMA.js → chunk-7BST7BA6.js} +1 -1
  102. package/dist/{chunk-IC7KNOBB.js → chunk-7CZTKBTI.js} +5 -5
  103. package/dist/{chunk-IGHELZI7.js → chunk-7DU35P5J.js} +2 -2
  104. package/dist/{chunk-V77JK73U.js → chunk-ADJJQSYC.js} +14 -14
  105. package/dist/{chunk-C535KSTY.js → chunk-AOLE4IRZ.js} +2 -2
  106. package/dist/{chunk-Q262Y2YX.js → chunk-APU42QDI.js} +78 -78
  107. package/dist/{chunk-ZHFKMMOT.js → chunk-BSX2R4CW.js} +4 -4
  108. package/dist/{chunk-ETCB6SUJ.js → chunk-BXRIQZAH.js} +2 -2
  109. package/dist/{chunk-O77Q4NG2.js → chunk-CVG4FILQ.js} +21 -21
  110. package/dist/{chunk-CKG2EG62.js → chunk-DDK4WFJ3.js} +9 -9
  111. package/dist/{chunk-WSLPZ43N.js → chunk-DLOYMWUQ.js} +7 -7
  112. package/dist/{chunk-OTMWHZBY.js → chunk-DWPOQ3S5.js} +2 -2
  113. package/dist/{chunk-VSQHDBHU.js → chunk-EF5SYXAY.js} +41 -16
  114. package/dist/chunk-EF5SYXAY.js.map +1 -0
  115. package/dist/{chunk-6VQTHC4W.js → chunk-F6LBGYS6.js} +9 -9
  116. package/dist/{chunk-X2AH4DRF.js → chunk-GALB2G3W.js} +2 -2
  117. package/dist/{chunk-Y7WMW74G.js → chunk-HNYJEAES.js} +2 -2
  118. package/dist/{chunk-FUSFVUIJ.js → chunk-HO5Y2OOX.js} +5 -5
  119. package/dist/{chunk-MNWQAYY3.js → chunk-HWDGJFM4.js} +3 -3
  120. package/dist/{chunk-VMFID36H.js → chunk-JK7C5A4I.js} +2 -2
  121. package/dist/{chunk-Q4OHNGGB.js → chunk-JRXHSVD5.js} +4 -4
  122. package/dist/{chunk-D3RK3NGM.js → chunk-JTN3T4QN.js} +4 -4
  123. package/dist/{chunk-KNFDWNCH.js → chunk-JXT5QDJC.js} +5 -5
  124. package/dist/{chunk-DKCX643S.js → chunk-KWKGBGAF.js} +11 -11
  125. package/dist/{chunk-RKILMLA7.js → chunk-L7YURQI2.js} +2 -2
  126. package/dist/{chunk-WUHB422G.js → chunk-LE2EDBXU.js} +5 -5
  127. package/dist/{chunk-45WYYFP6.js → chunk-N3ZP4IDU.js} +2 -2
  128. package/dist/{chunk-N7JKT4ZU.js → chunk-NCX5SAQY.js} +4 -4
  129. package/dist/{chunk-MM7OKOTE.js → chunk-NWZW2WHN.js} +6 -6
  130. package/dist/{chunk-KYBALSWW.js → chunk-O756O2I7.js} +1 -1
  131. package/dist/{chunk-V5RJAWRP.js → chunk-PN66T6JA.js} +2 -2
  132. package/dist/{chunk-QIYBOFUV.js → chunk-Q35DT6I6.js} +88 -88
  133. package/dist/{chunk-QILLL4JE.js → chunk-QCD5Y7K5.js} +2 -2
  134. package/dist/{chunk-LQKKSUCR.js → chunk-R63QEVPK.js} +4 -4
  135. package/dist/{chunk-O4DYOSOL.js → chunk-RA3FNMWF.js} +5 -5
  136. package/dist/{chunk-324X6T6G.js → chunk-RGW24ZZM.js} +19 -19
  137. package/dist/{chunk-GCGTYXJJ.js → chunk-RQDFYQHA.js} +2 -2
  138. package/dist/{chunk-H5WGWAYX.js → chunk-RSNYGEVZ.js} +3 -3
  139. package/dist/{chunk-YXIW7H4N.js → chunk-S4EZ3JUB.js} +23 -23
  140. package/dist/chunk-ST22GONV.js +204 -0
  141. package/dist/chunk-ST22GONV.js.map +1 -0
  142. package/dist/{chunk-NEVRLD77.js → chunk-SUVEDKE2.js} +3 -3
  143. package/dist/{chunk-GIZGNFIX.js → chunk-TGVVEGZG.js} +4 -4
  144. package/dist/{chunk-G3S2F35T.js → chunk-U36PO27Q.js} +3 -3
  145. package/dist/{chunk-DSGMYES6.js → chunk-U3HSFCON.js} +3 -3
  146. package/dist/{chunk-PFKTGZKE.js → chunk-UP4BE32Y.js} +4 -4
  147. package/dist/{chunk-H6WWEKEV.js → chunk-VP62LPLW.js} +3 -3
  148. package/dist/{chunk-E5FCEDMU.js → chunk-X7ASQHR6.js} +2 -2
  149. package/dist/{chunk-CKZW7A5S.js → chunk-X7L4H4VG.js} +3 -3
  150. package/dist/{chunk-TZD3U2S5.js → chunk-XOFUXMPP.js} +4 -4
  151. package/dist/{chunk-YTZQV2DD.js → chunk-YXOKAG66.js} +2 -2
  152. package/dist/{chunk-QECZVL7S.js → chunk-ZRJCGBVV.js} +2 -2
  153. package/dist/composer.js +1 -1
  154. package/dist/connect.js +3 -3
  155. package/dist/{embedded-new-voice-5PYRDZ6T.js → embedded-new-voice-LFHWWYD2.js} +3 -3
  156. package/dist/{embedded-voice-PABC5NBD.js → embedded-voice-6QS4ANNX.js} +3 -3
  157. package/dist/embedded-voice-6QS4ANNX.js.map +1 -0
  158. package/dist/index.js +22 -22
  159. package/dist/{latest-question-5LIRVG4G.js → latest-question-AW3PPDUM.js} +2 -2
  160. package/dist/realtime.js +2 -2
  161. package/dist/session-app.js +114 -114
  162. package/dist/session-ui.js +15 -15
  163. package/dist/session.js +6 -6
  164. package/dist/timeline/types.d.ts +2 -0
  165. package/dist/timeline-model.js +3 -3
  166. package/dist/ui/access-list.js +7 -7
  167. package/dist/ui/cadence-picker.js +2 -2
  168. package/dist/ui/list-row.js +3 -3
  169. package/dist/ui/revision-history.js +2 -2
  170. package/dist/ui/role-select.js +3 -3
  171. package/dist/ui/select-menu.js +2 -2
  172. package/package.json +4 -4
  173. package/src/app/components/capabilities/session-capability-card.tsx +14 -1
  174. package/src/app/components/capabilities/session-chrome-card.dom-fixture.tsx +377 -0
  175. package/src/app/components/capabilities/session-chrome-card.test.tsx +18 -0
  176. package/src/app/components/capabilities/session-chrome-card.tsx +243 -0
  177. package/src/app/components/capabilities/session-machine-card.dom-fixture.tsx +2 -0
  178. package/src/app/components/capabilities/session-machine-card.tsx +65 -14
  179. package/src/timeline/projection.ts +4 -2
  180. package/src/timeline/types.ts +2 -0
  181. package/dist/chunk-VSQHDBHU.js.map +0 -1
  182. package/dist/chunk-VU2EWALC.js.map +0 -1
  183. /package/dist/{embedded-new-voice-5PYRDZ6T.js.map → app/components/capabilities/session-chrome-card.js.map} +0 -0
  184. /package/dist/{chunk-TFK4KFKA.js.map → chunk-33TPANCR.js.map} +0 -0
  185. /package/dist/{chunk-DFZMKCVP.js.map → chunk-4432A3HQ.js.map} +0 -0
  186. /package/dist/{chunk-VRGOOVDB.js.map → chunk-4EONHNJU.js.map} +0 -0
  187. /package/dist/{chunk-DDIBZA4X.js.map → chunk-4R3JCVID.js.map} +0 -0
  188. /package/dist/{chunk-EDFJBX3V.js.map → chunk-4VKA5O7T.js.map} +0 -0
  189. /package/dist/{chunk-Q4NM77K7.js.map → chunk-4W7VISE7.js.map} +0 -0
  190. /package/dist/{chunk-4246UYVG.js.map → chunk-5JEIDFPD.js.map} +0 -0
  191. /package/dist/{chunk-OQAAI33Y.js.map → chunk-63VGE4OH.js.map} +0 -0
  192. /package/dist/{chunk-HJ5XWW62.js.map → chunk-6NIB2KGJ.js.map} +0 -0
  193. /package/dist/{chunk-NDETTZQA.js.map → chunk-6PQNLL4E.js.map} +0 -0
  194. /package/dist/{chunk-XVM566II.js.map → chunk-72J6C2AJ.js.map} +0 -0
  195. /package/dist/{chunk-VW3BBTMA.js.map → chunk-7BST7BA6.js.map} +0 -0
  196. /package/dist/{chunk-IC7KNOBB.js.map → chunk-7CZTKBTI.js.map} +0 -0
  197. /package/dist/{chunk-IGHELZI7.js.map → chunk-7DU35P5J.js.map} +0 -0
  198. /package/dist/{chunk-V77JK73U.js.map → chunk-ADJJQSYC.js.map} +0 -0
  199. /package/dist/{chunk-C535KSTY.js.map → chunk-AOLE4IRZ.js.map} +0 -0
  200. /package/dist/{chunk-Q262Y2YX.js.map → chunk-APU42QDI.js.map} +0 -0
  201. /package/dist/{chunk-ZHFKMMOT.js.map → chunk-BSX2R4CW.js.map} +0 -0
  202. /package/dist/{chunk-ETCB6SUJ.js.map → chunk-BXRIQZAH.js.map} +0 -0
  203. /package/dist/{chunk-O77Q4NG2.js.map → chunk-CVG4FILQ.js.map} +0 -0
  204. /package/dist/{chunk-CKG2EG62.js.map → chunk-DDK4WFJ3.js.map} +0 -0
  205. /package/dist/{chunk-WSLPZ43N.js.map → chunk-DLOYMWUQ.js.map} +0 -0
  206. /package/dist/{chunk-OTMWHZBY.js.map → chunk-DWPOQ3S5.js.map} +0 -0
  207. /package/dist/{chunk-6VQTHC4W.js.map → chunk-F6LBGYS6.js.map} +0 -0
  208. /package/dist/{chunk-X2AH4DRF.js.map → chunk-GALB2G3W.js.map} +0 -0
  209. /package/dist/{chunk-Y7WMW74G.js.map → chunk-HNYJEAES.js.map} +0 -0
  210. /package/dist/{chunk-FUSFVUIJ.js.map → chunk-HO5Y2OOX.js.map} +0 -0
  211. /package/dist/{chunk-MNWQAYY3.js.map → chunk-HWDGJFM4.js.map} +0 -0
  212. /package/dist/{chunk-VMFID36H.js.map → chunk-JK7C5A4I.js.map} +0 -0
  213. /package/dist/{chunk-Q4OHNGGB.js.map → chunk-JRXHSVD5.js.map} +0 -0
  214. /package/dist/{chunk-D3RK3NGM.js.map → chunk-JTN3T4QN.js.map} +0 -0
  215. /package/dist/{chunk-KNFDWNCH.js.map → chunk-JXT5QDJC.js.map} +0 -0
  216. /package/dist/{chunk-DKCX643S.js.map → chunk-KWKGBGAF.js.map} +0 -0
  217. /package/dist/{chunk-RKILMLA7.js.map → chunk-L7YURQI2.js.map} +0 -0
  218. /package/dist/{chunk-WUHB422G.js.map → chunk-LE2EDBXU.js.map} +0 -0
  219. /package/dist/{chunk-45WYYFP6.js.map → chunk-N3ZP4IDU.js.map} +0 -0
  220. /package/dist/{chunk-N7JKT4ZU.js.map → chunk-NCX5SAQY.js.map} +0 -0
  221. /package/dist/{chunk-MM7OKOTE.js.map → chunk-NWZW2WHN.js.map} +0 -0
  222. /package/dist/{chunk-KYBALSWW.js.map → chunk-O756O2I7.js.map} +0 -0
  223. /package/dist/{chunk-V5RJAWRP.js.map → chunk-PN66T6JA.js.map} +0 -0
  224. /package/dist/{chunk-QIYBOFUV.js.map → chunk-Q35DT6I6.js.map} +0 -0
  225. /package/dist/{chunk-QILLL4JE.js.map → chunk-QCD5Y7K5.js.map} +0 -0
  226. /package/dist/{chunk-LQKKSUCR.js.map → chunk-R63QEVPK.js.map} +0 -0
  227. /package/dist/{chunk-O4DYOSOL.js.map → chunk-RA3FNMWF.js.map} +0 -0
  228. /package/dist/{chunk-324X6T6G.js.map → chunk-RGW24ZZM.js.map} +0 -0
  229. /package/dist/{chunk-GCGTYXJJ.js.map → chunk-RQDFYQHA.js.map} +0 -0
  230. /package/dist/{chunk-H5WGWAYX.js.map → chunk-RSNYGEVZ.js.map} +0 -0
  231. /package/dist/{chunk-YXIW7H4N.js.map → chunk-S4EZ3JUB.js.map} +0 -0
  232. /package/dist/{chunk-NEVRLD77.js.map → chunk-SUVEDKE2.js.map} +0 -0
  233. /package/dist/{chunk-GIZGNFIX.js.map → chunk-TGVVEGZG.js.map} +0 -0
  234. /package/dist/{chunk-G3S2F35T.js.map → chunk-U36PO27Q.js.map} +0 -0
  235. /package/dist/{chunk-DSGMYES6.js.map → chunk-U3HSFCON.js.map} +0 -0
  236. /package/dist/{chunk-PFKTGZKE.js.map → chunk-UP4BE32Y.js.map} +0 -0
  237. /package/dist/{chunk-H6WWEKEV.js.map → chunk-VP62LPLW.js.map} +0 -0
  238. /package/dist/{chunk-E5FCEDMU.js.map → chunk-X7ASQHR6.js.map} +0 -0
  239. /package/dist/{chunk-CKZW7A5S.js.map → chunk-X7L4H4VG.js.map} +0 -0
  240. /package/dist/{chunk-TZD3U2S5.js.map → chunk-XOFUXMPP.js.map} +0 -0
  241. /package/dist/{chunk-YTZQV2DD.js.map → chunk-YXOKAG66.js.map} +0 -0
  242. /package/dist/{chunk-QECZVL7S.js.map → chunk-ZRJCGBVV.js.map} +0 -0
  243. /package/dist/{embedded-voice-PABC5NBD.js.map → embedded-new-voice-LFHWWYD2.js.map} +0 -0
  244. /package/dist/{latest-question-5LIRVG4G.js.map → latest-question-AW3PPDUM.js.map} +0 -0
package/dist/session.js CHANGED
@@ -28,9 +28,12 @@ import {
28
28
  useSessionEvents,
29
29
  useSessionReadAcknowledgement,
30
30
  useTurnQueue
31
- } from "./chunk-Q4NM77K7.js";
32
- import "./chunk-EMIVOS73.js";
31
+ } from "./chunk-4W7VISE7.js";
33
32
  import "./chunk-TWXIFWBD.js";
33
+ import "./chunk-EMIVOS73.js";
34
+ import {
35
+ useFileAttachments
36
+ } from "./chunk-I6FIXEL4.js";
34
37
  import {
35
38
  admissionControlNeedsRefresh,
36
39
  admissionRecheckControl,
@@ -54,10 +57,7 @@ import {
54
57
  extractSessionRef,
55
58
  groupTimeline,
56
59
  sessionStatusFromEvents
57
- } from "./chunk-VU2EWALC.js";
58
- import {
59
- useFileAttachments
60
- } from "./chunk-I6FIXEL4.js";
60
+ } from "./chunk-5MZTJMZ3.js";
61
61
  import "./chunk-ZOARMQY2.js";
62
62
  import {
63
63
  useRetainedReads
@@ -493,6 +493,8 @@ export type AuthNeededItem = {
493
493
  setupRequest?: ToolAuthNeededPayload["setupRequest"] | null | undefined;
494
494
  /** The Connected Machine the agent asked for, when the card names one. */
495
495
  requestedSandboxId?: string | null | undefined;
496
+ /** `chrome`: the Connected Machine card is for the person's own Chrome. */
497
+ purpose?: "chrome" | undefined;
496
498
  /**
497
499
  * A later Connected Machine card in this chat asks for the same thing, so
498
500
  * this one is shown as a short note instead of a second full card.
@@ -41,6 +41,9 @@ import {
41
41
  timelineGroupIndexAtSequence,
42
42
  timelineGroupSequences
43
43
  } from "./chunk-I6VUUEXH.js";
44
+ import {
45
+ annotationNoteNeedsDisclosure
46
+ } from "./chunk-LJUMYUBO.js";
44
47
  import {
45
48
  BUILT_IN_TURN_SUMMARY_FACETS,
46
49
  BUILT_IN_TURN_SUMMARY_FACET_IDS,
@@ -87,9 +90,6 @@ import {
87
90
  workerRowTitle,
88
91
  workerRowTitleParts
89
92
  } from "./chunk-HEVQKE27.js";
90
- import {
91
- annotationNoteNeedsDisclosure
92
- } from "./chunk-LJUMYUBO.js";
93
93
  import {
94
94
  timelineGroupContainsPresentedImage
95
95
  } from "./chunk-ANHUMDJX.js";
@@ -9,10 +9,10 @@ import {
9
9
  import {
10
10
  RoleSelect,
11
11
  roleLabel
12
- } from "../chunk-IC7KNOBB.js";
12
+ } from "../chunk-7CZTKBTI.js";
13
13
  import {
14
14
  InlineDisabledReason
15
- } from "../chunk-TFK4KFKA.js";
15
+ } from "../chunk-33TPANCR.js";
16
16
  import {
17
17
  StatusBadge
18
18
  } from "../chunk-EPCPGUIF.js";
@@ -26,11 +26,6 @@ import {
26
26
  import "../chunk-ZMBJPN4T.js";
27
27
  import "../chunk-X6WZOHBI.js";
28
28
  import "../chunk-L4OF77LE.js";
29
- import "../chunk-XNTW77T6.js";
30
- import {
31
- Skeleton
32
- } from "../chunk-HY7EYWIG.js";
33
- import "../chunk-KS57MLGE.js";
34
29
  import {
35
30
  DropdownMenu,
36
31
  DropdownMenuContent,
@@ -39,6 +34,11 @@ import {
39
34
  DropdownMenuTrigger
40
35
  } from "../chunk-TREHTIBD.js";
41
36
  import "../chunk-5OTQA7CM.js";
37
+ import "../chunk-XNTW77T6.js";
38
+ import {
39
+ Skeleton
40
+ } from "../chunk-HY7EYWIG.js";
41
+ import "../chunk-KS57MLGE.js";
42
42
  import "../chunk-WLEDGIVS.js";
43
43
  import {
44
44
  Button
@@ -2,12 +2,12 @@
2
2
  import {
3
3
  InlineDisabledReason,
4
4
  SelectMenuPanel
5
- } from "../chunk-TFK4KFKA.js";
6
- import "../chunk-XNTW77T6.js";
5
+ } from "../chunk-33TPANCR.js";
7
6
  import {
8
7
  MENU_CHECK_CLASS,
9
8
  MENU_SURFACE_CLASS
10
9
  } from "../chunk-5OTQA7CM.js";
10
+ import "../chunk-XNTW77T6.js";
11
11
  import {
12
12
  containedPortal
13
13
  } from "../chunk-J3BHDXQQ.js";
@@ -5,14 +5,14 @@ import {
5
5
  RowIndicator,
6
6
  RowList,
7
7
  useRowListVariant
8
- } from "../chunk-MM7OKOTE.js";
8
+ } from "../chunk-NWZW2WHN.js";
9
9
  import "../chunk-FQQEZUSN.js";
10
10
  import "../chunk-ZMBJPN4T.js";
11
+ import "../chunk-TREHTIBD.js";
12
+ import "../chunk-5OTQA7CM.js";
11
13
  import "../chunk-HY7EYWIG.js";
12
14
  import "../chunk-XG57V2BN.js";
13
15
  import "../chunk-KS57MLGE.js";
14
- import "../chunk-TREHTIBD.js";
15
- import "../chunk-5OTQA7CM.js";
16
16
  import "../chunk-J3BHDXQQ.js";
17
17
  import "../chunk-TXS65BB2.js";
18
18
  export {
@@ -5,7 +5,7 @@ import {
5
5
  } from "../chunk-3XOISKZC.js";
6
6
  import {
7
7
  InlineDisabledReason
8
- } from "../chunk-TFK4KFKA.js";
8
+ } from "../chunk-33TPANCR.js";
9
9
  import {
10
10
  MetaChip
11
11
  } from "../chunk-SS3N75VI.js";
@@ -15,6 +15,7 @@ import {
15
15
  import "../chunk-ZMBJPN4T.js";
16
16
  import "../chunk-X6WZOHBI.js";
17
17
  import "../chunk-L4OF77LE.js";
18
+ import "../chunk-5OTQA7CM.js";
18
19
  import "../chunk-XNTW77T6.js";
19
20
  import {
20
21
  Skeleton
@@ -25,7 +26,6 @@ import {
25
26
  import {
26
27
  DisabledReason
27
28
  } from "../chunk-KS57MLGE.js";
28
- import "../chunk-5OTQA7CM.js";
29
29
  import {
30
30
  Button
31
31
  } from "../chunk-HMU5IELG.js";
@@ -4,16 +4,16 @@ import {
4
4
  RoleSelect,
5
5
  needsEscalationConfirm,
6
6
  roleLabel
7
- } from "../chunk-IC7KNOBB.js";
8
- import "../chunk-TFK4KFKA.js";
7
+ } from "../chunk-7CZTKBTI.js";
8
+ import "../chunk-33TPANCR.js";
9
9
  import "../chunk-S7PVL7XH.js";
10
10
  import "../chunk-WHWUWN2X.js";
11
11
  import "../chunk-ZMBJPN4T.js";
12
12
  import "../chunk-L4OF77LE.js";
13
+ import "../chunk-5OTQA7CM.js";
13
14
  import "../chunk-XNTW77T6.js";
14
15
  import "../chunk-HY7EYWIG.js";
15
16
  import "../chunk-KS57MLGE.js";
16
- import "../chunk-5OTQA7CM.js";
17
17
  import "../chunk-WLEDGIVS.js";
18
18
  import "../chunk-HMU5IELG.js";
19
19
  import "../chunk-J3BHDXQQ.js";
@@ -12,9 +12,9 @@ import {
12
12
  nativeOptionText,
13
13
  nextEnabledIndex,
14
14
  typeaheadIndex
15
- } from "../chunk-TFK4KFKA.js";
16
- import "../chunk-XNTW77T6.js";
15
+ } from "../chunk-33TPANCR.js";
17
16
  import "../chunk-5OTQA7CM.js";
17
+ import "../chunk-XNTW77T6.js";
18
18
  import "../chunk-J3BHDXQQ.js";
19
19
  import "../chunk-TXS65BB2.js";
20
20
  export {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeni/react",
3
- "version": "4.3.3-canary.39786771332001",
3
+ "version": "4.4.1",
4
4
  "description": "React hooks and styled components for Opengeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -251,9 +251,9 @@
251
251
  "@dnd-kit/utilities": "3.2.2",
252
252
  "@fontsource-variable/noto-sans-arabic": "5.2.10",
253
253
  "@fontsource-variable/noto-sans-jp": "5.2.10",
254
- "@opengeni/connect": "4.3.3-canary.39786771332001",
255
- "@opengeni/contracts": "4.3.3-canary.39786771332001",
256
- "@opengeni/sdk": "4.3.3-canary.39786771332001",
254
+ "@opengeni/connect": "4.4.1",
255
+ "@opengeni/contracts": "4.4.1",
256
+ "@opengeni/sdk": "4.4.1",
257
257
  "@tanstack/react-virtual": "^3.14.11",
258
258
  "class-variance-authority": "^0.7.1",
259
259
  "clsx": "^2.1.1",
@@ -30,6 +30,7 @@ import type { CapabilityCatalogItem, ResourceRef } from "../../types";
30
30
  import type { ChatSendContext } from "./session-github-repositories";
31
31
  import { SessionGitHubCapabilityCard } from "./session-github-card";
32
32
  import { RepeatedMachineRequestNote, SessionMachineCapabilityCard } from "./session-machine-card";
33
+ import { SessionChromeCapabilityCard } from "./session-chrome-card";
33
34
  import { usePage } from "../../lib/navigation";
34
35
 
35
36
  /** The built-in card for connecting a person's own computer to this chat. */
@@ -90,7 +91,19 @@ export function SessionCapabilityCard(props: SessionCapabilityCardProps) {
90
91
  const capability = item.capability;
91
92
  if (capability?.id === CONNECTED_MACHINE_CAPABILITY_ID) {
92
93
  // The same request was made again further down, where its card is live.
93
- if (item.askedAgainLater) return <RepeatedMachineRequestNote />;
94
+ if (item.askedAgainLater)
95
+ return <RepeatedMachineRequestNote chrome={item.purpose === "chrome"} />;
96
+ if (item.purpose === "chrome") {
97
+ return (
98
+ <SessionChromeCapabilityCard
99
+ key={`${props.workspaceId}:${props.sessionId}`}
100
+ item={item}
101
+ workspaceId={props.workspaceId}
102
+ sessionId={props.sessionId}
103
+ sendContext={props.sendContext}
104
+ />
105
+ );
106
+ }
94
107
  return (
95
108
  <SessionMachineCapabilityCard
96
109
  key={`${props.workspaceId}:${props.sessionId}`}
@@ -0,0 +1,377 @@
1
+ // Run explicitly through session-chrome-card.test.tsx so Radix sees the DOM at import time.
2
+ import { afterAll, beforeAll, beforeEach, describe, expect, mock, test } from "bun:test";
3
+ import { GlobalRegistrator } from "@happy-dom/global-registrator";
4
+ import { act } from "react";
5
+
6
+ import type { AuthNeededItem } from "../../../timeline";
7
+
8
+ const STORE_URL =
9
+ "https://chromewebstore.google.com/detail/opengeni-browser/phpmmcbeelfkcinjfbbggegjdcdmnnch";
10
+
11
+ type Machine = {
12
+ sandboxId: string;
13
+ enrollmentId: string | null;
14
+ name: string;
15
+ kind: string;
16
+ state: string;
17
+ active: boolean;
18
+ isSessionGroup: boolean;
19
+ os: string;
20
+ hasDisplay: boolean;
21
+ allowScreenControl: boolean;
22
+ desktopUnavailableReason: string | null;
23
+ runtime: null;
24
+ scope: "user" | "workspace" | "organization";
25
+ };
26
+ const machine = (sandboxId: string, name: string, overrides: Partial<Machine> = {}): Machine => ({
27
+ sandboxId,
28
+ enrollmentId: `enrollment-${sandboxId}`,
29
+ name,
30
+ kind: "selfhosted",
31
+ state: "online",
32
+ active: false,
33
+ isSessionGroup: false,
34
+ os: "macos",
35
+ hasDisplay: true,
36
+ allowScreenControl: false,
37
+ desktopUnavailableReason: null,
38
+ runtime: null,
39
+ scope: "user",
40
+ ...overrides,
41
+ });
42
+ const sessionBox = machine("home", "Cloud sandbox", {
43
+ enrollmentId: null,
44
+ isSessionGroup: true,
45
+ kind: "modal",
46
+ active: true,
47
+ });
48
+
49
+ const fleet = {
50
+ machines: [sessionBox] as Machine[],
51
+ canRead: true,
52
+ canManage: true,
53
+ canAttach: true,
54
+ attaching: false,
55
+ loading: false,
56
+ error: null as unknown,
57
+ mutationError: null as unknown,
58
+ attach: mock(async (_sandboxId: string) => true),
59
+ refresh: mock(async () => {}),
60
+ };
61
+ mock.module("../../lib/use-workspace-machines", () => ({ useWorkspaceMachines: () => fleet }));
62
+ mock.module("../../lib/app-api", () => ({
63
+ useAppApi: () => ({ apiBaseUrl: "https://app.example.test" }),
64
+ }));
65
+ mock.module("../desktop/this-computer", () => ({ useDesktopThisComputer: () => null }));
66
+ mock.module("../../../clipboard", () => ({ copyTextToClipboard: mock(async () => true) }));
67
+
68
+ const mintEnrollToken = mock(
69
+ async (_workspaceId: string, input: { scope?: string; allowScreenControl?: boolean }) => ({
70
+ token: "oget_token.sig",
71
+ expiresAt: new Date(Date.now() + 3_600_000).toISOString(),
72
+ expiresInSeconds: 3600,
73
+ scope: input.scope ?? "workspace",
74
+ allowScreenControl: input.allowScreenControl ?? false,
75
+ }),
76
+ );
77
+ let browsers: { devices: unknown[]; bridges: unknown[]; revision: number } = {
78
+ devices: [],
79
+ bridges: [],
80
+ revision: 1,
81
+ };
82
+ const listAttachedBrowsers = mock(async () => browsers);
83
+ const sendMessage = mock(async (_workspaceId: string, _sessionId: string, _input: unknown) => ({
84
+ id: "accepted",
85
+ type: "user.message",
86
+ payload: { routing: "accepted_for_execution" },
87
+ }));
88
+ const everyone = ["enrollments:read", "enrollments:manage", "sessions:control"];
89
+ const context = {
90
+ client: { mintEnrollToken, listAttachedBrowsers, sendMessage },
91
+ accessContext: {
92
+ subjectId: "member",
93
+ workspaceGrants: [{ workspaceId: "workspace", permissions: everyone }],
94
+ },
95
+ workspaces: [{ id: "workspace", kind: "shared" }],
96
+ workspaceCapabilityCatalog: [],
97
+ };
98
+ mock.module("../../app-context", () => ({ useAppContext: () => context }));
99
+ mock.module("sonner", () => ({ toast: { success: () => {}, error: () => {}, info: () => {} } }));
100
+ GlobalRegistrator.register();
101
+ const opened: unknown[][] = [];
102
+ window.open = ((...args: unknown[]) => {
103
+ opened.push(args);
104
+ return null;
105
+ }) as typeof window.open;
106
+ const { createRoot } = await import("react-dom/client");
107
+ const { SessionCapabilityCard } = await import("./session-capability-card");
108
+ const { OpenGeniApiError } = await import("@opengeni/sdk");
109
+
110
+ const chromeNotice = {
111
+ id: "chrome-notice",
112
+ kind: "auth-needed",
113
+ serverId: "opengeni",
114
+ providerDomain: "opengeni.ai",
115
+ reason: "missing_connection",
116
+ toolName: "browser_open",
117
+ purpose: "chrome",
118
+ capability: {
119
+ id: "api:connected-machine",
120
+ name: "Connected Machine",
121
+ kind: "api",
122
+ action: "connect",
123
+ rationale: "Connect your Chrome so the agent can use it.",
124
+ requiredVariables: [],
125
+ },
126
+ } as unknown as AuthNeededItem;
127
+
128
+ const chromeProfile = (name: string, enrollmentId = "enrollment-mac") => ({
129
+ id: `device-${name}`,
130
+ enrollmentId,
131
+ name,
132
+ profileLabel: name,
133
+ browserName: "Chrome",
134
+ state: "connected",
135
+ capabilities: { debuggerAttachment: true },
136
+ });
137
+
138
+ beforeAll(() => {
139
+ (globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean | undefined }).IS_REACT_ACT_ENVIRONMENT =
140
+ true;
141
+ });
142
+ afterAll(() => {
143
+ mock.restore();
144
+ GlobalRegistrator.unregister();
145
+ });
146
+ beforeEach(() => {
147
+ opened.length = 0;
148
+ sendMessage.mockClear();
149
+ mintEnrollToken.mockClear();
150
+ listAttachedBrowsers.mockClear();
151
+ browsers = { devices: [], bridges: [], revision: 1 };
152
+ fleet.machines = [sessionBox];
153
+ fleet.canRead = true;
154
+ fleet.canManage = true;
155
+ fleet.error = null;
156
+ context.accessContext.workspaceGrants = [{ workspaceId: "workspace", permissions: everyone }];
157
+ });
158
+
159
+ type SendContext = {
160
+ blocked: string | null;
161
+ awaitingHuman: boolean;
162
+ extras: Record<string, unknown>;
163
+ };
164
+ async function render(item: AuthNeededItem = chromeNotice, sendContext?: () => SendContext) {
165
+ const container = document.createElement("div");
166
+ document.body.append(container);
167
+ const root = createRoot(container);
168
+ const paint = async () =>
169
+ act(async () => {
170
+ root.render(
171
+ <SessionCapabilityCard
172
+ item={item}
173
+ workspaceId="workspace"
174
+ sessionId="session"
175
+ sendContext={sendContext as never}
176
+ />,
177
+ );
178
+ await new Promise((resolve) => setTimeout(resolve, 0));
179
+ });
180
+ await paint();
181
+ const dialog = () => document.querySelector<HTMLElement>("[data-connect-machine-dialog]");
182
+ return {
183
+ text: () => container.textContent ?? "",
184
+ note: () => container.querySelector(".og-session-capability-note")?.textContent ?? "",
185
+ button: (name: string) =>
186
+ [...container.querySelectorAll<HTMLButtonElement>("button")].find(
187
+ (button) => button.textContent?.trim() === name,
188
+ ) ?? null,
189
+ dialog,
190
+ /** Render again after the mocked machines or browsers changed. */
191
+ rerender: async () => {
192
+ await paint();
193
+ await act(async () => new Promise((resolve) => setTimeout(resolve, 0)));
194
+ },
195
+ command: () => dialog()?.querySelector("[data-connect-command]")?.textContent ?? "",
196
+ close: async () => {
197
+ await act(async () => root.unmount());
198
+ container.remove();
199
+ },
200
+ };
201
+ }
202
+
203
+ async function click(element: HTMLElement | null) {
204
+ if (!element) throw new Error("Missing element");
205
+ await act(async () => {
206
+ element.click();
207
+ await new Promise((resolve) => setTimeout(resolve, 0));
208
+ });
209
+ }
210
+
211
+ describe("Chrome setup card", () => {
212
+ test("without a computer, its one action connects a computer", async () => {
213
+ const view = await render();
214
+ expect(view.text()).toContain("Chrome");
215
+ expect(view.text()).toContain(
216
+ "Connect your Chrome so the agent can use it. First connect a computer, then add the extension.",
217
+ );
218
+ // It is about Chrome, not about where this chat runs.
219
+ expect(view.text()).not.toContain("Choose where this chat runs");
220
+ expect(view.text()).not.toContain("Use in this chat");
221
+ expect(view.button("Add to Chrome")).toBeNull();
222
+ await click(view.button("Connect a computer"));
223
+ expect(view.dialog()).not.toBeNull();
224
+ // The shared connect dialog, opened on its first step.
225
+ expect(view.dialog()?.textContent).toContain("Open app and connect");
226
+ await view.close();
227
+ });
228
+
229
+ test("with a computer online, its one action adds the extension and then waits for Chrome", async () => {
230
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
231
+ const view = await render();
232
+ expect(view.text()).toContain("Add the extension to Chrome on Ada's MacBook.");
233
+ expect(view.button("Connect a computer")).toBeNull();
234
+ await click(view.button("Add to Chrome"));
235
+ expect(opened).toEqual([[STORE_URL, "_blank", "noopener,noreferrer"]]);
236
+ expect(view.note()).toBe("Waiting for Chrome to connect…");
237
+ await view.close();
238
+ });
239
+
240
+ test("an offline computer or one without a screen still needs a computer connected", async () => {
241
+ fleet.machines = [
242
+ sessionBox,
243
+ machine("mac", "Ada's MacBook", { state: "offline" }),
244
+ machine("box", "Build server", { os: "linux", hasDisplay: false }),
245
+ ];
246
+ const view = await render();
247
+ expect(view.button("Connect a computer")).not.toBeNull();
248
+ expect(view.button("Add to Chrome")).toBeNull();
249
+ await view.close();
250
+ });
251
+
252
+ test("once Chrome is connected, Continue tells the agent to go on", async () => {
253
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
254
+ const view = await render(chromeNotice, () => ({
255
+ blocked: null,
256
+ awaitingHuman: false,
257
+ extras: { model: "codex/test" },
258
+ }));
259
+ expect(view.button("Continue")).toBeNull();
260
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
261
+ await click(view.button("Add to Chrome"));
262
+ expect(view.text()).toContain("Chrome is connected (Work).");
263
+ await click(view.button("Continue"));
264
+ expect(sendMessage).toHaveBeenCalledTimes(1);
265
+ const [workspaceId, sessionId, input] = sendMessage.mock.calls[0]!;
266
+ expect([workspaceId, sessionId]).toEqual(["workspace", "session"]);
267
+ expect(input).toMatchObject({ text: "Chrome is connected. Continue.", model: "codex/test" });
268
+ expect(view.text()).toContain("The agent continues.");
269
+ expect(view.button("Continue")).toBeNull();
270
+ await view.close();
271
+ });
272
+
273
+ test("Continue waits for an open question and follows the composer's block", async () => {
274
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
275
+ const waiting = await render(chromeNotice, () => ({
276
+ blocked: null,
277
+ awaitingHuman: true,
278
+ extras: {},
279
+ }));
280
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
281
+ await click(waiting.button("Add to Chrome"));
282
+ expect(waiting.text()).toContain("Chrome is connected (Work).");
283
+ expect(waiting.button("Continue")).toBeNull();
284
+ await waiting.close();
285
+
286
+ browsers = { devices: [], bridges: [], revision: 1 };
287
+ const blocked = await render(chromeNotice, () => ({
288
+ blocked: "Choose a model first.",
289
+ awaitingHuman: false,
290
+ extras: {},
291
+ }));
292
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
293
+ await click(blocked.button("Add to Chrome"));
294
+ await click(blocked.button("Continue"));
295
+ expect(sendMessage).not.toHaveBeenCalled();
296
+ expect(blocked.text()).toContain("Choose a model first.");
297
+ await blocked.close();
298
+ });
299
+
300
+ test("a Chrome profile the agent cannot drive does not count as connected", async () => {
301
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
302
+ browsers = {
303
+ devices: [{ ...chromeProfile("Work"), capabilities: { debuggerAttachment: false } }],
304
+ bridges: [],
305
+ revision: 2,
306
+ };
307
+ const view = await render();
308
+ expect(view.text()).not.toContain("Chrome is connected");
309
+ expect(view.button("Add to Chrome")).not.toBeNull();
310
+ await view.close();
311
+ });
312
+
313
+ test("when a computer comes online with Chrome already linked, Continue appears without Add to Chrome", async () => {
314
+ const view = await render();
315
+ expect(view.button("Connect a computer")).not.toBeNull();
316
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
317
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
318
+ await view.rerender();
319
+ expect(opened).toEqual([]);
320
+ expect(view.text()).toContain("Chrome is connected (Work).");
321
+ expect(view.button("Continue")).not.toBeNull();
322
+ await view.close();
323
+ });
324
+
325
+ test("Chrome that links up a few seconds after the computer is noticed without a click", async () => {
326
+ const view = await render();
327
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
328
+ await view.rerender();
329
+ expect(view.button("Add to Chrome")).not.toBeNull();
330
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
331
+ await act(async () => new Promise((resolve) => setTimeout(resolve, 5_300)));
332
+ expect(opened).toEqual([]);
333
+ expect(view.text()).toContain("Chrome is connected (Work).");
334
+ expect(view.note()).toBe("");
335
+ await view.close();
336
+ }, 10_000);
337
+
338
+ test("a card shown after Chrome already connected offers no Continue", async () => {
339
+ fleet.machines = [sessionBox, machine("mac", "Ada's MacBook")];
340
+ browsers = { devices: [chromeProfile("Work")], bridges: [], revision: 2 };
341
+ const view = await render(chromeNotice, () => ({
342
+ blocked: null,
343
+ awaitingHuman: false,
344
+ extras: {},
345
+ }));
346
+ expect(view.text()).toContain("Chrome is connected (Work).");
347
+ expect(view.button("Continue")).toBeNull();
348
+ await view.close();
349
+ });
350
+
351
+ test("says who can connect when this person cannot", async () => {
352
+ fleet.canManage = false;
353
+ const member = await render();
354
+ expect(member.text()).toContain("Ask a workspace admin to connect a computer.");
355
+ expect(member.button("Connect a computer")).toBeNull();
356
+ await member.close();
357
+
358
+ fleet.error = new OpenGeniApiError(404, "not found", {});
359
+ const off = await render();
360
+ expect(off.text()).toContain("Connected Machines aren't available on this deployment.");
361
+ await off.close();
362
+ });
363
+
364
+ test("an earlier copy is a short note, and the machine card itself is unchanged", async () => {
365
+ const repeated = await render({ ...chromeNotice, askedAgainLater: true } as AuthNeededItem);
366
+ expect(repeated.text()).toBe("The agent asked to connect Chrome. It asked again below.");
367
+ await repeated.close();
368
+
369
+ const { purpose: _purpose, ...machineNotice } = chromeNotice as AuthNeededItem & {
370
+ purpose?: string;
371
+ };
372
+ const machineCard = await render(machineNotice as AuthNeededItem);
373
+ expect(machineCard.text()).toContain("Connected Machine");
374
+ expect(machineCard.text()).not.toContain("Opengeni Browser extension");
375
+ await machineCard.close();
376
+ });
377
+ });
@@ -0,0 +1,18 @@
1
+ import { expect, test } from "bun:test";
2
+
3
+ // Radix caches DOM availability at import time. Other tests intentionally render
4
+ // without a DOM, so keep the card in an independent module graph.
5
+ test("Chrome setup card leads with one next step and continues the chat once Chrome connects", async () => {
6
+ const child = Bun.spawn({
7
+ cmd: [process.execPath, "test", "./session-chrome-card.dom-fixture.tsx"],
8
+ cwd: import.meta.dir,
9
+ stdout: "pipe",
10
+ stderr: "pipe",
11
+ });
12
+ const [stdout, stderr, exitCode] = await Promise.all([
13
+ new Response(child.stdout).text(),
14
+ new Response(child.stderr).text(),
15
+ child.exited,
16
+ ]);
17
+ expect(exitCode, `${stdout}\n${stderr}`).toBe(0);
18
+ }, 20_000);