@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
@@ -0,0 +1,243 @@
1
+ import { t } from "../../lib/console-text";
2
+ import type { AuthNeededItem } from "../../../timeline";
3
+ import { MACHINES_SESSION_POLL_MS } from "../../../machines";
4
+ import { machineDisplayName } from "../../../sandbox-label-model";
5
+ import { OPENGENI_BROWSER_EXTENSION_URL } from "@opengeni/contracts";
6
+ import { OpenGeniApiError } from "@opengeni/sdk";
7
+ import { useEffect, useRef, useState } from "react";
8
+
9
+ import { useAppApi } from "../../lib/app-api";
10
+ import { ConnectMachineDialog } from "../machines/connect-machine-dialog";
11
+ import { Button } from "../../../ui/button";
12
+ import { Notice } from "../../../ui/notice";
13
+ import { useAppContext } from "../../app-context";
14
+ import { userErrorText } from "../../../ui/api-error";
15
+ import { isMachineComputeSelectable } from "../../lib/machine-selectability";
16
+ import { useDesktopThisComputer } from "../desktop/this-computer";
17
+ import { hasWorkspacePermission } from "../../lib/permissions";
18
+ import { useWorkspaceMachines } from "../../lib/use-workspace-machines";
19
+ import { thisComputerReady, thisComputerNeedsPermissions } from "../../../this-computer";
20
+ import { SessionCapabilityFrame } from "../../../components/session-capability-frame";
21
+ import type { ChatSendContext } from "./session-github-repositories";
22
+ import { useAttachedChrome } from "./session-machine-card";
23
+
24
+ /** While the connect dialog is open, look for the new machine every few seconds. */
25
+ const WATCH_POLL_MS = 3_000;
26
+
27
+ /** What the person's message says when they continue after connecting Chrome. */
28
+ export const CHROME_CONNECTED_MESSAGE = "Chrome is connected. Continue.";
29
+
30
+ /** A neutral browser mark for the card (the frame renders logos as images). */
31
+ const CHROME_CARD_LOGO =
32
+ "data:image/svg+xml;utf8," +
33
+ encodeURIComponent(
34
+ '<svg xmlns="http://www.w3.org/2000/svg" viewBox="-2 -2 28 28" fill="none" stroke="#71717a" stroke-width="1.75" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10"/><path d="M 12 2a14.5 14.5 0 0 0 0 20 14.5 14.5 0 0 0 0-20"/><path d="M2 12h20"/></svg>',
35
+ );
36
+
37
+ export type SessionChromeCapabilityCardProps = {
38
+ item: AuthNeededItem;
39
+ workspaceId: string;
40
+ sessionId: string;
41
+ /** What a composer Send would carry now, so Continue follows the same rules. */
42
+ sendContext?: (() => ChatSendContext) | undefined;
43
+ };
44
+
45
+ /**
46
+ * The card a browser tool posts when the agent needs the person's own Chrome and
47
+ * none is connected. It shows one next step at a time: connect a computer, then
48
+ * add the Opengeni Browser extension there. Once Chrome links up, Continue tells
49
+ * the agent to go on.
50
+ */
51
+ export function SessionChromeCapabilityCard({
52
+ item,
53
+ workspaceId,
54
+ sessionId,
55
+ sendContext,
56
+ }: SessionChromeCapabilityCardProps) {
57
+ const context = useAppContext();
58
+ const { apiBaseUrl } = useAppApi();
59
+ const accessKnown = context.accessContext !== null && context.accessContext !== undefined;
60
+ const canMessage =
61
+ !accessKnown || hasWorkspacePermission(context.accessContext, workspaceId, "sessions:control");
62
+ const [setupOpen, setSetupOpen] = useState(false);
63
+ const [sending, setSending] = useState(false);
64
+ const [sent, setSent] = useState(false);
65
+ const [sendError, setSendError] = useState<string | null>(null);
66
+ const cardRef = useRef<HTMLElement>(null);
67
+ const opener = useRef<HTMLButtonElement>(null);
68
+
69
+ const fleet = useWorkspaceMachines({
70
+ workspaceId,
71
+ sessionId,
72
+ pollIntervalMs: setupOpen ? WATCH_POLL_MS : MACHINES_SESSION_POLL_MS,
73
+ });
74
+ const unavailableOnDeployment =
75
+ fleet.error instanceof OpenGeniApiError && fleet.error.status === 404;
76
+ // Chrome runs on a computer with a screen that is online now.
77
+ const computer =
78
+ fleet.machines.find(
79
+ (machine) =>
80
+ !machine.isSessionGroup &&
81
+ machine.enrollmentId &&
82
+ machine.hasDisplay &&
83
+ isMachineComputeSelectable(machine.state),
84
+ ) ?? null;
85
+ // Read again when a computer comes online: its Chrome may already have the
86
+ // extension, so the card can skip straight to Continue.
87
+ const chrome = useAttachedChrome(workspaceId, "any", computer?.sandboxId ?? null);
88
+ // Continue is offered only after this card saw Chrome missing, so an old card,
89
+ // or one reloaded after Chrome connected, never sends a stray message.
90
+ const [sawMissing, setSawMissing] = useState(false);
91
+ useEffect(() => {
92
+ if (chrome.checked && chrome.connected === null) setSawMissing(true);
93
+ }, [chrome.checked, chrome.connected]);
94
+ const desktop = useDesktopThisComputer();
95
+ const offerThisComputer =
96
+ computer === null &&
97
+ desktop !== null &&
98
+ desktop.computer.status !== null &&
99
+ desktop.computer.status.state !== "unavailable" &&
100
+ !thisComputerReady(desktop.computer.status) &&
101
+ !unavailableOnDeployment;
102
+ const origin = apiBaseUrl || (typeof window !== "undefined" ? window.location.origin : "");
103
+ const computerName = computer ? machineDisplayName(t, computer) : "";
104
+
105
+ const chat = sendContext?.() ?? { blocked: null, awaitingHuman: false, extras: {} };
106
+ const canContinue = canMessage && sawMissing && !chat.awaitingHuman && !sent;
107
+ const sendContinue = async () => {
108
+ if (sending || !canContinue) return;
109
+ const current = sendContext?.() ?? chat;
110
+ if (current.blocked) {
111
+ setSendError(current.blocked);
112
+ return;
113
+ }
114
+ setSending(true);
115
+ setSendError(null);
116
+ try {
117
+ await context.client.sendMessage(workspaceId, sessionId, {
118
+ text: CHROME_CONNECTED_MESSAGE,
119
+ ...current.extras,
120
+ clientEventId: crypto.randomUUID(),
121
+ });
122
+ setSent(true);
123
+ } catch (failure) {
124
+ setSendError(userErrorText(failure, "Try again."));
125
+ } finally {
126
+ setSending(false);
127
+ }
128
+ };
129
+
130
+ // One clear next step: connect a computer, then add the extension there.
131
+ const step: "computer" | "extension" = computer ? "extension" : "computer";
132
+ const actionUnavailable = unavailableOnDeployment
133
+ ? "Connected Machines aren't available on this deployment."
134
+ : step === "computer" && !offerThisComputer && !fleet.canRead
135
+ ? "You don't have access to this workspace's machines."
136
+ : step === "computer" && !offerThisComputer && !fleet.canManage
137
+ ? "Ask a workspace admin to connect a computer."
138
+ : null;
139
+
140
+ return (
141
+ <>
142
+ <SessionCapabilityFrame
143
+ name="Chrome"
144
+ subtitle="Opengeni Browser extension"
145
+ logo={CHROME_CARD_LOGO}
146
+ typeLabel="Browser"
147
+ description={
148
+ step === "extension"
149
+ ? `Add the extension to Chrome on ${computerName}.`
150
+ : `${item.capability?.rationale ?? "Connect your Chrome so the agent can use it."} First connect a computer, then add the extension.`
151
+ }
152
+ skill={false}
153
+ expanded={false}
154
+ complete={chrome.connected !== null}
155
+ completeLabel={
156
+ chrome.connected
157
+ ? sent
158
+ ? `Chrome is connected (${chrome.connected.name}). The agent continues.`
159
+ : `Chrome is connected (${chrome.connected.name}).`
160
+ : undefined
161
+ }
162
+ actionLabel={
163
+ step === "extension"
164
+ ? "Add to Chrome"
165
+ : offerThisComputer
166
+ ? desktop.computer.acting
167
+ ? "Connecting…"
168
+ : "Use this computer"
169
+ : "Connect a computer"
170
+ }
171
+ busy={step === "computer" && offerThisComputer && desktop.computer.acting}
172
+ actionUnavailable={actionUnavailable}
173
+ opensDialog={step === "computer" && !offerThisComputer}
174
+ note={step === "extension" && chrome.watching ? "Waiting for Chrome to connect…" : ""}
175
+ onOpen={() => {
176
+ if (step === "extension") {
177
+ window.open(OPENGENI_BROWSER_EXTENSION_URL, "_blank", "noopener,noreferrer");
178
+ chrome.watch();
179
+ return;
180
+ }
181
+ if (!offerThisComputer) return setSetupOpen(true);
182
+ if (desktop.computer.acting) return;
183
+ if (thisComputerNeedsPermissions(desktop.computer.status)) return desktop.openSetup();
184
+ void (
185
+ desktop.computer.status?.state === "paused"
186
+ ? desktop.computer.resume().then(() => desktop.computer.status)
187
+ : desktop.computer.connect({ screenControl: false })
188
+ ).then((next) => {
189
+ if (thisComputerNeedsPermissions(next)) desktop.openSetup();
190
+ void fleet.refresh();
191
+ });
192
+ }}
193
+ onClose={() => setSetupOpen(false)}
194
+ opener={opener}
195
+ cardRef={cardRef}
196
+ details={
197
+ chrome.connected && (canContinue || sendError) ? (
198
+ <div
199
+ data-slot="chrome-continue"
200
+ className="space-y-2 border-t border-border px-[19px] pt-3 pb-4 max-[480px]:px-3.5"
201
+ >
202
+ {sendError ? (
203
+ <Notice tone="failed" live="assertive" className="text-xs">
204
+ {`Couldn't send. ${sendError}`}
205
+ </Notice>
206
+ ) : null}
207
+ {canContinue ? (
208
+ <Button
209
+ type="button"
210
+ size="sm"
211
+ className="rounded-[10px] pointer-coarse:h-11"
212
+ aria-disabled={sending || undefined}
213
+ onClick={() => void sendContinue()}
214
+ >
215
+ {sending ? "Sending…" : "Continue"}
216
+ </Button>
217
+ ) : null}
218
+ </div>
219
+ ) : null
220
+ }
221
+ />
222
+ {fleet.canManage && !unavailableOnDeployment ? (
223
+ <ConnectMachineDialog
224
+ open={setupOpen}
225
+ onOpenChange={(open) => {
226
+ if (!open) setSetupOpen(false);
227
+ }}
228
+ workspaceId={workspaceId}
229
+ sessionId={sessionId}
230
+ origin={origin}
231
+ machines={fleet.machines}
232
+ machinesLoading={fleet.loading}
233
+ machinesError={fleet.error}
234
+ personalWorkspace={
235
+ context.workspaces?.find((workspace) => workspace.id === workspaceId)?.kind ===
236
+ "personal"
237
+ }
238
+ defaultScope="user"
239
+ />
240
+ ) : null}
241
+ </>
242
+ );
243
+ }
@@ -434,6 +434,7 @@ describe("Connected Machine conversation card", () => {
434
434
  profileLabel: "Teammate",
435
435
  browserName: "Chrome",
436
436
  state: "connected",
437
+ capabilities: { debuggerAttachment: true },
437
438
  },
438
439
  {
439
440
  id: "device",
@@ -442,6 +443,7 @@ describe("Connected Machine conversation card", () => {
442
443
  profileLabel: "Work",
443
444
  browserName: "Chrome",
444
445
  state: "connected",
446
+ capabilities: { debuggerAttachment: true },
445
447
  },
446
448
  ],
447
449
  };
@@ -45,6 +45,11 @@ const WATCH_POLL_MS = 3_000;
45
45
  const CHROME_POLL_MS = 5_000;
46
46
  /** Stop checking for the extension after this long without a link. */
47
47
  const CHROME_POLL_LIMIT_MS = 10 * 60_000;
48
+ /**
49
+ * After a computer comes online, keep checking quietly for a short while: its
50
+ * Chrome profile usually links up a few seconds after the machine does.
51
+ */
52
+ const CHROME_REFRESH_POLL_LIMIT_MS = 60_000;
48
53
 
49
54
  export { machineUseMessage } from "../../lib/move-chat-to-machine";
50
55
 
@@ -237,7 +242,9 @@ export function SessionMachineCapabilityCard({
237
242
  null;
238
243
  const chrome = useAttachedChrome(
239
244
  workspaceId,
240
- fleet.canRead ? (chromeMachine?.enrollmentId ?? null) : null,
245
+ fleet.canRead && chromeMachine?.enrollmentId
246
+ ? { enrollmentId: chromeMachine.enrollmentId }
247
+ : null,
241
248
  );
242
249
  const fresh = justConnected
243
250
  ? (machines.find((machine) => machine.sandboxId === justConnected) ?? null)
@@ -478,11 +485,13 @@ export function SessionMachineCapabilityCard({
478
485
  * An earlier Connected Machine card whose request the agent made again further
479
486
  * down. The newer card is the live one, so this stays a single quiet line.
480
487
  */
481
- export function RepeatedMachineRequestNote() {
488
+ export function RepeatedMachineRequestNote({ chrome = false }: { chrome?: boolean }) {
482
489
  return (
483
490
  <p className="flex items-center gap-2 text-xs text-fg-muted">
484
491
  <LaptopIcon aria-hidden className="size-3.5 shrink-0" />
485
- The agent asked to use a machine. It asked again below.
492
+ {chrome
493
+ ? "The agent asked to connect Chrome. It asked again below."
494
+ : "The agent asked to use a machine. It asked again below."}
486
495
  </p>
487
496
  );
488
497
  }
@@ -562,32 +571,55 @@ function MachineRow({
562
571
  );
563
572
  }
564
573
 
565
- type ChromeState = {
574
+ export type ChromeState = {
566
575
  connected: { name: string } | null;
576
+ /** A read completed, so a null `connected` means Chrome is not linked yet. */
577
+ checked: boolean;
567
578
  /** Start checking for the extension (after the person opens the store). */
568
579
  watch: () => void;
580
+ /** Checking for the extension after the person opened the store. */
581
+ watching: boolean;
569
582
  };
570
583
 
584
+ /** Which Chrome to look for: one on an exact machine, or any the person connected. */
585
+ export type AttachedChromeScope = { enrollmentId: string } | "any" | null;
586
+
571
587
  /**
572
- * Whether the Opengeni Browser extension linked a Chrome profile on this exact
573
- * machine. Reads once; polls only after the person opens the store, stops once
574
- * linked, after a bounded time, or when the inventory is not available to them.
588
+ * Whether the Opengeni Browser extension linked a Chrome profile the agent can
589
+ * use (the same check browser_open applies) on this exact machine, or, with
590
+ * "any", on any machine. Reads once, and again whenever `refreshKey` changes
591
+ * (for example when a computer comes online); polls only after the person opens
592
+ * the store, stops once linked, after a bounded time, or when the inventory is
593
+ * not available to them.
575
594
  */
576
- function useAttachedChrome(workspaceId: string, enrollmentId: string | null): ChromeState {
595
+ export function useAttachedChrome(
596
+ workspaceId: string,
597
+ scope: AttachedChromeScope,
598
+ refreshKey: string | null = null,
599
+ ): ChromeState {
577
600
  const { client } = useAppContext();
578
601
  // Poll on its own timer; a new client identity on re-render must not refetch.
579
602
  const clientRef = useRef(client);
580
603
  clientRef.current = client;
581
604
  const [connected, setConnected] = useState<{ name: string } | null>(null);
605
+ const [checked, setChecked] = useState(false);
582
606
  const [watchSince, setWatchSince] = useState<number | null>(null);
607
+ const [refreshSince, setRefreshSince] = useState<number | null>(null);
608
+ const [expired, setExpired] = useState(false);
583
609
  const [denied, setDenied] = useState(false);
584
610
  const linked = connected !== null;
611
+ const enrollmentId = scope === null ? null : scope === "any" ? "*" : scope.enrollmentId;
585
612
 
586
613
  useEffect(() => {
587
614
  setConnected(null);
615
+ setChecked(false);
588
616
  setDenied(false);
589
617
  }, [enrollmentId]);
590
618
 
619
+ useEffect(() => {
620
+ setRefreshSince(refreshKey === null ? null : Date.now());
621
+ }, [refreshKey]);
622
+
591
623
  useEffect(() => {
592
624
  if (!enrollmentId || denied) return;
593
625
  let cancelled = false;
@@ -596,9 +628,13 @@ function useAttachedChrome(workspaceId: string, enrollmentId: string | null): Ch
596
628
  const result = await clientRef.current.listAttachedBrowsers(workspaceId);
597
629
  if (cancelled) return;
598
630
  const device = result.devices.find(
599
- (candidate) => candidate.state === "connected" && candidate.enrollmentId === enrollmentId,
631
+ (candidate) =>
632
+ candidate.state === "connected" &&
633
+ candidate.capabilities.debuggerAttachment &&
634
+ (enrollmentId === "*" || candidate.enrollmentId === enrollmentId),
600
635
  );
601
636
  setConnected(device ? { name: device.profileLabel ?? device.browserName } : null);
637
+ setChecked(true);
602
638
  } catch (failure) {
603
639
  // Without inventory access the step still offers the install link.
604
640
  if (
@@ -612,10 +648,17 @@ function useAttachedChrome(workspaceId: string, enrollmentId: string | null): Ch
612
648
  }
613
649
  };
614
650
  void read();
615
- if (linked || watchSince === null) return () => void (cancelled = true);
651
+ if (linked || (watchSince === null && refreshSince === null)) {
652
+ return () => void (cancelled = true);
653
+ }
616
654
  const timer = window.setInterval(() => {
617
- if (Date.now() - watchSince > CHROME_POLL_LIMIT_MS) {
655
+ const now = Date.now();
656
+ const watching = watchSince !== null && now - watchSince <= CHROME_POLL_LIMIT_MS;
657
+ const refreshing =
658
+ refreshSince !== null && now - refreshSince <= CHROME_REFRESH_POLL_LIMIT_MS;
659
+ if (!watching && !refreshing) {
618
660
  window.clearInterval(timer);
661
+ if (watchSince !== null) setExpired(true);
619
662
  return;
620
663
  }
621
664
  if (document.visibilityState !== "hidden") void read();
@@ -624,10 +667,18 @@ function useAttachedChrome(workspaceId: string, enrollmentId: string | null): Ch
624
667
  cancelled = true;
625
668
  window.clearInterval(timer);
626
669
  };
627
- }, [denied, enrollmentId, linked, watchSince, workspaceId]);
670
+ }, [denied, enrollmentId, linked, refreshSince, watchSince, workspaceId]);
628
671
 
629
- const watch = useCallback(() => setWatchSince(Date.now()), []);
630
- return { connected, watch };
672
+ const watch = useCallback(() => {
673
+ setExpired(false);
674
+ setWatchSince(Date.now());
675
+ }, []);
676
+ return {
677
+ connected,
678
+ checked,
679
+ watch,
680
+ watching: watchSince !== null && !linked && !expired && !denied,
681
+ };
631
682
  }
632
683
 
633
684
  /** How long to keep checking for the new consent, or a granted Mac permission. */
@@ -1498,6 +1498,7 @@ export function buildTimeline(
1498
1498
  capability,
1499
1499
  setupRequest,
1500
1500
  requestedSandboxId: stringValue(payload.requestedSandboxId) || null,
1501
+ ...(payload.purpose === "chrome" ? { purpose: "chrome" as const } : {}),
1501
1502
  occurredAt: event.occurredAt,
1502
1503
  });
1503
1504
  break;
@@ -2197,14 +2198,15 @@ const CONNECTED_MACHINE_CARD_ID = "api:connected-machine";
2197
2198
  * An agent that is refused a machine often asks again in later turns. Only the
2198
2199
  * newest Connected Machine card for the same request stays a full card; earlier
2199
2200
  * ones become a short note, so a chat never piles up copies of the same card.
2200
- * Cards that name different machines are separate requests.
2201
+ * Cards that name different machines, or ask for Chrome rather than a machine,
2202
+ * are separate requests.
2201
2203
  */
2202
2204
  function markRepeatedMachineRequests(items: TimelineItem[]): void {
2203
2205
  const seen = new Set<string>();
2204
2206
  for (let index = items.length - 1; index >= 0; index -= 1) {
2205
2207
  const item = items[index]!;
2206
2208
  if (item.kind !== "auth-needed" || item.capability?.id !== CONNECTED_MACHINE_CARD_ID) continue;
2207
- const key = item.requestedSandboxId ?? "";
2209
+ const key = `${item.purpose ?? ""}:${item.requestedSandboxId ?? ""}`;
2208
2210
  if (seen.has(key)) item.askedAgainLater = true;
2209
2211
  else seen.add(key);
2210
2212
  }
@@ -600,6 +600,8 @@ export type AuthNeededItem = {
600
600
  setupRequest?: ToolAuthNeededPayload["setupRequest"] | null | undefined;
601
601
  /** The Connected Machine the agent asked for, when the card names one. */
602
602
  requestedSandboxId?: string | null | undefined;
603
+ /** `chrome`: the Connected Machine card is for the person's own Chrome. */
604
+ purpose?: "chrome" | undefined;
603
605
  /**
604
606
  * A later Connected Machine card in this chat asks for the same thing, so
605
607
  * this one is shown as a short note instead of a second full card.
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/app/components/capabilities/session-machine-card.tsx"],"sourcesContent":["import { t } from \"../../lib/console-text\";\nimport type { AuthNeededItem } from \"../../../timeline\";\nimport { MACHINES_SESSION_POLL_MS, type MachineView } from \"../../../machines\";\nimport { machineDisplayName, machineScopeLabel } from \"../../../sandbox-label-model\";\nimport { OPENGENI_BROWSER_EXTENSION_URL } from \"@opengeni/contracts\";\nimport { OpenGeniApiError } from \"@opengeni/sdk\";\nimport {\n ArrowUpRightIcon,\n CheckIcon,\n ChevronRightIcon,\n LaptopIcon,\n Loader2Icon,\n MonitorIcon,\n PlusIcon,\n PuzzleIcon,\n ServerIcon,\n} from \"lucide-react\";\nimport { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from \"react\";\n\nimport { useAppApi } from \"../../lib/app-api\";\nimport {\n ConnectMachineDialog,\n connectScopeFor,\n type ConnectMachineScope,\n} from \"../machines/connect-machine-dialog\";\nimport { Button } from \"../../../ui/button\";\nimport { ListRow, RowList } from \"../../../ui/list-row\";\nimport { LogoTile } from \"../../../ui/logo-tile\";\nimport { Notice } from \"../../../ui/notice\";\nimport { RowButton } from \"../../../ui/page-actions\";\nimport { useAppContext } from \"../../app-context\";\nimport { userErrorText } from \"../../../ui/api-error\";\nimport { isMachineComputeSelectable } from \"../../lib/machine-selectability\";\nimport { useDesktopThisComputer } from \"../desktop/this-computer\";\nimport { moveChatToMachine } from \"../../lib/move-chat-to-machine\";\nimport { hasWorkspacePermission } from \"../../lib/permissions\";\nimport { useWorkspaceMachines } from \"../../lib/use-workspace-machines\";\nimport { thisComputerReady, thisComputerNeedsPermissions } from \"../../../this-computer\";\nimport { SessionCapabilityFrame } from \"../../../components/session-capability-frame\";\nimport type { ChatSendContext } from \"./session-github-repositories\";\n\n/** While the connect dialog is open, look for the new machine every few seconds. */\nconst WATCH_POLL_MS = 3_000;\n/** After \"Add to Chrome\", how often to check whether the extension linked up. */\nconst CHROME_POLL_MS = 5_000;\n/** Stop checking for the extension after this long without a link. */\nconst CHROME_POLL_LIMIT_MS = 10 * 60_000;\n\nexport { machineUseMessage } from \"../../lib/move-chat-to-machine\";\n\n/** A neutral laptop mark for the card (the frame renders logos as images). */\nexport const CONNECTED_MACHINE_LOGO =\n \"data:image/svg+xml;utf8,\" +\n encodeURIComponent(\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"-2 -2 28 28\" fill=\"none\" stroke=\"#71717a\" stroke-width=\"1.75\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M18 5a2 2 0 0 1 2 2v8.526a2 2 0 0 0 .212.897l1.068 2.127a1 1 0 0 1-.9 1.45H3.62a1 1 0 0 1-.9-1.45l1.068-2.127A2 2 0 0 0 4 15.526V7a2 2 0 0 1 2-2z\"/><path d=\"M20.054 15.987H3.946\"/></svg>',\n );\n\nexport type SessionMachineCapabilityCardProps = {\n item: AuthNeededItem;\n workspaceId: string;\n sessionId: string;\n /** What a composer Send would carry now, so this Send follows the same rules. */\n sendContext?: (() => ChatSendContext) | undefined;\n /** Re-read the session after the chat moves to a machine. */\n onConfigured?: (() => Promise<void>) | undefined;\n};\n\nfunction stateLabel(machine: MachineView): string {\n switch (machine.state) {\n case \"online\":\n case \"consent_required\":\n return \"Online\";\n case \"display_unavailable\":\n return \"Online · no display\";\n case \"reconnecting\":\n return \"Reconnecting\";\n case \"enrolling\":\n return \"Connecting\";\n default:\n return \"Offline\";\n }\n}\n\n/** Focus moves only when the person is not working somewhere else. */\nfunction focusIfIdle(card: HTMLElement | null, target: HTMLElement | null = card) {\n requestAnimationFrame(() => {\n const active = document.activeElement;\n const idle = !active || active === document.body || (card?.contains(active) ?? false);\n if (idle && target?.isConnected) target.focus();\n else if (idle && card?.isConnected) card.focus();\n });\n}\n\n/**\n * The Connected Machine card in a conversation. The agent can post it, but only\n * the person can connect a machine or move this chat onto one. Once a machine with a\n * screen is reachable, the card offers the Opengeni Browser extension for it.\n */\nexport function SessionMachineCapabilityCard({\n item,\n workspaceId,\n sessionId,\n sendContext,\n onConfigured,\n}: SessionMachineCapabilityCardProps) {\n const context = useAppContext();\n const { apiBaseUrl } = useAppApi();\n const recommendation = item.capability!;\n const accessKnown = context.accessContext !== null && context.accessContext !== undefined;\n const canMessage =\n !accessKnown || hasWorkspacePermission(context.accessContext, workspaceId, \"sessions:control\");\n\n const [setupOpen, setSetupOpen] = useState(false);\n // Reconnecting to turn screen control on starts with it already chosen, and\n // with the machine's own access so the same machine reconnects as it was.\n const [setupScreenControl, setSetupScreenControl] = useState(false);\n const [setupScope, setSetupScope] = useState<ConnectMachineScope>(\"user\");\n const [justConnected, setJustConnected] = useState<string | null>(null);\n const [using, setUsing] = useState<string | null>(null);\n const [useError, setUseError] = useState<{ name: string; reason: string | null } | null>(null);\n const [outcome, setOutcome] = useState<{ sandboxId: string; text: string } | null>(null);\n const [announcement, setAnnouncement] = useState(\"\");\n const [othersOpen, setOthersOpen] = useState(false);\n const cardRef = useRef<HTMLElement>(null);\n const opener = useRef<HTMLButtonElement>(null);\n const setupOpener = useRef<HTMLElement | null>(null);\n const alive = useRef(true);\n useEffect(() => {\n alive.current = true;\n return () => {\n alive.current = false;\n };\n }, []);\n\n const fleet = useWorkspaceMachines({\n workspaceId,\n sessionId,\n pollIntervalMs: setupOpen ? WATCH_POLL_MS : MACHINES_SESSION_POLL_MS,\n });\n const unavailableOnDeployment =\n fleet.error instanceof OpenGeniApiError && fleet.error.status === 404;\n const machines = useMemo(\n () => fleet.machines.filter((machine) => !machine.isSessionGroup && machine.enrollmentId),\n [fleet.machines],\n );\n const activeMachine = machines.find((machine) => machine.active) ?? null;\n const reachable = machines.filter((machine) => isMachineComputeSelectable(machine.state));\n // The machine the agent asked for, while this chat doesn't run on it yet.\n const requested =\n (item.requestedSandboxId\n ? machines.find((machine) => machine.sandboxId === item.requestedSandboxId)\n : undefined) ?? null;\n const asking = requested !== null && !requested.active;\n const requestedName = requested ? machineDisplayName(t, requested) : \"\";\n // The person can allow it right here: lead with that, other machines fold away.\n const allowHere =\n asking && fleet.canAttach && isMachineComputeSelectable(requested.state) ? requested : null;\n const personalWorkspace =\n context.workspaces?.find((workspace) => workspace.id === workspaceId)?.kind === \"personal\";\n\n const openSetup = (reconnect?: MachineView) => {\n const active = document.activeElement;\n setupOpener.current = active instanceof HTMLElement ? active : null;\n setJustConnected(null);\n setSetupScreenControl(reconnect !== undefined);\n setSetupScope(reconnect ? connectScopeFor(reconnect) : \"user\");\n setSetupOpen(true);\n };\n const closeSetup = () => {\n // Fast polling ends with the dialog; focus returns to what opened it.\n setSetupOpen(false);\n const back = setupOpener.current?.isConnected ? setupOpener.current : opener.current;\n focusIfIdle(cardRef.current, back);\n };\n\n const ordered = useMemo(() => {\n const rank = (machine: MachineView) =>\n machine.sandboxId === justConnected\n ? 0\n : machine.active\n ? 1\n : isMachineComputeSelectable(machine.state)\n ? 2\n : 3;\n return [...machines]\n .filter((machine) => machine.sandboxId !== allowHere?.sandboxId)\n .sort(\n (left, right) =>\n rank(left) - rank(right) ||\n machineDisplayName(t, left).localeCompare(machineDisplayName(t, right)),\n );\n }, [allowHere?.sandboxId, justConnected, machines]);\n\n const use = useCallback(\n async (machine: MachineView) => {\n if (using !== null) return;\n const name = machineDisplayName(t, machine);\n setUseError(null);\n setOutcome(null);\n const chat = sendContext?.() ?? { blocked: null, awaitingHuman: false, extras: {} };\n if (chat.blocked) {\n setUseError({ name, reason: chat.blocked });\n return;\n }\n setUsing(machine.sandboxId);\n const result = await moveChatToMachine({\n machine,\n chat,\n canMessage,\n previousSandboxId: fleet.machines.find((candidate) => candidate.active)?.sandboxId ?? null,\n attach: fleet.attach,\n sendMessage: (message) => context.client.sendMessage(workspaceId, sessionId, message),\n });\n if (!alive.current) return;\n if (!result.moved) {\n setUsing(null);\n setUseError({ name, reason: result.reason });\n return;\n }\n const text = result.text;\n setUsing(null);\n setOutcome({ sandboxId: machine.sandboxId, text });\n setAnnouncement(text);\n focusIfIdle(cardRef.current);\n await onConfigured?.().catch(() => {});\n },\n [canMessage, context.client, fleet, onConfigured, sendContext, sessionId, using, workspaceId],\n );\n\n // Chrome belongs to a machine with a screen: the one this chat runs on if it\n // has one, otherwise the first reachable one.\n const chromeMachine =\n (activeMachine?.hasDisplay && isMachineComputeSelectable(activeMachine.state)\n ? activeMachine\n : null) ??\n reachable.find((machine) => machine.hasDisplay) ??\n null;\n const chrome = useAttachedChrome(\n workspaceId,\n fleet.canRead ? (chromeMachine?.enrollmentId ?? null) : null,\n );\n const fresh = justConnected\n ? (machines.find((machine) => machine.sandboxId === justConnected) ?? null)\n : null;\n const origin = apiBaseUrl || (typeof window !== \"undefined\" ? window.location.origin : \"\");\n\n // One clear action per state. With a reachable machine, the list's \"Use in\n // this chat\" is primary and connecting another is a quiet secondary action.\n const actionUnavailable = unavailableOnDeployment\n ? \"Connected Machines aren't available on this deployment.\"\n : asking && !isMachineComputeSelectable(requested.state)\n ? `Turn on ${requestedName} to allow it here.`\n : !fleet.canRead\n ? \"You don't have access to this workspace's machines.\"\n : fresh && fleet.canAttach && !fresh.active\n ? `${machineDisplayName(t, fresh)} connected. Choose it below to use it here.`\n : reachable.length > 0 && fleet.canAttach\n ? \"Choose where this chat runs.\"\n : reachable.length > 0\n ? \"You can't change where this chat runs.\"\n : fleet.canManage\n ? null\n : machines.length > 0\n ? \"Turn on one of the machines below to use it here.\"\n : \"Ask a workspace admin to connect a machine.\";\n // Inside the desktop app, the computer it runs on is one click away.\n const desktop = useDesktopThisComputer();\n const desktopReady =\n thisComputerReady(desktop?.computer.status ?? null) && !desktop?.computer.status?.reconnecting;\n const offerThisComputer =\n desktop !== null &&\n desktop.computer.status !== null &&\n desktop.computer.status.state !== \"unavailable\" &&\n !thisComputerReady(desktop.computer.status) &&\n !unavailableOnDeployment &&\n fleet.canRead;\n const connectAnother = fleet.canManage && (reachable.length > 0 || activeMachine !== null);\n const visibleDetails =\n useError !== null || ordered.length > 0 || chromeMachine !== null || connectAnother;\n // While the agent's request can be allowed here, everything else is one\n // click away instead of competing with it.\n const folded = allowHere !== null && !othersOpen;\n const foldable =\n allowHere !== null && (ordered.length > 0 || chromeMachine !== null || connectAnother);\n\n return (\n <>\n <SessionCapabilityFrame\n name={asking ? requestedName : recommendation.name || \"Connected Machine\"}\n subtitle={\n asking\n ? `${stateLabel(requested)} · ${machineScopeLabel(t, requested.scope)}`\n : \"Your computer or server\"\n }\n logo={CONNECTED_MACHINE_LOGO}\n typeLabel=\"Machine\"\n description={\n allowHere\n ? \"The agent asked to use this machine. Allowing it moves this chat here, and the agent continues on it.\"\n : asking\n ? `The agent asked to use ${requestedName}.`\n : recommendation.rationale\n }\n skill={false}\n expanded={false}\n complete={activeMachine !== null || desktopReady}\n completeLabel={\n activeMachine\n ? outcome?.sandboxId === activeMachine.sandboxId\n ? outcome.text\n : `This chat runs on ${machineDisplayName(t, activeMachine)}`\n : desktopReady\n ? `Your agents can use ${desktop?.computer.status?.machineName || \"this computer\"}.`\n : undefined\n }\n actionLabel={\n allowHere\n ? using === allowHere.sandboxId\n ? \"Allowing…\"\n : \"Allow\"\n : offerThisComputer\n ? desktop.computer.acting\n ? \"Connecting…\"\n : \"Use this computer\"\n : \"Connect a computer\"\n }\n busy={allowHere ? using !== null : offerThisComputer && desktop.computer.acting}\n actionUnavailable={allowHere || offerThisComputer ? null : actionUnavailable}\n opensDialog={false}\n note={\n allowHere\n ? \"\"\n : offerThisComputer\n ? \"Only you can use this computer. Your agents can work with files and use the screen after you allow system permissions. Turn it off anytime in the app.\"\n : fleet.canManage\n ? \"You can remove a connected machine anytime from Machines.\"\n : \"\"\n }\n onOpen={() => {\n if (allowHere) return void use(allowHere);\n if (!offerThisComputer) return openSetup();\n if (desktop.computer.acting) return;\n if (thisComputerNeedsPermissions(desktop.computer.status)) return desktop.openSetup();\n void (\n desktop.computer.status?.state === \"paused\"\n ? desktop.computer.resume().then(() => desktop.computer.status)\n : desktop.computer.connect({ screenControl: true })\n ).then((next) => {\n if (thisComputerNeedsPermissions(next)) desktop.openSetup();\n void fleet.refresh();\n });\n }}\n onClose={closeSetup}\n opener={opener}\n cardRef={cardRef}\n details={\n unavailableOnDeployment || !fleet.canRead ? null : (\n <>\n {desktop?.computer.actionError !== null &&\n desktop?.computer.actionError !== undefined ? (\n <Notice tone=\"failed\" live=\"assertive\" className=\"mx-4 mb-3 text-xs\">\n Couldn't connect this computer. {desktop.computer.actionError || \"Try again.\"}\n </Notice>\n ) : null}\n {/* Always mounted, so moves and new machines are announced. */}\n <span className=\"sr-only\" role=\"status\" aria-live=\"polite\">\n {announcement}\n </span>\n {visibleDetails ? (\n <div\n data-slot=\"connected-machines\"\n className=\"space-y-3 border-t border-border px-[19px] pt-3 pb-4 max-[480px]:px-3.5\"\n >\n {useError ? (\n <Notice tone=\"failed\" live=\"assertive\" className=\"text-xs\">\n {`Couldn't move this chat to ${useError.name}. ${\n useError.reason ??\n (fleet.mutationError\n ? userErrorText(fleet.mutationError, \"Try again.\")\n : \"Try again.\")\n }`}\n </Notice>\n ) : null}\n {foldable ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ml-2 text-fg-muted\"\n aria-expanded={othersOpen}\n onClick={() => setOthersOpen((open) => !open)}\n >\n <ChevronRightIcon\n aria-hidden\n className={\n othersOpen ? \"rotate-90 transition-transform\" : \"transition-transform\"\n }\n />\n Use a different machine\n </Button>\n ) : null}\n {!folded && ordered.length > 0 ? (\n <RowList label=\"Connected machines\" flush>\n {ordered.map((machine) => (\n <MachineRow\n key={machine.sandboxId}\n machine={machine}\n primary={machine.sandboxId === justConnected || reachable.length === 1}\n canUse={fleet.canAttach}\n busy={using === machine.sandboxId}\n locked={using !== null || fleet.attaching}\n onUse={() => void use(machine)}\n />\n ))}\n </RowList>\n ) : null}\n {!folded && chromeMachine ? (\n <ScreenControlStep\n key={chromeMachine.sandboxId}\n machine={chromeMachine}\n workspaceId={workspaceId}\n canManage={fleet.canManage}\n refresh={fleet.refresh}\n onReconnect={() => openSetup(chromeMachine)}\n />\n ) : null}\n {!folded && chromeMachine ? (\n <ChromeStep\n chrome={chrome}\n machineName={machineDisplayName(t, chromeMachine)}\n />\n ) : null}\n {!folded && connectAnother ? (\n <Button\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n className=\"-ml-2 text-fg-muted\"\n onClick={() => openSetup()}\n >\n <PlusIcon aria-hidden />\n Connect another machine\n </Button>\n ) : null}\n </div>\n ) : null}\n </>\n )\n }\n />\n {fleet.canManage && !unavailableOnDeployment ? (\n <ConnectMachineDialog\n open={setupOpen}\n onOpenChange={(open) => {\n if (!open) closeSetup();\n }}\n workspaceId={workspaceId}\n sessionId={sessionId}\n origin={origin}\n machines={fleet.machines}\n machinesLoading={fleet.loading}\n machinesError={fleet.error}\n personalWorkspace={personalWorkspace}\n defaultScope={setupScope}\n defaultAllowScreenControl={setupScreenControl}\n onConnected={(machine) => {\n setJustConnected(machine.sandboxId);\n setAnnouncement(`${machineDisplayName(t, machine)} connected.`);\n }}\n useInChat={{ canUse: fleet.canAttach, onUse: (machine) => void use(machine) }}\n />\n ) : null}\n </>\n );\n}\n\n/**\n * An earlier Connected Machine card whose request the agent made again further\n * down. The newer card is the live one, so this stays a single quiet line.\n */\nexport function RepeatedMachineRequestNote() {\n return (\n <p className=\"flex items-center gap-2 text-xs text-fg-muted\">\n <LaptopIcon aria-hidden className=\"size-3.5 shrink-0\" />\n The agent asked to use a machine. It asked again below.\n </p>\n );\n}\n\nfunction MachineRow({\n machine,\n primary,\n canUse,\n busy,\n locked,\n onUse,\n}: {\n machine: MachineView;\n primary: boolean;\n canUse: boolean;\n busy: boolean;\n locked: boolean;\n onUse: () => void;\n}) {\n const name = machineDisplayName(t, machine);\n const selectable = isMachineComputeSelectable(machine.state);\n const Icon = machine.os === \"linux\" && !machine.hasDisplay ? ServerIcon : LaptopIcon;\n const common = {\n leading: <LogoTile icon={<Icon />} />,\n title: name,\n meta: [stateLabel(machine), machineScopeLabel(t, machine.scope)],\n };\n if (machine.active) {\n return (\n <ListRow\n {...common}\n control={\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-status-idle\">\n <CheckIcon aria-hidden className=\"size-3.5\" />\n In this chat\n </span>\n }\n />\n );\n }\n // The meta line already says why (\"Offline\", \"Reconnecting\").\n if (!selectable) return <ListRow {...common} disabled />;\n if (!canUse) return <ListRow {...common} />;\n const label = busy ? `Moving this chat to ${name}` : `Use ${name} in this chat`;\n return (\n <ListRow\n {...common}\n control={\n primary ? (\n <Button\n type=\"button\"\n size=\"sm\"\n aria-label={label}\n aria-disabled={locked || undefined}\n className=\"rounded-[10px] pointer-coarse:h-11\"\n onClick={() => {\n if (!locked) onUse();\n }}\n >\n {busy ? <Loader2Icon aria-hidden className=\"animate-spin\" /> : null}\n {busy ? \"Moving…\" : \"Use in this chat\"}\n </Button>\n ) : (\n <RowButton\n aria-label={label}\n aria-disabled={locked || undefined}\n onClick={() => {\n if (!locked) onUse();\n }}\n >\n {busy ? <Loader2Icon aria-hidden className=\"animate-spin\" /> : null}\n {busy ? \"Moving…\" : \"Use\"}\n </RowButton>\n )\n }\n />\n );\n}\n\ntype ChromeState = {\n connected: { name: string } | null;\n /** Start checking for the extension (after the person opens the store). */\n watch: () => void;\n};\n\n/**\n * Whether the Opengeni Browser extension linked a Chrome profile on this exact\n * machine. Reads once; polls only after the person opens the store, stops once\n * linked, after a bounded time, or when the inventory is not available to them.\n */\nfunction useAttachedChrome(workspaceId: string, enrollmentId: string | null): ChromeState {\n const { client } = useAppContext();\n // Poll on its own timer; a new client identity on re-render must not refetch.\n const clientRef = useRef(client);\n clientRef.current = client;\n const [connected, setConnected] = useState<{ name: string } | null>(null);\n const [watchSince, setWatchSince] = useState<number | null>(null);\n const [denied, setDenied] = useState(false);\n const linked = connected !== null;\n\n useEffect(() => {\n setConnected(null);\n setDenied(false);\n }, [enrollmentId]);\n\n useEffect(() => {\n if (!enrollmentId || denied) return;\n let cancelled = false;\n const read = async () => {\n try {\n const result = await clientRef.current.listAttachedBrowsers(workspaceId);\n if (cancelled) return;\n const device = result.devices.find(\n (candidate) => candidate.state === \"connected\" && candidate.enrollmentId === enrollmentId,\n );\n setConnected(device ? { name: device.profileLabel ?? device.browserName } : null);\n } catch (failure) {\n // Without inventory access the step still offers the install link.\n if (\n !cancelled &&\n failure instanceof OpenGeniApiError &&\n failure.status >= 400 &&\n failure.status < 500\n ) {\n setDenied(true);\n }\n }\n };\n void read();\n if (linked || watchSince === null) return () => void (cancelled = true);\n const timer = window.setInterval(() => {\n if (Date.now() - watchSince > CHROME_POLL_LIMIT_MS) {\n window.clearInterval(timer);\n return;\n }\n if (document.visibilityState !== \"hidden\") void read();\n }, CHROME_POLL_MS);\n return () => {\n cancelled = true;\n window.clearInterval(timer);\n };\n }, [denied, enrollmentId, linked, watchSince, workspaceId]);\n\n const watch = useCallback(() => setWatchSince(Date.now()), []);\n return { connected, watch };\n}\n\n/** How long to keep checking for the new consent, or a granted Mac permission. */\nconst SCREEN_CONTROL_WATCH_MS = 90_000;\nconst MAC_PERMISSION_WATCH_MS = 3 * 60_000;\nconst SCREEN_CONTROL_POLL_MS = 3_000;\n\ntype MacPermissionPane = \"screen_recording\" | \"accessibility\" | \"input_monitoring\";\ntype MacPermissions = NonNullable<NonNullable<MachineView[\"runtime\"]>[\"macPermissions\"]>;\n\n/** The order the agent needs them in: seeing the screen first, then input. */\nconst MAC_PERMISSIONS: ReadonlyArray<{\n pane: MacPermissionPane;\n key: keyof MacPermissions;\n label: string;\n}> = [\n { pane: \"screen_recording\", key: \"screenRecording\", label: \"Screen Recording\" },\n { pane: \"accessibility\", key: \"accessibility\", label: \"Accessibility\" },\n { pane: \"input_monitoring\", key: \"inputMonitoring\", label: \"Input Monitoring\" },\n];\n\nfunction isOnline(machine: MachineView): boolean {\n return (\n machine.state === \"online\" ||\n machine.state === \"consent_required\" ||\n machine.state === \"display_unavailable\"\n );\n}\n\n/**\n * Screen control for the machine with a screen. Turning it on changes this\n * machine in place (agents can do the same themselves); the step then follows\n * what the machine reports: the agent's own consent, then on a Mac each of the\n * three OS permissions, one System Settings pane at a time.\n */\nfunction ScreenControlStep({\n machine,\n workspaceId,\n canManage,\n refresh,\n onReconnect,\n}: {\n machine: MachineView;\n workspaceId: string;\n canManage: boolean;\n refresh: () => Promise<void>;\n /** Open the connect command, for a connection too old to change in place. */\n onReconnect: () => void;\n}) {\n const { client } = useAppContext();\n const stepRef = useRef<HTMLDivElement>(null);\n const refreshRef = useRef(refresh);\n refreshRef.current = refresh;\n const [busy, setBusy] = useState(false);\n const [note, setNote] = useState<string | null>(null);\n const [opened, setOpened] = useState<MacPermissionPane | null>(null);\n const [watchUntil, setWatchUntil] = useState(0);\n const [stalled, setStalled] = useState(false);\n const [reconnect, setReconnect] = useState(false);\n\n const online = isOnline(machine);\n const runtime = machine.runtime;\n const allowed = machine.allowScreenControl;\n // Older reports cannot say whether the agent holds the consent; trust the row.\n const applied = allowed && (runtime?.capabilities.screenControl ?? true);\n const renewable = runtime?.capabilities.credentialRenew === true;\n const mac = machine.os === \"macos\";\n const permissions = mac ? (runtime?.macPermissions ?? null) : null;\n const reason = machine.desktopUnavailableReason?.trim() || null;\n const missing = permissions\n ? (MAC_PERMISSIONS.find((step) => !permissions[step.key]) ?? null)\n : mac && reason !== null && /screen recording/i.test(reason)\n ? MAC_PERMISSIONS[0]!\n : null;\n const updating = runtime?.versionState === \"updating\";\n const waiting =\n (allowed && !applied && online && (renewable || updating) && !reconnect) || opened !== null;\n\n // Also when the card opens on a change already underway (a reload, or an\n // agent turned it on): watch for a while, then offer Try again.\n useEffect(() => {\n if (waiting && watchUntil === 0) setWatchUntil(Date.now() + SCREEN_CONTROL_WATCH_MS);\n }, [waiting, watchUntil]);\n\n // Re-read the machine while a change is on its way, for a bounded time.\n useEffect(() => {\n if (!waiting || watchUntil === 0) return;\n const timer = window.setInterval(() => {\n if (Date.now() > watchUntil) {\n window.clearInterval(timer);\n setStalled(true);\n return;\n }\n void refreshRef.current().catch(() => {});\n }, SCREEN_CONTROL_POLL_MS);\n return () => window.clearInterval(timer);\n }, [waiting, watchUntil]);\n\n async function turnOn() {\n if (busy || !machine.enrollmentId) return;\n setBusy(true);\n setNote(null);\n setStalled(false);\n try {\n const result = await client.enableMachineScreenControl(workspaceId, machine.enrollmentId);\n if (result.reason === \"renewal_failed\") setNote(result.message);\n setReconnect(result.reason === \"reconnect_required\");\n setWatchUntil(Date.now() + SCREEN_CONTROL_WATCH_MS);\n await refreshRef.current().catch(() => {});\n } catch (failure) {\n setNote(\n failure instanceof OpenGeniApiError && failure.status === 403\n ? \"Only an organization admin can turn this on.\"\n : userErrorText(failure, \"Try again.\"),\n );\n } finally {\n setBusy(false);\n focusIfIdle(stepRef.current);\n }\n }\n\n async function updateAgent() {\n if (busy || !machine.enrollmentId) return;\n setBusy(true);\n setNote(null);\n try {\n await client.updateMachineAgent(workspaceId, machine.enrollmentId);\n setStalled(false);\n setWatchUntil(Date.now() + MAC_PERMISSION_WATCH_MS);\n await refreshRef.current().catch(() => {});\n } catch (failure) {\n setNote(userErrorText(failure, \"Try again.\"));\n } finally {\n setBusy(false);\n }\n }\n\n async function openPane(pane: MacPermissionPane) {\n if (busy || !machine.enrollmentId) return;\n setBusy(true);\n setNote(null);\n try {\n const result = await client.openMachinePrivacySettings(workspaceId, machine.enrollmentId, {\n pane,\n });\n if (result.opened) {\n setOpened(pane);\n setStalled(false);\n setWatchUntil(Date.now() + MAC_PERMISSION_WATCH_MS);\n } else {\n setNote(result.message);\n }\n } catch (failure) {\n setNote(userErrorText(failure, \"Try again.\"));\n } finally {\n setBusy(false);\n }\n }\n\n const button = (label: string, onClick: () => void) => (\n <RowButton aria-disabled={busy || undefined} aria-busy={busy || undefined} onClick={onClick}>\n {busy ? <Loader2Icon aria-hidden className=\"animate-spin\" /> : null}\n {label}\n </RowButton>\n );\n const done = (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-status-idle\">\n <CheckIcon aria-hidden className=\"size-3.5\" />\n On\n </span>\n );\n\n // The row stays one short line like its neighbours; anything the person has\n // to do on the machine goes in the wrapping line under it.\n let description: string;\n let hint: string | null = null;\n let control: ReactNode = null;\n if (!allowed) {\n description = canManage\n ? \"Let agents see and use this screen\"\n : \"Ask a workspace admin to turn it on\";\n if (canManage) control = button(busy ? \"Turning on…\" : \"Turn on\", () => void turnOn());\n } else if (!applied) {\n // The step only shows for a reachable machine.\n if (reconnect) {\n description = \"Needs a fresh connection\";\n hint = \"Run the connect command on this machine again, with screen control on.\";\n if (canManage) control = button(\"Connect again\", onReconnect);\n } else if (updating) {\n description = \"Updating the agent…\";\n } else if (!renewable) {\n description = \"Waiting for an agent update\";\n hint = \"It turns on by itself once this machine's agent is updated.\";\n if (canManage && !runtime?.updateBlockedReason) {\n control = button(busy ? \"Updating…\" : \"Update agent\", () => void updateAgent());\n }\n } else if (stalled || note) {\n description = \"Not on yet\";\n if (canManage) control = button(busy ? \"Trying…\" : \"Try again\", () => void turnOn());\n } else {\n description = \"Turning on…\";\n }\n } else if (missing) {\n const shown = opened === missing.pane;\n // The row stays short at any width; the hint names the permission.\n description = \"Mac permission needed\";\n hint = shown\n ? `On the Mac, switch on Opengeni under ${missing.label}.${\n missing.pane === \"screen_recording\" ? \" Choose Quit & Reopen if macOS asks.\" : \"\"\n }`\n : `Allow ${missing.label} for Opengeni on this Mac.`;\n if (canManage && online) {\n control = button(shown ? \"Open again\" : \"Open settings\", () => void openPane(missing.pane));\n }\n } else if (reason) {\n description = reason;\n } else {\n description = \"Agents can see and use this screen\";\n control = done;\n }\n const notice = note ?? hint;\n\n return (\n <div\n data-slot=\"screen-control\"\n ref={stepRef}\n tabIndex={-1}\n className=\"space-y-2 outline-hidden\"\n >\n <RowList label=\"Screen control\" flush>\n <ListRow\n leading={<LogoTile icon={<MonitorIcon />} />}\n title=\"Screen control\"\n description={description}\n control={control}\n />\n </RowList>\n <span className=\"sr-only\" aria-live=\"polite\">\n {notice ? `${description}. ${notice}` : description}\n </span>\n {notice ? (\n <Notice tone=\"waiting\" className=\"text-xs\">\n {notice}\n </Notice>\n ) : null}\n </div>\n );\n}\n\nfunction ChromeStep({ chrome, machineName }: { chrome: ChromeState; machineName: string }) {\n return (\n <div data-slot=\"chrome-extension\">\n <RowList label=\"Chrome\" flush>\n <ListRow\n leading={<LogoTile icon={<PuzzleIcon />} />}\n title=\"Chrome\"\n description={\n chrome.connected\n ? `${chrome.connected.name} on ${machineName}`\n : `Add Opengeni Browser on ${machineName}`\n }\n control={\n chrome.connected ? (\n <span className=\"inline-flex items-center gap-1 text-xs font-medium text-status-idle\">\n <CheckIcon aria-hidden className=\"size-3.5\" />\n Connected\n </span>\n ) : (\n <Button asChild size=\"sm\" variant=\"outline\" className=\"rounded-[10px]\">\n <a\n href={OPENGENI_BROWSER_EXTENSION_URL}\n target=\"_blank\"\n rel=\"noreferrer\"\n onClick={chrome.watch}\n >\n Add to Chrome\n <ArrowUpRightIcon aria-hidden />\n <span className=\"sr-only\"> (opens the Chrome Web Store in a new tab)</span>\n </a>\n </Button>\n )\n }\n />\n </RowList>\n </div>\n );\n}\n"],"mappings":"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","names":[]}