@opengeni/react 3.3.2 → 3.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (855) hide show
  1. package/README.md +769 -22
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/approvals.d.ts +2 -1
  5. package/dist/artifacts-document.js +5 -3
  6. package/dist/artifacts-presentation.js +5 -3
  7. package/dist/artifacts-spreadsheet.d.ts +1 -1
  8. package/dist/artifacts-spreadsheet.js +5 -3
  9. package/dist/artifacts.d.ts +5 -1
  10. package/dist/artifacts.js +536 -42
  11. package/dist/artifacts.js.map +1 -1
  12. package/dist/browser-viewer-ZWR6MWJ7.js +19 -0
  13. package/dist/capability-catalog-row.d.ts +20 -0
  14. package/dist/capability-logo-fallback.d.ts +3 -0
  15. package/dist/chat-interactive-block-4EUKJXUO.js +14 -0
  16. package/dist/chunk-2DDJ6YH6.js +41 -0
  17. package/dist/chunk-2DDJ6YH6.js.map +1 -0
  18. package/dist/chunk-2HVLQ26H.js +183 -0
  19. package/dist/chunk-2HVLQ26H.js.map +1 -0
  20. package/dist/{chunk-XYRDDSXF.js → chunk-2I6R3GCZ.js} +4285 -3980
  21. package/dist/chunk-2I6R3GCZ.js.map +1 -0
  22. package/dist/{chunk-JAQ5K7W6.js → chunk-2SFFYIH3.js} +1178 -166
  23. package/dist/chunk-2SFFYIH3.js.map +1 -0
  24. package/dist/chunk-3PVDYTTB.js +24 -0
  25. package/dist/chunk-3PVDYTTB.js.map +1 -0
  26. package/dist/chunk-3RJKUVCA.js +14068 -0
  27. package/dist/chunk-3RJKUVCA.js.map +1 -0
  28. package/dist/chunk-3UZCWK46.js +298 -0
  29. package/dist/chunk-3UZCWK46.js.map +1 -0
  30. package/dist/chunk-47CKUXTW.js +293 -0
  31. package/dist/chunk-47CKUXTW.js.map +1 -0
  32. package/dist/chunk-4QYP6QUI.js +109 -0
  33. package/dist/chunk-4QYP6QUI.js.map +1 -0
  34. package/dist/chunk-5EY26VZ7.js +1053 -0
  35. package/dist/chunk-5EY26VZ7.js.map +1 -0
  36. package/dist/chunk-5I4VRWBB.js +56 -0
  37. package/dist/chunk-5I4VRWBB.js.map +1 -0
  38. package/dist/chunk-5WOR7UCL.js +191 -0
  39. package/dist/chunk-5WOR7UCL.js.map +1 -0
  40. package/dist/chunk-5YJLGOM5.js +51 -0
  41. package/dist/chunk-5YJLGOM5.js.map +1 -0
  42. package/dist/chunk-6JFN3OWG.js +386 -0
  43. package/dist/chunk-6JFN3OWG.js.map +1 -0
  44. package/dist/chunk-7QJT5O4S.js +35 -0
  45. package/dist/chunk-7QJT5O4S.js.map +1 -0
  46. package/dist/chunk-A55KV72M.js +402 -0
  47. package/dist/chunk-A55KV72M.js.map +1 -0
  48. package/dist/chunk-ADWCLI4G.js +2028 -0
  49. package/dist/chunk-ADWCLI4G.js.map +1 -0
  50. package/dist/chunk-B27E3V37.js +29 -0
  51. package/dist/chunk-B27E3V37.js.map +1 -0
  52. package/dist/chunk-B7TC2A33.js +83 -0
  53. package/dist/chunk-B7TC2A33.js.map +1 -0
  54. package/dist/chunk-BLL56F7R.js +385 -0
  55. package/dist/chunk-BLL56F7R.js.map +1 -0
  56. package/dist/chunk-BOFEQC6K.js +162 -0
  57. package/dist/chunk-BOFEQC6K.js.map +1 -0
  58. package/dist/chunk-CM7LVUKC.js +35 -0
  59. package/dist/chunk-CM7LVUKC.js.map +1 -0
  60. package/dist/chunk-CORUP6JB.js +122 -0
  61. package/dist/chunk-CORUP6JB.js.map +1 -0
  62. package/dist/chunk-CSX5FFKZ.js +35 -0
  63. package/dist/chunk-CSX5FFKZ.js.map +1 -0
  64. package/dist/chunk-EGVHJKGJ.js +3125 -0
  65. package/dist/chunk-EGVHJKGJ.js.map +1 -0
  66. package/dist/{chunk-NEXLLTP4.js → chunk-G76LSA76.js} +6 -5
  67. package/dist/chunk-G76LSA76.js.map +1 -0
  68. package/dist/{chunk-PEI6RANI.js → chunk-GQMRL52F.js} +311 -75
  69. package/dist/chunk-GQMRL52F.js.map +1 -0
  70. package/dist/{chunk-ELIYWBZR.js → chunk-GUUXOISA.js} +1113 -457
  71. package/dist/chunk-GUUXOISA.js.map +1 -0
  72. package/dist/{chunk-HUIKIDM5.js → chunk-I7X2P4FU.js} +225 -116
  73. package/dist/chunk-I7X2P4FU.js.map +1 -0
  74. package/dist/chunk-IGFZFLI2.js +1390 -0
  75. package/dist/chunk-IGFZFLI2.js.map +1 -0
  76. package/dist/{chunk-4MDLEHXL.js → chunk-IMSUVYQB.js} +26 -13
  77. package/dist/chunk-IMSUVYQB.js.map +1 -0
  78. package/dist/chunk-IOYHDEP3.js +38 -0
  79. package/dist/chunk-IOYHDEP3.js.map +1 -0
  80. package/dist/chunk-IZV5FTJC.js +55 -0
  81. package/dist/chunk-IZV5FTJC.js.map +1 -0
  82. package/dist/chunk-KF33GTWN.js +343 -0
  83. package/dist/chunk-KF33GTWN.js.map +1 -0
  84. package/dist/chunk-KSGQ5ODA.js +457 -0
  85. package/dist/chunk-KSGQ5ODA.js.map +1 -0
  86. package/dist/{chunk-ZPBTI3TE.js → chunk-KZC6QRNR.js} +1513 -697
  87. package/dist/chunk-KZC6QRNR.js.map +1 -0
  88. package/dist/chunk-LJUMYUBO.js +218 -0
  89. package/dist/chunk-LJUMYUBO.js.map +1 -0
  90. package/dist/chunk-LSW2OJVV.js +380 -0
  91. package/dist/chunk-LSW2OJVV.js.map +1 -0
  92. package/dist/chunk-M357MHH3.js +2 -0
  93. package/dist/chunk-M357MHH3.js.map +1 -0
  94. package/dist/chunk-MK2TQO4Y.js +24 -0
  95. package/dist/chunk-MK2TQO4Y.js.map +1 -0
  96. package/dist/chunk-N7S72EXK.js +25 -0
  97. package/dist/chunk-N7S72EXK.js.map +1 -0
  98. package/dist/{chunk-YVCHAQZY.js → chunk-NKMYZFHA.js} +616 -621
  99. package/dist/chunk-NKMYZFHA.js.map +1 -0
  100. package/dist/chunk-NX4ADH73.js +14 -0
  101. package/dist/chunk-NX4ADH73.js.map +1 -0
  102. package/dist/chunk-O7F6UJO4.js +71 -0
  103. package/dist/chunk-O7F6UJO4.js.map +1 -0
  104. package/dist/chunk-OYQQJL3J.js +16 -0
  105. package/dist/chunk-OYQQJL3J.js.map +1 -0
  106. package/dist/{chunk-H5YGYNXS.js → chunk-P4BNUO57.js} +60 -17
  107. package/dist/chunk-P4BNUO57.js.map +1 -0
  108. package/dist/{chunk-JVYQRRC4.js → chunk-PEHTX3V7.js} +21 -1
  109. package/dist/chunk-PEHTX3V7.js.map +1 -0
  110. package/dist/chunk-PMV53IMM.js +26 -0
  111. package/dist/chunk-PMV53IMM.js.map +1 -0
  112. package/dist/{chunk-KCGXPP5V.js → chunk-PYEOPA4M.js} +13 -7
  113. package/dist/chunk-PYEOPA4M.js.map +1 -0
  114. package/dist/{chunk-KZ73RL76.js → chunk-QHUE7E4N.js} +8 -3
  115. package/dist/chunk-QHUE7E4N.js.map +1 -0
  116. package/dist/chunk-QV3DJVRZ.js +11 -0
  117. package/dist/chunk-QV3DJVRZ.js.map +1 -0
  118. package/dist/chunk-R5SBNG7S.js +810 -0
  119. package/dist/chunk-R5SBNG7S.js.map +1 -0
  120. package/dist/chunk-RI42H5IU.js +326 -0
  121. package/dist/chunk-RI42H5IU.js.map +1 -0
  122. package/dist/chunk-S2QH6EZ5.js +211 -0
  123. package/dist/chunk-S2QH6EZ5.js.map +1 -0
  124. package/dist/chunk-SHLZMAJN.js +304 -0
  125. package/dist/chunk-SHLZMAJN.js.map +1 -0
  126. package/dist/chunk-TGXK4BAZ.js +293 -0
  127. package/dist/chunk-TGXK4BAZ.js.map +1 -0
  128. package/dist/{chunk-5YK2ZDIG.js → chunk-TNEZNMSN.js} +4 -10
  129. package/dist/chunk-TNEZNMSN.js.map +1 -0
  130. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  131. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  132. package/dist/chunk-UNJPZGNK.js +63 -0
  133. package/dist/chunk-UNJPZGNK.js.map +1 -0
  134. package/dist/{chunk-IOLVN74D.js → chunk-V5ZUJT2T.js} +175 -42
  135. package/dist/chunk-V5ZUJT2T.js.map +1 -0
  136. package/dist/chunk-WBJY57A3.js +402 -0
  137. package/dist/chunk-WBJY57A3.js.map +1 -0
  138. package/dist/chunk-WIUKB3LD.js +1911 -0
  139. package/dist/chunk-WIUKB3LD.js.map +1 -0
  140. package/dist/{chunk-ICA6R6TW.js → chunk-WNN7NO2N.js} +1451 -355
  141. package/dist/chunk-WNN7NO2N.js.map +1 -0
  142. package/dist/{chunk-3AGBGTRT.js → chunk-X5UVUIMF.js} +1712 -1174
  143. package/dist/chunk-X5UVUIMF.js.map +1 -0
  144. package/dist/chunk-XDA5QJE7.js +402 -0
  145. package/dist/chunk-XDA5QJE7.js.map +1 -0
  146. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  147. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  148. package/dist/chunk-XHF7OYAN.js +1150 -0
  149. package/dist/chunk-XHF7OYAN.js.map +1 -0
  150. package/dist/chunk-YSRBFJMQ.js +43 -0
  151. package/dist/chunk-YSRBFJMQ.js.map +1 -0
  152. package/dist/chunk-ZOZAPAUO.js +174 -0
  153. package/dist/chunk-ZOZAPAUO.js.map +1 -0
  154. package/dist/chunk-ZQV5W7YC.js +809 -0
  155. package/dist/chunk-ZQV5W7YC.js.map +1 -0
  156. package/dist/chunk-ZUWF4NF7.js +13 -0
  157. package/dist/chunk-ZUWF4NF7.js.map +1 -0
  158. package/dist/client.d.ts +16 -7
  159. package/dist/clipboard.js +3 -1
  160. package/dist/commands/types.d.ts +2 -0
  161. package/dist/components/anthropic-mark.d.ts +3 -0
  162. package/dist/components/approval-surface.d.ts +19 -6
  163. package/dist/components/artifacts/artifact-chrome.d.ts +120 -0
  164. package/dist/components/artifacts/artifact-sandbox.d.ts +30 -0
  165. package/dist/components/artifacts/artifact-surface.d.ts +4 -2
  166. package/dist/components/artifacts/chat-interactive-block.d.ts +29 -0
  167. package/dist/components/artifacts/deferred-chat-media.d.ts +15 -0
  168. package/dist/components/artifacts/document-editor.d.ts +4 -2
  169. package/dist/components/artifacts/editable-artifact-ui.d.ts +2 -1
  170. package/dist/components/artifacts/editable-artifact-view.d.ts +48 -0
  171. package/dist/components/artifacts/editable-document.d.ts +1 -1
  172. package/dist/components/artifacts/editable-presentation.d.ts +2 -2
  173. package/dist/components/artifacts/editable-spreadsheet.d.ts +8 -3
  174. package/dist/components/artifacts/index.d.ts +9 -2
  175. package/dist/components/artifacts/inline-html-document.d.ts +2 -0
  176. package/dist/components/artifacts/latest-task-runner.d.ts +15 -0
  177. package/dist/components/artifacts/presentation-editor.d.ts +6 -2
  178. package/dist/components/artifacts/published-html-artifact-frame.d.ts +23 -3
  179. package/dist/components/artifacts/session-artifact-viewer.d.ts +39 -0
  180. package/dist/components/artifacts/site-view.d.ts +26 -0
  181. package/dist/components/artifacts/spreadsheet-command-queue.d.ts +20 -0
  182. package/dist/components/artifacts/spreadsheet-download.d.ts +7 -0
  183. package/dist/components/artifacts/spreadsheet-general-display.d.ts +24 -0
  184. package/dist/components/artifacts/spreadsheet-grid.d.ts +10 -3
  185. package/dist/components/artifacts/spreadsheet-resize.d.ts +50 -0
  186. package/dist/components/artifacts/spreadsheet-submissions.d.ts +9 -0
  187. package/dist/components/artifacts/visualization-assets.gen.d.ts +2 -0
  188. package/dist/components/browser-select-control.d.ts +16 -0
  189. package/dist/components/browser-viewer.d.ts +11 -2
  190. package/dist/components/chat-composer.d.ts +21 -3
  191. package/dist/components/child-session-link.d.ts +9 -0
  192. package/dist/components/claude-mark.d.ts +3 -0
  193. package/dist/components/code-editor.d.ts +1 -4
  194. package/dist/components/composer-transcription-control.d.ts +7 -0
  195. package/dist/components/composer.d.ts +34 -67
  196. package/dist/components/computer-viewer.d.ts +1 -1
  197. package/dist/components/copy-button.d.ts +3 -1
  198. package/dist/components/desktop-viewer.d.ts +1 -1
  199. package/dist/components/grok-mark.d.ts +26 -0
  200. package/dist/components/human-input-form.d.ts +28 -32
  201. package/dist/components/human-input-surface.d.ts +7 -2
  202. package/dist/components/linkified-text.d.ts +18 -0
  203. package/dist/components/machine-input-display.d.ts +22 -1
  204. package/dist/components/machines/agent-status-icon.d.ts +6 -0
  205. package/dist/components/machines/machine-detail.d.ts +7 -1
  206. package/dist/components/machines/machine-status.d.ts +48 -0
  207. package/dist/components/markdown-table-layout.d.ts +18 -0
  208. package/dist/components/markdown.d.ts +37 -4
  209. package/dist/components/message-timeline.d.ts +86 -7
  210. package/dist/components/model-mark.d.ts +29 -0
  211. package/dist/components/model-policy-picker-menu.d.ts +9 -0
  212. package/dist/components/model-policy-picker.d.ts +35 -28
  213. package/dist/components/new-conversation.d.ts +32 -0
  214. package/dist/components/open-geni-chat.d.ts +73 -0
  215. package/dist/components/open-geni-links.d.ts +68 -0
  216. package/dist/components/openrouter-mark.d.ts +6 -0
  217. package/dist/components/opper-mark.d.ts +7 -0
  218. package/dist/components/preview-loading.d.ts +2 -0
  219. package/dist/components/provider-recovery-notice.d.ts +16 -0
  220. package/dist/components/queue-item-content.d.ts +9 -0
  221. package/dist/components/rehype-secret-refs.d.ts +23 -0
  222. package/dist/components/remark-soft-line-breaks.d.ts +38 -0
  223. package/dist/components/sandbox-files.d.ts +3 -1
  224. package/dist/components/sandbox-workspace.d.ts +18 -0
  225. package/dist/components/secret-question-field.d.ts +31 -0
  226. package/dist/components/secret-ref-chip.d.ts +35 -0
  227. package/dist/components/session-capability-frame.d.ts +37 -0
  228. package/dist/components/session-chrome.d.ts +57 -8
  229. package/dist/components/session-commands-panel.d.ts +6 -0
  230. package/dist/components/session-commands.d.ts +6 -0
  231. package/dist/components/session-connection-request.d.ts +8 -0
  232. package/dist/components/session-conversation.d.ts +157 -0
  233. package/dist/components/session-list.d.ts +39 -0
  234. package/dist/components/session-mcp-capability-card.d.ts +41 -0
  235. package/dist/components/session-proxy-scope.d.ts +35 -0
  236. package/dist/components/session-status.d.ts +4 -1
  237. package/dist/components/session-usage-limit-notice.d.ts +39 -0
  238. package/dist/components/timeline-anchor.d.ts +24 -0
  239. package/dist/components/timeline-annotation-chrome.d.ts +25 -0
  240. package/dist/components/timeline-annotation-layout.d.ts +42 -0
  241. package/dist/components/timeline-annotation-markers.d.ts +8 -0
  242. package/dist/components/timeline-annotation-reveal-context.d.ts +4 -0
  243. package/dist/components/timeline-annotation-shared.d.ts +31 -0
  244. package/dist/components/timeline-annotations-dialog.d.ts +3 -2
  245. package/dist/components/timeline-annotations.d.ts +13 -3
  246. package/dist/components/timeline-reading-progress.d.ts +11 -0
  247. package/dist/components/timeline-search.d.ts +34 -0
  248. package/dist/components/timeline-settlement.d.ts +16 -0
  249. package/dist/components/tool-action-review.d.ts +47 -0
  250. package/dist/components/tool-review-history.d.ts +25 -0
  251. package/dist/components/usage-limit-notice.d.ts +49 -0
  252. package/dist/components/usage-member-list.d.ts +91 -0
  253. package/dist/components/usage-meter.d.ts +73 -0
  254. package/dist/components/use-viewer-menu-dismiss.d.ts +2 -0
  255. package/dist/components/user-message-body.d.ts +10 -1
  256. package/dist/composer-messages.d.ts +74 -0
  257. package/dist/composer-messages.js +9 -0
  258. package/dist/composer-messages.js.map +1 -0
  259. package/dist/composer-voice-events.d.ts +8 -0
  260. package/dist/composer.d.ts +5 -4
  261. package/dist/composer.js +26 -13
  262. package/dist/computer-viewer-GMLDXPON.js +20 -0
  263. package/dist/computer-viewer-GMLDXPON.js.map +1 -0
  264. package/dist/connect-account-identity.d.ts +8 -0
  265. package/dist/connect-accounts.d.ts +15 -0
  266. package/dist/connect-chooser.d.ts +18 -0
  267. package/dist/connect-panel.d.ts +18 -0
  268. package/dist/connect-setup.d.ts +18 -0
  269. package/dist/connect.d.ts +21 -0
  270. package/dist/connect.js +2149 -0
  271. package/dist/connect.js.map +1 -0
  272. package/dist/connection-catalog.d.ts +38 -0
  273. package/dist/connection-discovery.d.ts +12 -0
  274. package/dist/connection-installed.d.ts +17 -0
  275. package/dist/connection-logo.d.ts +9 -0
  276. package/dist/connection-service-logo.d.ts +8 -0
  277. package/dist/connection-service-presentation.d.ts +13 -0
  278. package/dist/connection-skeleton.d.ts +5 -0
  279. package/dist/connection-type-picker.d.ts +6 -0
  280. package/dist/conversation-timeline.d.ts +5 -0
  281. package/dist/desktop.d.ts +7 -0
  282. package/dist/desktop.js +27 -0
  283. package/dist/desktop.js.map +1 -0
  284. package/dist/device-authorization.d.ts +23 -0
  285. package/dist/diffs.d.ts +8 -0
  286. package/dist/diffs.js +15 -0
  287. package/dist/diffs.js.map +1 -0
  288. package/dist/discovery-cache.d.ts +5 -0
  289. package/dist/editor.d.ts +7 -0
  290. package/dist/editor.js +22 -0
  291. package/dist/editor.js.map +1 -0
  292. package/dist/embedded-new-voice-OCRNT6U5.js +18 -0
  293. package/dist/embedded-new-voice-OCRNT6U5.js.map +1 -0
  294. package/dist/embedded-voice-RPHPYVBT.js +18 -0
  295. package/dist/embedded-voice-RPHPYVBT.js.map +1 -0
  296. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-SI63O5V4.js} +4 -1
  297. package/dist/fleet-decision-row-SI63O5V4.js.map +1 -0
  298. package/dist/hooks/latest-question.d.ts +25 -0
  299. package/dist/hooks/shared-machines-list.d.ts +4 -0
  300. package/dist/hooks/shared-query.d.ts +113 -0
  301. package/dist/hooks/use-attached-browsers.d.ts +1 -1
  302. package/dist/hooks/use-auth-runs.d.ts +1 -4
  303. package/dist/hooks/use-available-models.d.ts +1 -1
  304. package/dist/hooks/use-browser-frame-stream.d.ts +1 -1
  305. package/dist/hooks/use-browser-session.d.ts +12 -1
  306. package/dist/hooks/use-client-config-flags.d.ts +49 -0
  307. package/dist/hooks/use-codex-accounts.d.ts +59 -10
  308. package/dist/hooks/use-composer.d.ts +24 -2
  309. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  310. package/dist/hooks/use-computer-input-posture.d.ts +13 -0
  311. package/dist/hooks/use-computer-session.d.ts +4 -0
  312. package/dist/hooks/use-connect.d.ts +23 -0
  313. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  314. package/dist/hooks/use-file-attachments.d.ts +12 -0
  315. package/dist/hooks/use-goal.d.ts +2 -1
  316. package/dist/hooks/use-last-started-turn-policy.d.ts +5 -1
  317. package/dist/hooks/use-new-conversation.d.ts +56 -0
  318. package/dist/hooks/use-new-session-draft.d.ts +74 -0
  319. package/dist/hooks/use-realtime-voice-models.d.ts +7 -0
  320. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  321. package/dist/hooks/use-session-conversation.d.ts +49 -0
  322. package/dist/hooks/use-session-events.d.ts +26 -5
  323. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  324. package/dist/hooks/use-session-model-account.d.ts +48 -0
  325. package/dist/hooks/use-supergrok-accounts.d.ts +7 -1
  326. package/dist/hooks/use-terminal-stream.d.ts +11 -5
  327. package/dist/hooks/use-usage.d.ts +40 -0
  328. package/dist/hooks/use-vaults.d.ts +53 -0
  329. package/dist/hooks/use-voice-input.d.ts +8 -1
  330. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  331. package/dist/hooks/use-workspace-sessions.d.ts +24 -5
  332. package/dist/human-input-model.d.ts +122 -0
  333. package/dist/identity-link-accounts.d.ts +11 -0
  334. package/dist/identity-link-consent.d.ts +18 -0
  335. package/dist/index.d.ts +82 -20
  336. package/dist/index.js +2340 -6603
  337. package/dist/index.js.map +1 -1
  338. package/dist/interaction.d.ts +1 -1
  339. package/dist/interaction.js +14 -24
  340. package/dist/interaction.js.map +1 -1
  341. package/dist/latest-question-R77KWSUP.js +118 -0
  342. package/dist/latest-question-R77KWSUP.js.map +1 -0
  343. package/dist/lib/clipboard.d.ts +7 -0
  344. package/dist/lib/error-message.d.ts +7 -0
  345. package/dist/lib/format.d.ts +58 -4
  346. package/dist/lib/host-theme.d.ts +77 -0
  347. package/dist/lib/interaction-errors.d.ts +3 -0
  348. package/dist/lib/menu-styles.d.ts +22 -0
  349. package/dist/lib/notify-observer.d.ts +2 -0
  350. package/dist/lib/pierre-diffs-loader.d.ts +20 -0
  351. package/dist/lib/provider-recovery.d.ts +55 -0
  352. package/dist/lib/scrollbars.d.ts +11 -0
  353. package/dist/lib/terminal-input-readiness.d.ts +11 -0
  354. package/dist/lib/transient-retry.d.ts +25 -0
  355. package/dist/lib/workbench-peers.d.ts +36 -0
  356. package/dist/lib/xterm-theme.d.ts +7 -5
  357. package/dist/machines.js +9 -5
  358. package/dist/markdown-table-layout-3PV2UAAH.js +113 -0
  359. package/dist/markdown-table-layout-3PV2UAAH.js.map +1 -0
  360. package/dist/mcp-connection-status.d.ts +14 -0
  361. package/dist/model-mark-paths.d.ts +34 -0
  362. package/dist/model-mark-paths.js +16 -0
  363. package/dist/model-mark-paths.js.map +1 -0
  364. package/dist/model-picker-model.d.ts +111 -0
  365. package/dist/model-picker-model.js +25 -0
  366. package/dist/model-picker-model.js.map +1 -0
  367. package/dist/model-policy-picker-menu-7GJ5URPR.js +17 -0
  368. package/dist/model-policy-picker-menu-7GJ5URPR.js.map +1 -0
  369. package/dist/model-policy.d.ts +30 -3
  370. package/dist/model-policy.js +14 -1
  371. package/dist/native-previews.d.ts +28 -0
  372. package/dist/native-previews.js +82 -0
  373. package/dist/native-previews.js.map +1 -0
  374. package/dist/new-session-draft.d.ts +7 -0
  375. package/dist/new-session-draft.js +639 -0
  376. package/dist/new-session-draft.js.map +1 -0
  377. package/dist/notification-text.d.ts +27 -0
  378. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  379. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  380. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  381. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  382. package/dist/older-history.d.ts +3 -2
  383. package/dist/organization-model.d.ts +32 -0
  384. package/dist/organization-model.js +65 -0
  385. package/dist/organization-model.js.map +1 -0
  386. package/dist/{platform-activity-row-BJHUHXAT.js → platform-activity-row-5OCKOQWZ.js} +16 -78
  387. package/dist/platform-activity-row-5OCKOQWZ.js.map +1 -0
  388. package/dist/plugin-details.d.ts +14 -0
  389. package/dist/plugin-discovery.d.ts +25 -0
  390. package/dist/preview-loading-paint.d.ts +20 -0
  391. package/dist/provider.d.ts +25 -2
  392. package/dist/queue-presentation-model.d.ts +72 -0
  393. package/dist/queue-presentation-model.js +38 -0
  394. package/dist/queue-presentation-model.js.map +1 -0
  395. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-GJBH7NKO.js} +65 -265
  396. package/dist/queue-surface-implementation-GJBH7NKO.js.map +1 -0
  397. package/dist/realtime/embedded-new-voice.d.ts +1 -0
  398. package/dist/realtime/embedded-voice.d.ts +1 -0
  399. package/dist/realtime/realtime-control.d.ts +4 -65
  400. package/dist/realtime/session-realtime.d.ts +78 -0
  401. package/dist/realtime.js +25 -1333
  402. package/dist/realtime.js.map +1 -1
  403. package/dist/sandbox-files-O27IPOZW.js +21 -0
  404. package/dist/sandbox-files-O27IPOZW.js.map +1 -0
  405. package/dist/sandbox-label-model.d.ts +30 -0
  406. package/dist/sandbox-label-model.js +20 -0
  407. package/dist/sandbox-label-model.js.map +1 -0
  408. package/dist/scrollbars.d.ts +1 -0
  409. package/dist/scrollbars.js +10 -0
  410. package/dist/scrollbars.js.map +1 -0
  411. package/dist/secret-reference-label.d.ts +14 -0
  412. package/dist/secrets-ui.d.ts +57 -0
  413. package/dist/session-agents-model.d.ts +10 -0
  414. package/dist/session-agents-model.js +25 -0
  415. package/dist/session-agents-model.js.map +1 -0
  416. package/dist/session-attention-model.d.ts +8 -0
  417. package/dist/session-attention-model.js +47 -0
  418. package/dist/session-attention-model.js.map +1 -0
  419. package/dist/session-auth-recommendation.d.ts +5 -0
  420. package/dist/session-capability-policy.d.ts +31 -0
  421. package/dist/session-feedback-model.d.ts +20 -0
  422. package/dist/session-feedback-model.js +56 -0
  423. package/dist/session-feedback-model.js.map +1 -0
  424. package/dist/session-list-model.d.ts +138 -0
  425. package/dist/session-list-model.js +274 -0
  426. package/dist/session-list-model.js.map +1 -0
  427. package/dist/session-realtime-7DJOIHQW.js +21 -0
  428. package/dist/session-realtime-7DJOIHQW.js.map +1 -0
  429. package/dist/session-realtime.d.ts +7 -0
  430. package/dist/session-realtime.js +19 -0
  431. package/dist/session-realtime.js.map +1 -0
  432. package/dist/session-status-model.d.ts +36 -0
  433. package/dist/session-ui.d.ts +39 -1
  434. package/dist/session-ui.js +126 -12
  435. package/dist/session-ui.js.map +1 -1
  436. package/dist/session.d.ts +8 -2
  437. package/dist/session.js +35 -20
  438. package/dist/site-snapshot.d.ts +14 -0
  439. package/dist/sites-ui.d.ts +24 -0
  440. package/dist/sites.d.ts +3 -0
  441. package/dist/sites.js +14 -0
  442. package/dist/sites.js.map +1 -0
  443. package/dist/skill-discovery.d.ts +47 -0
  444. package/dist/terminal.d.ts +8 -0
  445. package/dist/terminal.js +32 -0
  446. package/dist/terminal.js.map +1 -0
  447. package/dist/testing/session-scenarios.d.ts +10 -0
  448. package/dist/testing/session-scenarios.js +367 -0
  449. package/dist/testing/session-scenarios.js.map +1 -0
  450. package/dist/timeline/activity-rail.d.ts +11 -2
  451. package/dist/timeline/agent-identity.d.ts +30 -0
  452. package/dist/timeline/agent-row.d.ts +31 -0
  453. package/dist/timeline/allowance-exhausted-row.d.ts +24 -0
  454. package/dist/timeline/compaction-copy.d.ts +46 -0
  455. package/dist/timeline/focus-sequence.d.ts +9 -0
  456. package/dist/timeline/genie-copy.d.ts +3 -0
  457. package/dist/timeline/genie-loading.d.ts +47 -0
  458. package/dist/timeline/index.d.ts +10 -5
  459. package/dist/timeline/knowledge-receipt.d.ts +17 -0
  460. package/dist/timeline/message-sender.d.ts +19 -0
  461. package/dist/timeline/notice-presentation.d.ts +13 -0
  462. package/dist/timeline/parsers.d.ts +11 -0
  463. package/dist/timeline/patch-apply-command.d.ts +18 -0
  464. package/dist/timeline/platform-activity-presentation.d.ts +21 -0
  465. package/dist/timeline/platform-activity-row.d.ts +1 -2
  466. package/dist/timeline/presented-image.d.ts +11 -0
  467. package/dist/timeline/projection.d.ts +31 -2
  468. package/dist/timeline/provider-logo.d.ts +15 -0
  469. package/dist/timeline/question-nav-model.d.ts +23 -0
  470. package/dist/timeline/registry.d.ts +12 -1
  471. package/dist/timeline/retained-image.d.ts +18 -0
  472. package/dist/timeline/retained-loaders.d.ts +27 -0
  473. package/dist/timeline/rolling-activity.d.ts +11 -0
  474. package/dist/timeline/screenshot-lightbox.d.ts +23 -1
  475. package/dist/timeline/session-usage-limit-row.d.ts +11 -0
  476. package/dist/timeline/shared.d.ts +4 -1
  477. package/dist/timeline/startup-dispatch-details.d.ts +5 -0
  478. package/dist/timeline/startup-preference.d.ts +3 -0
  479. package/dist/timeline/startup-timings.d.ts +4 -0
  480. package/dist/timeline/tool-display-name.d.ts +5 -2
  481. package/dist/timeline/tool-presentation.d.ts +172 -0
  482. package/dist/timeline/turn-summary-model.d.ts +95 -0
  483. package/dist/timeline/turn-summary.d.ts +20 -54
  484. package/dist/timeline/types.d.ts +179 -7
  485. package/dist/timeline/work-presentation.d.ts +60 -0
  486. package/dist/timeline-annotation-markers-U3G66KUD.js +166 -0
  487. package/dist/timeline-annotation-markers-U3G66KUD.js.map +1 -0
  488. package/dist/timeline-annotation-selection-BI7QTPTZ.js +242 -0
  489. package/dist/timeline-annotation-selection-BI7QTPTZ.js.map +1 -0
  490. package/dist/timeline-annotations-dialog-FOMHSDTE.js +274 -0
  491. package/dist/timeline-annotations-dialog-FOMHSDTE.js.map +1 -0
  492. package/dist/timeline-model.d.ts +29 -0
  493. package/dist/timeline-model.js +254 -0
  494. package/dist/timeline-model.js.map +1 -0
  495. package/dist/usage/allowance-copy.d.ts +25 -0
  496. package/dist/usage/session-usage-limit.d.ts +88 -0
  497. package/dist/usage/summary.d.ts +35 -0
  498. package/dist/usage.d.ts +17 -0
  499. package/dist/usage.js +1187 -0
  500. package/dist/usage.js.map +1 -0
  501. package/dist/voice-recording-owner.d.ts +7 -0
  502. package/package.json +106 -7
  503. package/src/approvals.ts +8 -1
  504. package/src/artifacts-spreadsheet.ts +1 -0
  505. package/src/artifacts.ts +44 -0
  506. package/src/assets/README.md +8 -0
  507. package/src/assets/gmail.ico +0 -0
  508. package/src/assets/outlook.ico +0 -0
  509. package/src/capability-catalog-row.tsx +94 -0
  510. package/src/capability-logo-fallback.ts +22 -0
  511. package/src/client.ts +20 -31
  512. package/src/commands/registry.ts +18 -15
  513. package/src/commands/types.ts +2 -0
  514. package/src/components/anthropic-mark.tsx +10 -0
  515. package/src/components/approval-surface.tsx +401 -138
  516. package/src/components/artifacts/artifact-chrome.tsx +413 -0
  517. package/src/components/artifacts/artifact-sandbox.tsx +193 -0
  518. package/src/components/artifacts/artifact-surface.tsx +40 -20
  519. package/src/components/artifacts/chat-interactive-block.tsx +260 -0
  520. package/src/components/artifacts/deferred-chat-media.tsx +67 -0
  521. package/src/components/artifacts/document-editor.tsx +141 -16
  522. package/src/components/artifacts/editable-artifact-ui.tsx +4 -2
  523. package/src/components/artifacts/editable-artifact-view.tsx +197 -0
  524. package/src/components/artifacts/editable-artifact-workbench.tsx +5 -2
  525. package/src/components/artifacts/editable-document.tsx +37 -36
  526. package/src/components/artifacts/editable-presentation.tsx +36 -18
  527. package/src/components/artifacts/editable-spreadsheet.tsx +543 -103
  528. package/src/components/artifacts/index.ts +45 -0
  529. package/src/components/artifacts/inline-html-document.ts +24 -0
  530. package/src/components/artifacts/latest-task-runner.ts +49 -0
  531. package/src/components/artifacts/presentation-editor.tsx +323 -67
  532. package/src/components/artifacts/published-html-artifact-frame.tsx +122 -5
  533. package/src/components/artifacts/session-artifact-viewer.tsx +378 -0
  534. package/src/components/artifacts/site-view.tsx +121 -0
  535. package/src/components/artifacts/spreadsheet-canvas.ts +5 -10
  536. package/src/components/artifacts/spreadsheet-command-queue.ts +184 -0
  537. package/src/components/artifacts/spreadsheet-download.tsx +109 -0
  538. package/src/components/artifacts/spreadsheet-general-display.ts +43 -0
  539. package/src/components/artifacts/spreadsheet-grid.tsx +344 -144
  540. package/src/components/artifacts/spreadsheet-resize.tsx +421 -0
  541. package/src/components/artifacts/spreadsheet-submissions.tsx +46 -0
  542. package/src/components/artifacts/visualization-assets/base.css +1015 -0
  543. package/src/components/artifacts/visualization-assets/helpers.html +349 -0
  544. package/src/components/artifacts/visualization-assets.gen.ts +3 -0
  545. package/src/components/browser-select-control.tsx +312 -0
  546. package/src/components/browser-viewer.tsx +778 -318
  547. package/src/components/chat-composer.tsx +47 -5
  548. package/src/components/child-session-link.tsx +36 -0
  549. package/src/components/claude-mark.tsx +11 -0
  550. package/src/components/code-editor.tsx +13 -16
  551. package/src/components/composer-transcription-control.tsx +128 -30
  552. package/src/components/composer.tsx +404 -195
  553. package/src/components/computer-viewer.tsx +880 -253
  554. package/src/components/copy-button.tsx +13 -5
  555. package/src/components/desktop-viewer.tsx +1 -1
  556. package/src/components/enrollment-consent.tsx +1 -1
  557. package/src/components/enrollment-device-flow.tsx +1 -1
  558. package/src/components/file-browser.tsx +10 -14
  559. package/src/components/fleet-tile.tsx +5 -3
  560. package/src/components/generated-video-player.tsx +4 -2
  561. package/src/components/grok-mark.tsx +34 -0
  562. package/src/components/human-input-form.tsx +590 -283
  563. package/src/components/human-input-surface.tsx +11 -0
  564. package/src/components/interaction-intervention-banner.tsx +2 -2
  565. package/src/components/linkified-text.tsx +79 -0
  566. package/src/components/machine-card.tsx +35 -78
  567. package/src/components/machine-dock-bar.tsx +1 -1
  568. package/src/components/machine-input-display.ts +197 -3
  569. package/src/components/machines/agent-status-icon.tsx +31 -0
  570. package/src/components/machines/machine-detail.tsx +200 -31
  571. package/src/components/machines/machine-operation-policy.tsx +1 -1
  572. package/src/components/machines/machine-status.ts +241 -0
  573. package/src/components/machines-dashboard.tsx +2 -17
  574. package/src/components/markdown-table-layout.ts +143 -0
  575. package/src/components/markdown.tsx +466 -129
  576. package/src/components/message-timeline.tsx +2370 -667
  577. package/src/components/model-mark.tsx +102 -0
  578. package/src/components/model-picker.tsx +15 -4
  579. package/src/components/model-policy-picker-menu.tsx +371 -0
  580. package/src/components/model-policy-picker.tsx +252 -429
  581. package/src/components/new-conversation.tsx +221 -0
  582. package/src/components/open-geni-chat.tsx +309 -0
  583. package/src/components/open-geni-links.tsx +179 -0
  584. package/src/components/openrouter-mark.tsx +13 -0
  585. package/src/components/opper-mark.tsx +18 -0
  586. package/src/components/pierre-diff.tsx +41 -16
  587. package/src/components/pierre-file.tsx +35 -10
  588. package/src/components/preview-loading.tsx +109 -0
  589. package/src/components/provider-recovery-notice.tsx +49 -0
  590. package/src/components/queue-item-content.ts +14 -0
  591. package/src/components/queue-surface-implementation.tsx +51 -356
  592. package/src/components/queue-surface-state.tsx +3 -1
  593. package/src/components/rehype-secret-refs.ts +98 -0
  594. package/src/components/remark-soft-line-breaks.ts +166 -0
  595. package/src/components/sandbox-files.tsx +91 -15
  596. package/src/components/sandbox-terminal.tsx +84 -13
  597. package/src/components/sandbox-workspace.tsx +147 -51
  598. package/src/components/secret-question-field.tsx +324 -0
  599. package/src/components/secret-ref-chip.tsx +185 -0
  600. package/src/components/session-capability-frame.tsx +197 -0
  601. package/src/components/session-chrome.tsx +842 -251
  602. package/src/components/session-commands-panel.tsx +126 -0
  603. package/src/components/session-commands.tsx +18 -0
  604. package/src/components/session-connection-request.tsx +97 -0
  605. package/src/components/session-conversation.tsx +876 -0
  606. package/src/components/session-list.tsx +257 -0
  607. package/src/components/session-mcp-capability-card.tsx +680 -0
  608. package/src/components/session-proxy-scope.tsx +153 -0
  609. package/src/components/session-status.tsx +26 -17
  610. package/src/components/session-usage-limit-notice.tsx +174 -0
  611. package/src/components/timeline-anchor.tsx +119 -0
  612. package/src/components/timeline-annotation-chrome.tsx +196 -0
  613. package/src/components/timeline-annotation-layout.ts +268 -0
  614. package/src/components/timeline-annotation-markers.tsx +185 -0
  615. package/src/components/timeline-annotation-reveal-context.ts +9 -0
  616. package/src/components/timeline-annotation-selection.tsx +106 -54
  617. package/src/components/timeline-annotation-shared.ts +245 -0
  618. package/src/components/timeline-annotations-dialog.tsx +185 -128
  619. package/src/components/timeline-annotations.tsx +189 -45
  620. package/src/components/timeline-reading-progress.ts +80 -0
  621. package/src/components/timeline-search.tsx +168 -0
  622. package/src/components/timeline-settlement.ts +60 -0
  623. package/src/components/tool-action-review.tsx +650 -0
  624. package/src/components/tool-review-history.tsx +88 -0
  625. package/src/components/usage-limit-notice.tsx +179 -0
  626. package/src/components/usage-member-list.tsx +792 -0
  627. package/src/components/usage-meter.tsx +382 -0
  628. package/src/components/use-viewer-menu-dismiss.ts +33 -0
  629. package/src/components/user-message-body.tsx +27 -2
  630. package/src/components/workspace-dock.tsx +18 -1
  631. package/src/composer-messages.ts +150 -0
  632. package/src/composer-voice-events.ts +12 -0
  633. package/src/composer.ts +6 -1
  634. package/src/connect-account-identity.tsx +34 -0
  635. package/src/connect-accounts.tsx +233 -0
  636. package/src/connect-chooser.tsx +336 -0
  637. package/src/connect-panel.tsx +99 -0
  638. package/src/connect-setup.tsx +355 -0
  639. package/src/connect.ts +60 -0
  640. package/src/connection-assets.d.ts +4 -0
  641. package/src/connection-catalog.tsx +162 -0
  642. package/src/connection-discovery.tsx +238 -0
  643. package/src/connection-installed.tsx +51 -0
  644. package/src/connection-logo.tsx +37 -0
  645. package/src/connection-service-logo.tsx +55 -0
  646. package/src/connection-service-presentation.ts +46 -0
  647. package/src/connection-skeleton.tsx +16 -0
  648. package/src/connection-type-picker.tsx +36 -0
  649. package/src/conversation-timeline.ts +92 -0
  650. package/src/desktop.ts +24 -0
  651. package/src/device-authorization.tsx +98 -0
  652. package/src/diffs.ts +16 -0
  653. package/src/discovery-cache.ts +42 -0
  654. package/src/editor.ts +13 -0
  655. package/src/hooks/internal.ts +3 -2
  656. package/src/hooks/latest-question.ts +164 -0
  657. package/src/hooks/shared-machines-list.ts +34 -1
  658. package/src/hooks/shared-query.ts +721 -0
  659. package/src/hooks/use-attached-browsers.ts +1 -1
  660. package/src/hooks/use-auth-runs.ts +0 -27
  661. package/src/hooks/use-available-models.ts +2 -2
  662. package/src/hooks/use-browser-frame-stream.ts +19 -2
  663. package/src/hooks/use-browser-session.ts +680 -205
  664. package/src/hooks/use-browser-sessions.ts +32 -1
  665. package/src/hooks/use-client-config-flags.ts +118 -0
  666. package/src/hooks/use-codex-accounts.ts +247 -47
  667. package/src/hooks/use-composer.ts +227 -44
  668. package/src/hooks/use-computer-frame-stream.ts +25 -7
  669. package/src/hooks/use-computer-input-posture.ts +94 -0
  670. package/src/hooks/use-computer-session.ts +409 -95
  671. package/src/hooks/use-connect.ts +23 -0
  672. package/src/hooks/use-desktop-stream.ts +19 -28
  673. package/src/hooks/use-file-attachments.ts +80 -4
  674. package/src/hooks/use-goal.ts +79 -9
  675. package/src/hooks/use-last-started-turn-policy.ts +37 -11
  676. package/src/hooks/use-machines.ts +4 -0
  677. package/src/hooks/use-new-conversation.ts +374 -0
  678. package/src/hooks/use-new-session-draft.ts +813 -0
  679. package/src/hooks/use-realtime-voice-models.ts +46 -0
  680. package/src/hooks/use-sandbox-files.ts +68 -20
  681. package/src/hooks/use-session-background-commands.ts +22 -11
  682. package/src/hooks/use-session-capabilities.ts +3 -2
  683. package/src/hooks/use-session-conversation.ts +150 -0
  684. package/src/hooks/use-session-events.ts +531 -335
  685. package/src/hooks/use-session-mcp-approval-policy.ts +13 -5
  686. package/src/hooks/use-session-model-account.ts +174 -0
  687. package/src/hooks/use-session.ts +30 -2
  688. package/src/hooks/use-slash-commands.ts +6 -11
  689. package/src/hooks/use-supergrok-accounts.ts +22 -8
  690. package/src/hooks/use-terminal-stream.ts +153 -27
  691. package/src/hooks/use-transcription.ts +6 -0
  692. package/src/hooks/use-turn-queue.ts +2 -1
  693. package/src/hooks/use-usage.ts +100 -0
  694. package/src/hooks/use-vaults.ts +153 -0
  695. package/src/hooks/use-video-artifact-playback.ts +1 -1
  696. package/src/hooks/use-voice-input.ts +127 -19
  697. package/src/hooks/use-workspace-capture.ts +3 -2
  698. package/src/hooks/use-workspace-edit.ts +23 -5
  699. package/src/hooks/use-workspace-sessions.ts +74 -7
  700. package/src/human-input-model.ts +359 -0
  701. package/src/identity-link-accounts.tsx +148 -0
  702. package/src/identity-link-consent.tsx +192 -0
  703. package/src/index.ts +262 -16
  704. package/src/interaction.ts +1 -0
  705. package/src/lib/clipboard.ts +29 -0
  706. package/src/lib/error-message.ts +37 -0
  707. package/src/lib/format.ts +205 -10
  708. package/src/lib/host-theme.ts +529 -0
  709. package/src/lib/interaction-errors.ts +15 -0
  710. package/src/lib/menu-styles.ts +44 -0
  711. package/src/lib/notify-observer.ts +15 -0
  712. package/src/lib/pierre-diffs-loader.ts +53 -0
  713. package/src/lib/provider-recovery.ts +243 -0
  714. package/src/lib/scrollbars.ts +30 -0
  715. package/src/lib/terminal-input-readiness.ts +51 -0
  716. package/src/lib/transient-retry.ts +99 -0
  717. package/src/lib/workbench-peers.ts +62 -0
  718. package/src/lib/xterm-theme.ts +23 -21
  719. package/src/mcp-connection-status.ts +35 -0
  720. package/src/model-mark-paths.ts +53 -0
  721. package/src/model-picker-model.ts +330 -0
  722. package/src/model-policy.ts +299 -74
  723. package/src/native-previews.ts +100 -0
  724. package/src/new-session-draft.ts +22 -0
  725. package/src/notification-text.ts +81 -0
  726. package/src/older-history.ts +22 -4
  727. package/src/organization-model.ts +118 -0
  728. package/src/plugin-details.tsx +185 -0
  729. package/src/plugin-discovery.tsx +196 -0
  730. package/src/preview-loading-paint.ts +68 -0
  731. package/src/provider.tsx +74 -15
  732. package/src/queue-presentation-model.ts +464 -0
  733. package/src/realtime/dropdown-menu.tsx +19 -12
  734. package/src/realtime/embedded-new-voice.tsx +1 -0
  735. package/src/realtime/embedded-voice.tsx +3 -0
  736. package/src/realtime/realtime-control.tsx +138 -550
  737. package/src/realtime/session-realtime.ts +566 -0
  738. package/src/sandbox-label-model.ts +53 -0
  739. package/src/scrollbars.ts +3 -0
  740. package/src/secret-reference-label.ts +43 -0
  741. package/src/secrets-ui.tsx +59 -0
  742. package/src/session-agents-model.ts +33 -0
  743. package/src/session-attention-model.ts +62 -0
  744. package/src/session-auth-recommendation.ts +48 -0
  745. package/src/session-capability-policy.ts +196 -0
  746. package/src/session-context.ts +3 -4
  747. package/src/session-feedback-model.ts +95 -0
  748. package/src/session-list-model.ts +434 -0
  749. package/src/session-realtime.ts +18 -0
  750. package/src/session-status-model.ts +99 -0
  751. package/src/session-ui.ts +98 -1
  752. package/src/session.ts +17 -1
  753. package/src/site-snapshot.ts +74 -0
  754. package/src/sites-ui.tsx +348 -0
  755. package/src/sites.ts +3 -0
  756. package/src/skill-discovery.tsx +208 -0
  757. package/src/terminal.ts +26 -0
  758. package/src/testing/session-scenarios.ts +404 -0
  759. package/src/timeline/activity-rail.tsx +242 -101
  760. package/src/timeline/agent-identity.tsx +84 -0
  761. package/src/timeline/agent-row.tsx +231 -0
  762. package/src/timeline/allowance-exhausted-row.tsx +86 -0
  763. package/src/timeline/compaction-copy.ts +115 -0
  764. package/src/timeline/fleet-decision-projection.ts +2 -1
  765. package/src/timeline/fleet-decision-row.tsx +1 -0
  766. package/src/timeline/focus-sequence.ts +44 -0
  767. package/src/timeline/genie-copy.ts +19 -0
  768. package/src/timeline/genie-loading.tsx +143 -0
  769. package/src/timeline/index.ts +30 -2
  770. package/src/timeline/knowledge-receipt.tsx +100 -0
  771. package/src/timeline/message-sender.tsx +71 -0
  772. package/src/timeline/notice-presentation.ts +48 -0
  773. package/src/timeline/parsers.ts +42 -2
  774. package/src/timeline/patch-apply-command.tsx +135 -0
  775. package/src/timeline/platform-activity-presentation.ts +166 -0
  776. package/src/timeline/platform-activity-row.tsx +15 -90
  777. package/src/timeline/presented-image.ts +63 -0
  778. package/src/timeline/projection.ts +1742 -76
  779. package/src/timeline/provider-logo.tsx +53 -0
  780. package/src/timeline/question-nav-model.ts +28 -0
  781. package/src/timeline/registry.ts +26 -4
  782. package/src/timeline/retained-image.ts +97 -0
  783. package/src/timeline/retained-loaders.ts +91 -0
  784. package/src/timeline/rolling-activity.tsx +80 -0
  785. package/src/timeline/screenshot-lightbox.tsx +228 -5
  786. package/src/timeline/session-usage-limit-row.tsx +42 -0
  787. package/src/timeline/shared.tsx +42 -15
  788. package/src/timeline/startup-dispatch-details.tsx +36 -0
  789. package/src/timeline/startup-preference.ts +33 -0
  790. package/src/timeline/startup-timings.tsx +145 -0
  791. package/src/timeline/tool-display-name.ts +19 -7
  792. package/src/timeline/tool-presentation.ts +1316 -0
  793. package/src/timeline/tool-renderers.tsx +835 -1138
  794. package/src/timeline/turn-summary-model.ts +343 -0
  795. package/src/timeline/turn-summary.tsx +272 -365
  796. package/src/timeline/types.ts +189 -6
  797. package/src/timeline/work-presentation.ts +189 -0
  798. package/src/timeline-model.ts +143 -0
  799. package/src/usage/allowance-copy.ts +64 -0
  800. package/src/usage/session-usage-limit.ts +266 -0
  801. package/src/usage/summary.ts +93 -0
  802. package/src/usage.ts +70 -0
  803. package/src/voice-recording-owner.ts +22 -0
  804. package/styles/compiled.css +2319 -748
  805. package/styles/connect.css +290 -0
  806. package/styles/connect.d.ts +1 -0
  807. package/styles/effective-tokens.css +1 -0
  808. package/styles/index.css +309 -9
  809. package/styles/responsive.css +99 -7
  810. package/styles/session-capability.css +44 -0
  811. package/styles/tokens.css +134 -66
  812. package/dist/browser-viewer-MKKFRRRV.js +0 -14
  813. package/dist/chunk-2DZVYHVO.js +0 -470
  814. package/dist/chunk-2DZVYHVO.js.map +0 -1
  815. package/dist/chunk-3AGBGTRT.js.map +0 -1
  816. package/dist/chunk-4MDLEHXL.js.map +0 -1
  817. package/dist/chunk-5YK2ZDIG.js.map +0 -1
  818. package/dist/chunk-7EAFGICR.js +0 -302
  819. package/dist/chunk-7EAFGICR.js.map +0 -1
  820. package/dist/chunk-ELIYWBZR.js.map +0 -1
  821. package/dist/chunk-H5YGYNXS.js.map +0 -1
  822. package/dist/chunk-HUIKIDM5.js.map +0 -1
  823. package/dist/chunk-ICA6R6TW.js.map +0 -1
  824. package/dist/chunk-IOLVN74D.js.map +0 -1
  825. package/dist/chunk-JAQ5K7W6.js.map +0 -1
  826. package/dist/chunk-JVYQRRC4.js.map +0 -1
  827. package/dist/chunk-KCGXPP5V.js.map +0 -1
  828. package/dist/chunk-KZ73RL76.js.map +0 -1
  829. package/dist/chunk-NEXLLTP4.js.map +0 -1
  830. package/dist/chunk-PEI6RANI.js.map +0 -1
  831. package/dist/chunk-VTOREEXF.js +0 -142
  832. package/dist/chunk-VTOREEXF.js.map +0 -1
  833. package/dist/chunk-WSK7G5LE.js +0 -170
  834. package/dist/chunk-WSK7G5LE.js.map +0 -1
  835. package/dist/chunk-X4A66EWZ.js +0 -223
  836. package/dist/chunk-X4A66EWZ.js.map +0 -1
  837. package/dist/chunk-XYRDDSXF.js.map +0 -1
  838. package/dist/chunk-YG4QEQF5.js +0 -9557
  839. package/dist/chunk-YG4QEQF5.js.map +0 -1
  840. package/dist/chunk-YVCHAQZY.js.map +0 -1
  841. package/dist/chunk-ZPBTI3TE.js.map +0 -1
  842. package/dist/computer-viewer-FWEZISW4.js +0 -16
  843. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
  844. package/dist/hooks/use-environments.d.ts +0 -41
  845. package/dist/hooks/use-packs.d.ts +0 -32
  846. package/dist/platform-activity-row-BJHUHXAT.js.map +0 -1
  847. package/dist/queue-surface-implementation-BSKUWHRI.js.map +0 -1
  848. package/dist/timeline-annotation-selection-3L7LHGG2.js +0 -187
  849. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +0 -1
  850. package/dist/timeline-annotations-dialog-U7EVLR75.js +0 -196
  851. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +0 -1
  852. package/src/hooks/use-environments.ts +0 -182
  853. package/src/hooks/use-packs.ts +0 -157
  854. /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-ZWR6MWJ7.js.map} +0 -0
  855. /package/dist/{computer-viewer-FWEZISW4.js.map → chat-interactive-block-4EUKJXUO.js.map} +0 -0
@@ -1,13 +1,25 @@
1
+ "use client";
1
2
  import {
2
3
  InteractionInterventionBanner,
4
+ isInteractionControlUnavailable,
3
5
  isNonRetryableInteractionError,
4
6
  isSourcePlacementChangedError,
5
7
  useInteractionInterventions,
6
- useInteractionInvalidation
7
- } from "./chunk-H5YGYNXS.js";
8
+ useInteractionInvalidation,
9
+ useViewerMenuDismiss
10
+ } from "./chunk-P4BNUO57.js";
11
+ import {
12
+ copyTextToClipboard
13
+ } from "./chunk-PEHTX3V7.js";
8
14
  import {
9
15
  DesktopViewer
10
- } from "./chunk-4MDLEHXL.js";
16
+ } from "./chunk-IMSUVYQB.js";
17
+ import {
18
+ usePageLiveActivity
19
+ } from "./chunk-QHUE7E4N.js";
20
+ import {
21
+ useEmbeddedComputerInteraction
22
+ } from "./chunk-G76LSA76.js";
11
23
  import {
12
24
  STREAM_CLOSE_REASON_NORMAL,
13
25
  STREAM_KIND_COMPUTER,
@@ -16,19 +28,10 @@ import {
16
28
  decodeStreamOpenAck,
17
29
  encodeStreamClose,
18
30
  encodeStreamOpen
19
- } from "./chunk-5YK2ZDIG.js";
20
- import {
21
- usePageLiveActivity
22
- } from "./chunk-KZ73RL76.js";
23
- import {
24
- useEmbeddedComputerInteraction
25
- } from "./chunk-NEXLLTP4.js";
26
- import {
27
- copyTextToClipboard
28
- } from "./chunk-JVYQRRC4.js";
31
+ } from "./chunk-TNEZNMSN.js";
29
32
  import {
30
33
  cn
31
- } from "./chunk-22KP6LR5.js";
34
+ } from "./chunk-TXS65BB2.js";
32
35
 
33
36
  // src/components/computer-viewer.tsx
34
37
  import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
@@ -46,11 +49,12 @@ import {
46
49
  RotateCcwIcon
47
50
  } from "lucide-react";
48
51
  import {
49
- useCallback as useCallback4,
50
- useEffect as useEffect4,
52
+ useCallback as useCallback5,
53
+ useEffect as useEffect5,
54
+ useLayoutEffect,
51
55
  useMemo as useMemo2,
52
- useRef as useRef4,
53
- useState as useState4
56
+ useRef as useRef5,
57
+ useState as useState5
54
58
  } from "react";
55
59
 
56
60
  // src/hooks/use-computer-frame-stream.ts
@@ -144,9 +148,18 @@ function useComputerFrameStream(options) {
144
148
  socketHandlers = null;
145
149
  pendingFrame = null;
146
150
  };
151
+ const clearRecoveryTimers = () => {
152
+ if (reconnectTimer) clearTimeout(reconnectTimer);
153
+ if (expiryTimer) clearTimeout(expiryTimer);
154
+ reconnectTimer = null;
155
+ expiryTimer = null;
156
+ };
147
157
  const scheduleReconnect = () => {
148
158
  if (disposed || reconnectTimer) return;
149
- if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) return;
159
+ if (failures >= MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME) {
160
+ clearRecoveryTimers();
161
+ return;
162
+ }
150
163
  const delay = Math.min(5e3, 250 * 2 ** Math.min(failures, 5));
151
164
  failures += 1;
152
165
  setResult((current) => ({ ...current, state: "reconnecting" }));
@@ -161,6 +174,7 @@ function useComputerFrameStream(options) {
161
174
  setResult((current) => ({ ...current, state: "error", error }));
162
175
  clearSocket();
163
176
  if (reconnectAutomatically) scheduleReconnect();
177
+ else clearRecoveryTimers();
164
178
  };
165
179
  const onOpen = (source) => {
166
180
  if (disposed || source !== socket) return;
@@ -264,8 +278,7 @@ function useComputerFrameStream(options) {
264
278
  };
265
279
  const onClose = (source) => {
266
280
  if (disposed || source !== socket) return;
267
- clearSocket();
268
- scheduleReconnect();
281
+ fail(new Error("Desktop view lost connection."));
269
282
  };
270
283
  const connectSocket = () => {
271
284
  if (disposed) return;
@@ -320,13 +333,14 @@ function useComputerFrameStream(options) {
320
333
  expiresInSeconds: 120,
321
334
  ...streamRef.current ? { stream: streamRef.current } : {}
322
335
  },
323
- { signal: attachmentAbort.signal }
336
+ { signal: attachmentAbort.signal, timeoutMs: 15e3 }
324
337
  );
325
338
  if (disposed) return;
326
339
  if (attachment.computerSessionId !== computerSessionId || attachment.targetId !== targetId) {
327
340
  throw new Error("desktop attachment does not match the requested resource");
328
341
  }
329
342
  controllerGeneration = attachment.controllerGeneration;
343
+ latestRef.current = { key: "", sequence: -1 };
330
344
  activeAttachment = attachment;
331
345
  activeStream = attachment.stream;
332
346
  attachmentExpiresAt = Date.parse(attachment.expiresAt);
@@ -339,6 +353,7 @@ function useComputerFrameStream(options) {
339
353
  computerSessionId: attachment.computerSessionId,
340
354
  controllerGeneration: attachment.controllerGeneration,
341
355
  targetId: attachment.targetId,
356
+ inputAllowed: attachment.inputAllowed,
342
357
  stream: attachment.stream,
343
358
  expiresAt: attachment.expiresAt
344
359
  },
@@ -364,8 +379,7 @@ function useComputerFrameStream(options) {
364
379
  disposed = true;
365
380
  pendingFrame = null;
366
381
  attachmentAbort.abort();
367
- if (reconnectTimer) clearTimeout(reconnectTimer);
368
- if (expiryTimer) clearTimeout(expiryTimer);
382
+ clearRecoveryTimers();
369
383
  clearSocket(true);
370
384
  };
371
385
  }, [client, enabled, nonce, options.computerSessionId, options.targetId, workspaceId]);
@@ -394,27 +408,49 @@ function isPlacementGenerationLossError(cause) {
394
408
 
395
409
  // src/hooks/use-computer-session.ts
396
410
  import { useCallback as useCallback2, useEffect as useEffect2, useRef as useRef2, useState as useState2 } from "react";
411
+ var actionResultFences = /* @__PURE__ */ new WeakMap();
412
+ function isStaleComputerActionResult(value) {
413
+ return typeof value === "object" && value !== null && actionResultFences.get(value)?.() === false;
414
+ }
415
+ function computerActionReceiptError(receipt) {
416
+ const error = new Error(receipt.error?.message ?? "Desktop input did not complete.");
417
+ const fence = actionResultFences.get(receipt);
418
+ if (fence) actionResultFences.set(error, fence);
419
+ return error;
420
+ }
397
421
  function useComputerSession(options) {
398
422
  const { client, workspaceId } = useEmbeddedComputerInteraction(options);
399
423
  const computerSessionId = options.computerSessionId;
400
424
  const enabled = (options.enabled ?? true) && computerSessionId !== null;
425
+ const sourceRef = useRef2({
426
+ client,
427
+ workspaceId,
428
+ computerSessionId,
429
+ enabled
430
+ });
431
+ if (sourceRef.current.client !== client || sourceRef.current.workspaceId !== workspaceId || sourceRef.current.computerSessionId !== computerSessionId || sourceRef.current.enabled !== enabled) {
432
+ sourceRef.current = { client, workspaceId, computerSessionId, enabled };
433
+ }
434
+ const source = sourceRef.current;
401
435
  const pageLive = usePageLiveActivity();
402
436
  const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
403
- const [state, setState] = useState2(
404
- () => emptyState(computerSessionId, enabled)
405
- );
406
- const visible = state.computerSessionId === computerSessionId ? state : emptyState(computerSessionId, enabled);
437
+ const [state, setState] = useState2(() => emptyState(source, enabled));
438
+ const visible = state.source === source ? state : emptyState(source, enabled);
407
439
  const refreshBlocked = isNonRetryableInteractionError(visible.error);
408
440
  const selectedTargetIdRef = useRef2(visible.selectedTargetId);
409
441
  selectedTargetIdRef.current = visible.selectedTargetId;
442
+ const selectionRevisionRef = useRef2(0);
443
+ const actionResultOrderRef = useRef2({ next: 0, settled: 0 });
410
444
  const targetsRef = useRef2({
445
+ source,
411
446
  computerSessionId,
412
447
  targets: visible.targets
413
448
  });
414
- const observationRef = useRef2({ computerSessionId, observation: visible.observation });
415
- if (targetsRef.current.computerSessionId !== computerSessionId) {
416
- targetsRef.current = { computerSessionId, targets: visible.targets };
449
+ const observationRef = useRef2({ source, computerSessionId, observation: visible.observation });
450
+ if (targetsRef.current.source !== source) {
451
+ targetsRef.current = { source, computerSessionId, targets: visible.targets };
417
452
  observationRef.current = {
453
+ source,
418
454
  computerSessionId,
419
455
  observation: visible.observation
420
456
  };
@@ -428,20 +464,26 @@ function useComputerSession(options) {
428
464
  });
429
465
  const mutationRef = useRef2({
430
466
  computerSessionId,
467
+ source,
431
468
  count: 0
432
469
  });
433
470
  const mountedRef = useRef2(true);
471
+ const isCurrentSource = useCallback2(
472
+ () => mountedRef.current && sourceRef.current === source,
473
+ [source]
474
+ );
434
475
  const invalidateRefresh = useCallback2(() => {
435
476
  const id = requestRef.current.id + 1;
436
477
  requestRef.current.controller?.abort();
437
478
  requestRef.current = { id, controller: null };
438
479
  }, []);
439
480
  const refresh = useCallback2(async () => {
440
- if (!enabled || !computerSessionId) return;
481
+ if (!enabled || !computerSessionId || !isCurrentSource()) return;
441
482
  const id = requestRef.current.id + 1;
442
483
  requestRef.current.controller?.abort();
443
484
  const controller = new AbortController();
444
485
  requestRef.current = { id, controller };
486
+ let discoveryCompleted = false;
445
487
  try {
446
488
  const [session, targetResponse] = await Promise.all([
447
489
  client.getComputerSession(workspaceId, computerSessionId, {
@@ -451,56 +493,84 @@ function useComputerSession(options) {
451
493
  signal: controller.signal
452
494
  })
453
495
  ]);
454
- if (!mountedRef.current || requestRef.current.id !== id) return;
496
+ discoveryCompleted = true;
497
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
455
498
  const targets = sortComputerTargets(targetResponse.targets);
456
- const selected = chooseTarget(targets, selectedTargetIdRef.current, session.platform);
499
+ const selected = chooseTarget(targets, selectedTargetIdRef.current);
500
+ const selectedId = selected?.id ?? selectedTargetIdRef.current;
501
+ const previous = observationRef.current.source === source ? observationRef.current.observation : null;
502
+ const retainedObservation = selected && previous?.target.id === selected.id && previous.target.controllerGeneration === selected.controllerGeneration && previous.target.targetGeneration === selected.targetGeneration ? previous : null;
503
+ selectedTargetIdRef.current = selectedId;
504
+ targetsRef.current = { source, computerSessionId, targets };
505
+ observationRef.current = { source, computerSessionId, observation: retainedObservation };
506
+ setState(
507
+ (current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
508
+ ...current,
509
+ session,
510
+ targets,
511
+ selectedTargetId: selectedId,
512
+ observation: retainedObservation,
513
+ loading: false,
514
+ error: null,
515
+ controlError: null
516
+ } : current
517
+ );
457
518
  const observation = selected ? await client.observeComputerTarget(workspaceId, computerSessionId, selected.id, {
458
519
  signal: controller.signal
459
520
  }) : null;
460
- if (!mountedRef.current || requestRef.current.id !== id) return;
461
- selectedTargetIdRef.current = selected?.id ?? null;
462
- targetsRef.current = { computerSessionId, targets };
463
- observationRef.current = { computerSessionId, observation };
521
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
522
+ selectedTargetIdRef.current = selectedId;
523
+ targetsRef.current = { source, computerSessionId, targets };
524
+ observationRef.current = { source, computerSessionId, observation };
464
525
  setState(
465
- (current) => current.computerSessionId === computerSessionId ? {
526
+ (current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
466
527
  ...current,
467
528
  session,
468
529
  targets,
469
- selectedTargetId: selected?.id ?? null,
530
+ selectedTargetId: selectedId,
470
531
  observation,
471
532
  loading: false,
472
533
  error: null
473
534
  } : current
474
535
  );
475
536
  } catch (cause) {
476
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
537
+ if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
538
+ const error = cause instanceof Error ? cause : new Error(String(cause));
477
539
  setState(
478
- (current) => current.computerSessionId === computerSessionId ? {
540
+ (current) => isCurrentSource() && current.source === source && current.computerSessionId === computerSessionId ? {
479
541
  ...current,
480
542
  loading: false,
481
- error: cause instanceof Error ? cause : new Error(String(cause))
543
+ error,
544
+ controlError: isInteractionControlUnavailable(
545
+ error,
546
+ discoveryCompleted ? "observation" : "control"
547
+ ) ? error : current.controlError
482
548
  } : current
483
549
  );
484
550
  } finally {
485
551
  if (requestRef.current.id === id) requestRef.current = { id, controller: null };
486
552
  }
487
- }, [client, computerSessionId, enabled, workspaceId]);
553
+ }, [client, computerSessionId, enabled, isCurrentSource, source, workspaceId]);
488
554
  useEffect2(() => {
489
555
  mountedRef.current = true;
490
556
  if (!enabled) {
491
557
  invalidateRefresh();
492
- setState(emptyState(computerSessionId, false));
558
+ setState(emptyState(source, false));
493
559
  return;
494
560
  }
495
561
  setState(
496
- (current) => current.computerSessionId === computerSessionId ? { ...current, loading: true } : emptyState(computerSessionId, true)
562
+ (current) => current.source === source ? {
563
+ ...current,
564
+ loading: true,
565
+ mutating: mutationRef.current.source === source && mutationRef.current.count > 0
566
+ } : emptyState(source, true)
497
567
  );
498
568
  void refresh();
499
569
  return () => {
500
570
  mountedRef.current = false;
501
571
  invalidateRefresh();
502
572
  };
503
- }, [computerSessionId, enabled, invalidateRefresh, refresh]);
573
+ }, [computerSessionId, enabled, invalidateRefresh, refresh, source]);
504
574
  useEffect2(() => {
505
575
  if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
506
576
  const timer = setInterval(() => {
@@ -523,47 +593,48 @@ function useComputerSession(options) {
523
593
  }, [client, computerSessionId, enabled, pageLive, refresh, refreshBlocked, workspaceId]);
524
594
  const runMutation = useCallback2(
525
595
  async (scopeComputerSessionId, operation) => {
596
+ if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
526
597
  invalidateRefresh();
527
- if (mutationRef.current.computerSessionId !== scopeComputerSessionId) {
598
+ if (mutationRef.current.computerSessionId !== scopeComputerSessionId || mutationRef.current.source !== source) {
528
599
  mutationRef.current = {
529
600
  computerSessionId: scopeComputerSessionId,
601
+ source,
530
602
  count: 0
531
603
  };
532
604
  }
533
605
  mutationRef.current.count += 1;
534
606
  setState(
535
- (current) => current.computerSessionId === scopeComputerSessionId ? { ...current, mutating: true, error: null } : current
607
+ (current) => isCurrentSource() && current.source === source && current.computerSessionId === scopeComputerSessionId ? { ...current, mutating: true, error: null } : current
536
608
  );
537
609
  try {
538
610
  return await operation();
539
- } catch (cause) {
540
- const error = cause instanceof Error ? cause : new Error(String(cause));
541
- setState(
542
- (current) => current.computerSessionId === scopeComputerSessionId ? { ...current, error } : current
543
- );
544
- throw error;
545
611
  } finally {
546
- if (mutationRef.current.computerSessionId === scopeComputerSessionId) {
612
+ if (mutationRef.current.computerSessionId === scopeComputerSessionId && mutationRef.current.source === source) {
547
613
  mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
548
614
  const mutating = mutationRef.current.count > 0;
549
615
  setState(
550
- (current) => current.computerSessionId === scopeComputerSessionId ? { ...current, mutating } : current
616
+ (current) => isCurrentSource() && current.source === source && current.computerSessionId === scopeComputerSessionId ? { ...current, mutating } : current
551
617
  );
552
618
  }
553
619
  }
554
620
  },
555
- [invalidateRefresh]
621
+ [invalidateRefresh, isCurrentSource, source]
556
622
  );
557
623
  const selectTarget = useCallback2(
558
624
  async (targetId) => {
559
625
  if (!computerSessionId) throw new Error("No desktop is selected.");
560
- const target = targetsRef.current.targets.find((candidate) => candidate.id === targetId);
626
+ if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
627
+ const target = targetsRef.current.source === source ? targetsRef.current.targets.find((candidate) => candidate.id === targetId) : null;
561
628
  if (!target) throw new Error("The selected desktop target is no longer available.");
562
629
  invalidateRefresh();
630
+ const readRequestId = requestRef.current.id;
631
+ const selectionRevision = ++selectionRevisionRef.current;
632
+ const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
633
+ const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
563
634
  selectedTargetIdRef.current = target.id;
564
- observationRef.current = { computerSessionId, observation: null };
635
+ observationRef.current = { source, computerSessionId, observation: null };
565
636
  setState(
566
- (current) => current.computerSessionId === computerSessionId ? {
637
+ (current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId ? {
567
638
  ...current,
568
639
  selectedTargetId: target.id,
569
640
  observation: null,
@@ -577,36 +648,50 @@ function useComputerSession(options) {
577
648
  computerSessionId,
578
649
  target.id
579
650
  );
580
- if (selectedTargetIdRef.current !== target.id) return target;
581
- observationRef.current = { computerSessionId, observation };
651
+ if (!isCurrentSelectionRead() || selectedTargetIdRef.current !== target.id) return target;
652
+ observationRef.current = { source, computerSessionId, observation };
582
653
  setState(
583
- (current) => current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? { ...current, observation, loading: false, error: null } : current
654
+ (current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? { ...current, observation, loading: false, error: null } : current
584
655
  );
585
656
  return observation.target;
586
657
  } catch (cause) {
587
658
  const error = cause instanceof Error ? cause : new Error(String(cause));
588
- setState(
589
- (current) => current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? { ...current, loading: false, error } : current
590
- );
659
+ if (isCurrentSelectionRead()) {
660
+ setState(
661
+ (current) => isCurrentSelection() && current.source === source && current.computerSessionId === computerSessionId && current.selectedTargetId === target.id ? {
662
+ ...current,
663
+ loading: false,
664
+ error,
665
+ controlError: isInteractionControlUnavailable(error, "observation") ? error : current.controlError
666
+ } : current
667
+ );
668
+ }
591
669
  throw error;
592
670
  }
593
671
  },
594
- [client, computerSessionId, invalidateRefresh, workspaceId]
672
+ [client, computerSessionId, invalidateRefresh, isCurrentSource, source, workspaceId]
595
673
  );
596
674
  const dispatchAction = useCallback2(
597
675
  async (action, operationId, frame) => {
598
676
  if (!computerSessionId) throw new Error("No desktop is selected.");
677
+ if (!isCurrentSource()) throw new Error("The desktop source is no longer selected.");
599
678
  if (frame && frame.computerSessionId !== computerSessionId) {
600
679
  throw new Error("The displayed desktop frame belongs to another desktop session.");
601
680
  }
602
- const currentObservation = observationRef.current.computerSessionId === computerSessionId ? observationRef.current.observation : null;
603
- const focusTarget = action.type === "focus" ? targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ?? null : null;
604
- const frameTarget = frame ? targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ?? (currentObservation?.target.id === frame.targetId ? currentObservation.target : null) : null;
605
- const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? null;
681
+ const currentObservation = observationRef.current.source === source && observationRef.current.computerSessionId === computerSessionId ? observationRef.current.observation : null;
682
+ const focusTarget = action.type === "focus" && targetsRef.current.source === source ? targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ?? null : null;
683
+ const frameTarget = frame && targetsRef.current.source === source ? currentObservation?.target.id === frame.targetId ? currentObservation.target : targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ?? null : null;
684
+ const inputTarget = (action.type === "keyboard" || action.type === "clipboard") && targetsRef.current.source === source && targetsRef.current.computerSessionId === computerSessionId ? targetsRef.current.targets.find(
685
+ (candidate) => candidate.id === selectedTargetIdRef.current
686
+ ) : null;
687
+ const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? inputTarget ?? null;
606
688
  if (!target) throw new Error("The desktop target is not ready for input.");
607
689
  if (frame && frame.targetId !== selectedTargetIdRef.current) {
608
690
  throw new Error("The displayed desktop frame is no longer selected.");
609
691
  }
692
+ if (frame && (frame.controllerGeneration !== target.controllerGeneration || frame.targetGeneration !== target.targetGeneration)) {
693
+ throw new Error("The displayed desktop frame belongs to an earlier target generation.");
694
+ }
610
695
  if (action.type === "pointer") {
611
696
  const expectedFrameId = frame?.frameId ?? currentObservation?.frameId ?? null;
612
697
  if (!expectedFrameId || action.frameId !== expectedFrameId) {
@@ -616,34 +701,114 @@ function useComputerSession(options) {
616
701
  if (action.type === "semantic" && !currentObservation) {
617
702
  throw new Error("The desktop accessibility tree is not ready for input.");
618
703
  }
704
+ const selectedTargetId = selectedTargetIdRef.current;
705
+ const selectedTarget = targetsRef.current.targets.find(
706
+ (candidate) => candidate.id === selectedTargetId
707
+ );
708
+ const selectedFence = selectedTarget && {
709
+ id: selectedTarget.id,
710
+ computerSessionId: selectedTarget.computerSessionId,
711
+ controllerGeneration: selectedTarget.controllerGeneration,
712
+ targetGeneration: selectedTarget.targetGeneration
713
+ };
714
+ const selectionRevision = selectionRevisionRef.current;
715
+ const isCurrentView = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && selectedTargetIdRef.current === selectedTargetId && targetsRef.current.source === source && targetsRef.current.computerSessionId === computerSessionId && sameTargetFence(
716
+ selectedFence,
717
+ targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId)
718
+ );
719
+ const resultOrder = ++actionResultOrderRef.current.next;
720
+ let admitted = false;
721
+ let resultTargetId = selectedTargetId;
722
+ let resultFence = selectedFence;
723
+ const isCurrentResultView = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && selectedTargetIdRef.current === resultTargetId && targetsRef.current.source === source && sameTargetFence(
724
+ resultFence,
725
+ targetsRef.current.targets.find((candidate) => candidate.id === resultTargetId)
726
+ );
727
+ const canProjectResult = () => {
728
+ if (!admitted) {
729
+ if (!isCurrentView() || resultOrder < actionResultOrderRef.current.settled) return false;
730
+ actionResultOrderRef.current.settled = resultOrder;
731
+ admitted = true;
732
+ invalidateRefresh();
733
+ }
734
+ return isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled;
735
+ };
736
+ const projectState = (update) => {
737
+ setState(
738
+ (current) => admitted && isCurrentResultView() && current.source === source && current.computerSessionId === computerSessionId && current.actionResultOrder <= resultOrder ? { ...update(current), actionResultOrder: resultOrder } : current
739
+ );
740
+ };
619
741
  return await runMutation(computerSessionId, async () => {
620
- const receipt = await client.actInComputer(workspaceId, computerSessionId, {
621
- operationId,
622
- targetId: target.id,
623
- expectedTargetGeneration: frame?.targetGeneration ?? target.targetGeneration,
624
- expectedObservationId: action.type === "pointer" ? null : currentObservation?.observationId ?? null,
625
- expectedFrameId: action.type === "pointer" ? action.frameId : null,
626
- action
627
- });
628
- if (receipt.observation) {
629
- const observation = receipt.observation;
630
- observationRef.current = { computerSessionId, observation };
631
- selectedTargetIdRef.current = observation.target.id;
632
- setState(
633
- (current) => current.computerSessionId === computerSessionId ? {
742
+ try {
743
+ const receipt = await client.actInComputer(workspaceId, computerSessionId, {
744
+ operationId,
745
+ targetId: target.id,
746
+ expectedTargetGeneration: frame?.targetGeneration ?? target.targetGeneration,
747
+ expectedObservationId: action.type === "pointer" ? null : currentObservation?.observationId ?? null,
748
+ expectedFrameId: action.type === "pointer" ? action.frameId : null,
749
+ action
750
+ });
751
+ const receiptSettled = receipt.state === "completed" || receipt.state === "failed" || receipt.state === "outcome_unknown";
752
+ actionResultFences.set(
753
+ receipt,
754
+ () => admitted ? isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled : isCurrentView() && resultOrder >= actionResultOrderRef.current.settled
755
+ );
756
+ if (!receiptSettled || !canProjectResult()) return receipt;
757
+ if (receipt.observation) {
758
+ const observation = receipt.observation;
759
+ observationRef.current = { source, computerSessionId, observation };
760
+ selectedTargetIdRef.current = observation.target.id;
761
+ targetsRef.current.targets = replaceTarget(
762
+ targetsRef.current.targets,
763
+ observation.target
764
+ );
765
+ resultTargetId = observation.target.id;
766
+ resultFence = {
767
+ id: observation.target.id,
768
+ computerSessionId: observation.target.computerSessionId,
769
+ controllerGeneration: observation.target.controllerGeneration,
770
+ targetGeneration: observation.target.targetGeneration
771
+ };
772
+ projectState((current) => ({
634
773
  ...current,
774
+ error: null,
775
+ controlError: null,
635
776
  observation,
636
777
  selectedTargetId: observation.target.id,
637
778
  targets: replaceTarget(current.targets, observation.target)
638
- } : current
779
+ }));
780
+ } else {
781
+ projectState((current) => ({ ...current, error: null, controlError: null }));
782
+ void refresh();
783
+ }
784
+ return receipt;
785
+ } catch (cause) {
786
+ const error = cause instanceof Error ? cause : new Error(String(cause));
787
+ actionResultFences.set(
788
+ error,
789
+ () => admitted && isCurrentResultView() && resultOrder === actionResultOrderRef.current.settled
639
790
  );
640
- } else {
641
- void refresh();
791
+ if (canProjectResult()) {
792
+ projectState((current) => ({
793
+ ...current,
794
+ error,
795
+ controlError: isInteractionControlUnavailable(error) ? error : current.controlError
796
+ }));
797
+ }
798
+ throw error;
642
799
  }
643
- return receipt;
644
800
  });
645
801
  },
646
- [client, computerSessionId, refresh, runMutation, workspaceId]
802
+ [
803
+ client,
804
+ computerSessionId,
805
+ invalidateRefresh,
806
+ isCurrentSource,
807
+ refresh,
808
+ runMutation,
809
+ source,
810
+ workspaceId
811
+ ]
647
812
  );
648
813
  const act = useCallback2(
649
814
  async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
@@ -655,8 +820,39 @@ function useComputerSession(options) {
655
820
  );
656
821
  const readClipboard = useCallback2(async () => {
657
822
  if (!computerSessionId) throw new Error("No desktop is selected.");
658
- return await client.readComputerClipboard(workspaceId, computerSessionId);
659
- }, [client, computerSessionId, workspaceId]);
823
+ const selectionRevision = selectionRevisionRef.current;
824
+ const settledOrder = actionResultOrderRef.current.settled;
825
+ const selectedTargetId = selectedTargetIdRef.current;
826
+ const selectedTarget = targetsRef.current.targets.find(
827
+ (candidate) => candidate.id === selectedTargetId
828
+ );
829
+ const selectedFence = selectedTarget && {
830
+ id: selectedTarget.id,
831
+ computerSessionId: selectedTarget.computerSessionId,
832
+ controllerGeneration: selectedTarget.controllerGeneration,
833
+ targetGeneration: selectedTarget.targetGeneration
834
+ };
835
+ const isCurrentRead = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision && actionResultOrderRef.current.settled === settledOrder && selectedTargetIdRef.current === selectedTargetId && targetsRef.current.source === source && sameTargetFence(
836
+ selectedFence,
837
+ targetsRef.current.targets.find((candidate) => candidate.id === selectedTargetId)
838
+ );
839
+ try {
840
+ const clipboard = await client.readComputerClipboard(workspaceId, computerSessionId);
841
+ actionResultFences.set(clipboard, isCurrentRead);
842
+ return clipboard;
843
+ } catch (cause) {
844
+ const error = cause instanceof Error ? cause : new Error(String(cause));
845
+ actionResultFences.set(error, isCurrentRead);
846
+ setState(
847
+ (current) => isCurrentRead() && current.source === source && current.computerSessionId === computerSessionId ? {
848
+ ...current,
849
+ error,
850
+ controlError: isInteractionControlUnavailable(error) ? error : current.controlError
851
+ } : current
852
+ );
853
+ throw error;
854
+ }
855
+ }, [client, computerSessionId, isCurrentSource, source, workspaceId]);
660
856
  return {
661
857
  session: visible.session,
662
858
  targets: visible.targets,
@@ -665,6 +861,7 @@ function useComputerSession(options) {
665
861
  loading: visible.loading,
666
862
  mutating: visible.mutating,
667
863
  error: visible.error,
864
+ controlError: visible.controlError,
668
865
  refresh,
669
866
  readClipboard,
670
867
  selectTarget,
@@ -672,25 +869,24 @@ function useComputerSession(options) {
672
869
  actFromFrame
673
870
  };
674
871
  }
675
- function emptyState(computerSessionId, loading) {
872
+ function emptyState(source, loading) {
676
873
  return {
677
- computerSessionId,
874
+ source,
875
+ computerSessionId: source.computerSessionId,
678
876
  session: null,
679
877
  targets: [],
680
878
  selectedTargetId: null,
681
879
  observation: null,
682
880
  loading,
683
881
  mutating: false,
684
- error: null
882
+ error: null,
883
+ controlError: null,
884
+ actionResultOrder: 0
685
885
  };
686
886
  }
687
- function chooseTarget(targets, preferredId, platform) {
688
- const preferred = targets.find((target) => target.id === preferredId);
689
- if (preferred) return preferred;
690
- if (platform === "linux") {
691
- return targets.find((target) => target.focused && target.kind === "screen") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets[0] ?? null;
692
- }
693
- return targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "app") ?? targets.find((target) => target.kind === "app") ?? targets[0] ?? null;
887
+ function chooseTarget(targets, preferredId) {
888
+ if (preferredId !== null) return targets.find((target) => target.id === preferredId) ?? null;
889
+ return targets.find((target) => target.focused && target.kind === "screen") ?? targets.find((target) => target.kind === "screen") ?? targets.find((target) => target.focused && target.kind === "window") ?? targets.find((target) => target.kind === "window") ?? targets.find((target) => target.focused && target.kind === "app") ?? targets.find((target) => target.kind === "app") ?? targets[0] ?? null;
694
890
  }
695
891
  function sortComputerTargets(targets) {
696
892
  const rank = { window: 0, app: 1, screen: 2 };
@@ -704,9 +900,76 @@ function replaceTarget(targets, target) {
704
900
  target
705
901
  ]);
706
902
  }
903
+ function sameTargetFence(left, right) {
904
+ return left?.id === right?.id && left?.computerSessionId === right?.computerSessionId && left?.controllerGeneration === right?.controllerGeneration && left?.targetGeneration === right?.targetGeneration;
905
+ }
906
+
907
+ // src/hooks/use-computer-input-posture.ts
908
+ import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
909
+ function useComputerInputPosture(options) {
910
+ const { client, workspaceId } = useEmbeddedComputerInteraction(options);
911
+ const { computerSessionId, controllerGeneration, enabled } = options;
912
+ const [nonce, setNonce] = useState3(0);
913
+ const [state, setState] = useState3(null);
914
+ const key = JSON.stringify([workspaceId, computerSessionId, controllerGeneration, enabled]);
915
+ const sourceRef = useRef3({ client, key });
916
+ if (sourceRef.current.client !== client || sourceRef.current.key !== key) {
917
+ sourceRef.current = { client, key };
918
+ }
919
+ const source = sourceRef.current;
920
+ const visible = state?.source === source && state.nonce === nonce ? state : null;
921
+ const refresh = useCallback3(() => setNonce((value) => value + 1), []);
922
+ useEffect3(() => {
923
+ if (!enabled || !computerSessionId || !controllerGeneration) return;
924
+ const controller = new AbortController();
925
+ let disposed = false;
926
+ setState({ source, nonce, inputAllowed: null, pending: true, error: null });
927
+ void (async () => {
928
+ try {
929
+ if (!client.getComputerInputPosture) {
930
+ throw new Error("Desktop input availability could not be verified.");
931
+ }
932
+ const posture = await client.getComputerInputPosture(workspaceId, computerSessionId, {
933
+ signal: controller.signal,
934
+ timeoutMs: 15e3
935
+ });
936
+ if (disposed || sourceRef.current !== source) return;
937
+ if (posture.computerSessionId !== computerSessionId || posture.controllerGeneration !== controllerGeneration || typeof posture.inputAllowed !== "boolean") {
938
+ throw new Error("Desktop input availability belongs to a different controller.");
939
+ }
940
+ setState({
941
+ source,
942
+ nonce,
943
+ inputAllowed: posture.inputAllowed,
944
+ pending: false,
945
+ error: null
946
+ });
947
+ } catch (cause) {
948
+ if (disposed || controller.signal.aborted || sourceRef.current !== source) return;
949
+ setState({
950
+ source,
951
+ nonce,
952
+ inputAllowed: null,
953
+ pending: false,
954
+ error: cause instanceof Error ? cause : new Error(String(cause))
955
+ });
956
+ }
957
+ })();
958
+ return () => {
959
+ disposed = true;
960
+ controller.abort();
961
+ };
962
+ }, [client, computerSessionId, controllerGeneration, enabled, nonce, source, workspaceId]);
963
+ return {
964
+ inputAllowed: visible?.inputAllowed ?? null,
965
+ pending: enabled && (visible?.pending ?? true),
966
+ error: visible?.error ?? null,
967
+ refresh
968
+ };
969
+ }
707
970
 
708
971
  // src/hooks/use-computer-sessions.ts
709
- import { useCallback as useCallback3, useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState3 } from "react";
972
+ import { useCallback as useCallback4, useEffect as useEffect4, useMemo, useRef as useRef4, useState as useState4 } from "react";
710
973
  function useComputerSessions(options = {}) {
711
974
  const {
712
975
  client,
@@ -716,19 +979,19 @@ function useComputerSessions(options = {}) {
716
979
  workspaceInteractionConnectionState
717
980
  } = useEmbeddedComputerInteraction(options);
718
981
  const enabled = options.enabled ?? true;
719
- const [state, setState] = useState3(() => emptyState2(workspaceId, enabled));
720
- const requestRef = useRef3({
982
+ const [state, setState] = useState4(() => emptyState2(workspaceId, enabled));
983
+ const requestRef = useRef4({
721
984
  id: 0,
722
985
  controller: null
723
986
  });
724
- const mountedRef = useRef3(true);
987
+ const mountedRef = useRef4(true);
725
988
  const visible = state.workspaceId === workspaceId ? state : emptyState2(workspaceId, enabled);
726
- const cancelLoad = useCallback3(() => {
989
+ const cancelLoad = useCallback4(() => {
727
990
  const id = requestRef.current.id + 1;
728
991
  requestRef.current.controller?.abort();
729
992
  requestRef.current = { id, controller: null };
730
993
  }, []);
731
- const load = useCallback3(
994
+ const load = useCallback4(
732
995
  async (foreground) => {
733
996
  if (!enabled) return;
734
997
  const id = requestRef.current.id + 1;
@@ -771,8 +1034,8 @@ function useComputerSessions(options = {}) {
771
1034
  },
772
1035
  [client, enabled, workspaceId]
773
1036
  );
774
- const refresh = useCallback3(async () => await load(false), [load]);
775
- useEffect3(() => {
1037
+ const refresh = useCallback4(async () => await load(false), [load]);
1038
+ useEffect4(() => {
776
1039
  mountedRef.current = true;
777
1040
  if (!enabled) {
778
1041
  cancelLoad();
@@ -795,7 +1058,7 @@ function useComputerSessions(options = {}) {
795
1058
  refresh,
796
1059
  fallbackPollIntervalMs: options.pollIntervalMs
797
1060
  });
798
- useEffect3(() => {
1061
+ useEffect4(() => {
799
1062
  if (!enabled || !options.sessionId) return;
800
1063
  return registerSessionReconciler(
801
1064
  options.sessionId,
@@ -803,7 +1066,7 @@ function useComputerSessions(options = {}) {
803
1066
  refresh
804
1067
  );
805
1068
  }, [enabled, options.sessionId, refresh, registerSessionReconciler, workspaceId]);
806
- const create = useCallback3(
1069
+ const create = useCallback4(
807
1070
  async (request) => {
808
1071
  const response = await client.createComputerSession(workspaceId, {
809
1072
  ...request,
@@ -814,7 +1077,7 @@ function useComputerSessions(options = {}) {
814
1077
  },
815
1078
  [client, workspaceId]
816
1079
  );
817
- const end = useCallback3(
1080
+ const end = useCallback4(
818
1081
  async (computerSessionId, operationId = crypto.randomUUID()) => {
819
1082
  const response = await client.endComputerSession(workspaceId, computerSessionId, {
820
1083
  operationId
@@ -918,10 +1181,10 @@ function ComputerViewer({
918
1181
  () => liveRelevant.length > 0 ? liveRelevant : recentRelevantFailure ? [recentRelevantFailure] : [],
919
1182
  [liveRelevant, recentRelevantFailure]
920
1183
  );
921
- const [selection, setSelection] = useState4(
1184
+ const [selection, setSelection] = useState5(
922
1185
  () => initialComputerSessionId ? { sessionId: initialComputerSessionId, pinned: true } : null
923
1186
  );
924
- const selectComputerSession = useCallback4(
1187
+ const selectComputerSession = useCallback5(
925
1188
  (next) => {
926
1189
  setSelection(next);
927
1190
  onComputerSessionIdChange?.(next?.sessionId ?? null);
@@ -933,20 +1196,20 @@ function ComputerViewer({
933
1196
  enabled,
934
1197
  resourceKind: "computer_session"
935
1198
  });
936
- const [creating, setCreating] = useState4(false);
937
- const [createError, setCreateError] = useState4(null);
938
- const [showControls, setShowControls] = useState4(false);
939
- const previousSessionIdRef = useRef4(sessionId);
940
- const handledRequestRef = useRef4(null);
941
- const seenInterventionIdsRef = useRef4(/* @__PURE__ */ new Set());
942
- const createInFlightRef = useRef4(false);
943
- const autoCreateSessionRef = useRef4(null);
944
- const emptyCatalogConfirmationRef = useRef4(null);
945
- const endedGenerationLossRef = useRef4(/* @__PURE__ */ new Set());
946
- const recreatedPlacementLossRef = useRef4(/* @__PURE__ */ new Set());
947
- const [suppressGenericCreate, setSuppressGenericCreate] = useState4(false);
1199
+ const [creating, setCreating] = useState5(false);
1200
+ const [createError, setCreateError] = useState5(null);
1201
+ const [showControls, setShowControls] = useState5(false);
1202
+ const previousSessionIdRef = useRef5(sessionId);
1203
+ const handledRequestRef = useRef5(null);
1204
+ const seenInterventionIdsRef = useRef5(/* @__PURE__ */ new Set());
1205
+ const createInFlightRef = useRef5(false);
1206
+ const autoCreateSessionRef = useRef5(null);
1207
+ const emptyCatalogConfirmationRef = useRef5(null);
1208
+ const endedGenerationLossRef = useRef5(/* @__PURE__ */ new Set());
1209
+ const recreatedPlacementLossRef = useRef5(/* @__PURE__ */ new Set());
1210
+ const [suppressGenericCreate, setSuppressGenericCreate] = useState5(false);
948
1211
  const endStaleComputer = registry.end;
949
- useEffect4(() => {
1212
+ useEffect5(() => {
950
1213
  if (!enabled) return;
951
1214
  for (const session of registry.sessions) {
952
1215
  if (!isAttachedChromeGenerationLoss(session)) continue;
@@ -958,8 +1221,9 @@ function ComputerViewer({
958
1221
  });
959
1222
  }
960
1223
  }, [enabled, endStaleComputer, registry.sessions]);
961
- const notifyError = useCallback4(
1224
+ const notifyError = useCallback5(
962
1225
  (cause, fallback) => {
1226
+ if (isStaleComputerActionResult(cause)) return;
963
1227
  onNotify?.({
964
1228
  kind: "error",
965
1229
  message: cause instanceof Error ? cause.message : fallback
@@ -967,7 +1231,7 @@ function ComputerViewer({
967
1231
  },
968
1232
  [onNotify]
969
1233
  );
970
- useEffect4(() => {
1234
+ useEffect5(() => {
971
1235
  if (previousSessionIdRef.current === sessionId) return;
972
1236
  previousSessionIdRef.current = sessionId;
973
1237
  handledRequestRef.current = null;
@@ -980,7 +1244,7 @@ function ComputerViewer({
980
1244
  initialComputerSessionId ? { sessionId: initialComputerSessionId, pinned: true } : null
981
1245
  );
982
1246
  }, [initialComputerSessionId, sessionId]);
983
- useEffect4(() => {
1247
+ useEffect5(() => {
984
1248
  if (!enabled || registry.loading) return;
985
1249
  const requested = requestedComputerSessionId ? liveSessions.find((session) => session.id === requestedComputerSessionId) ?? null : null;
986
1250
  const requestKey = requested ? `${requestedComputerRequestId ?? "default"}:${requested.id}` : null;
@@ -1031,7 +1295,10 @@ function ComputerViewer({
1031
1295
  computerSessionId: selection?.sessionId ?? null,
1032
1296
  enabled: enabled && selection !== null && controllerReady
1033
1297
  });
1034
- useEffect4(() => {
1298
+ useEffect5(() => {
1299
+ setShowControls(computer.selectedTarget?.kind === "app");
1300
+ }, [sessionId, selection?.sessionId, computer.selectedTarget?.id, computer.selectedTarget?.kind]);
1301
+ useEffect5(() => {
1035
1302
  if (!isSourcePlacementChangedError(computer.error, "computer_session")) return;
1036
1303
  void refreshRegistry();
1037
1304
  }, [computer.error, refreshRegistry]);
@@ -1052,15 +1319,46 @@ function ComputerViewer({
1052
1319
  },
1053
1320
  ...webSocketFactory ? { webSocketFactory } : {}
1054
1321
  });
1055
- const displayedFrame = frames.frame && frames.frame.computerSessionId === selection?.sessionId && frames.frame.targetId === computer.selectedTarget?.id ? frames.frame : null;
1322
+ const displayedFrame = frames.frame && frames.frame.computerSessionId === selection?.sessionId && frames.frame.targetId === computer.selectedTarget?.id && frames.frame.targetGeneration === computer.selectedTarget?.targetGeneration ? frames.frame : null;
1056
1323
  const rfbStream = frames.attachment?.stream.kind === "direct_rfb" ? frames.attachment.stream : null;
1324
+ const appTarget = computer.selectedTarget?.kind === "app";
1325
+ const appInputPosture = useComputerInputPosture({
1326
+ ...override,
1327
+ computerSessionId: selection?.sessionId ?? null,
1328
+ controllerGeneration: computer.session?.controller?.controllerGeneration ?? null,
1329
+ enabled: enabled && controllerReady && appTarget
1330
+ });
1331
+ const inputPostureRef = useRef5({
1332
+ sessionId: selection?.sessionId ?? null,
1333
+ controllerGeneration: computer.session?.controller?.controllerGeneration ?? null,
1334
+ denied: false
1335
+ });
1336
+ const postureSessionId = selection?.sessionId ?? null;
1337
+ const postureController = computer.session?.controller?.controllerGeneration ?? null;
1338
+ if (inputPostureRef.current.sessionId !== postureSessionId || inputPostureRef.current.controllerGeneration !== postureController) {
1339
+ inputPostureRef.current = {
1340
+ sessionId: postureSessionId,
1341
+ controllerGeneration: postureController,
1342
+ denied: false
1343
+ };
1344
+ }
1345
+ if (frames.attachment?.computerSessionId === postureSessionId && frames.attachment.controllerGeneration === postureController) {
1346
+ inputPostureRef.current.denied = frames.attachment.inputAllowed === false;
1347
+ }
1348
+ const inputDenied = appTarget ? appInputPosture.inputAllowed !== true : inputPostureRef.current.denied;
1349
+ const attachmentInputAllowed = frames.attachment !== null && !inputDenied;
1350
+ const inputPosturePending = appTarget ? appInputPosture.pending : inputDenied && (frames.state === "attaching" || frames.state === "reconnecting");
1351
+ const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
1352
+ const controlUnavailable = computer.controlError !== null;
1353
+ const connectionState = controlUnavailable ? "error" : frames.state;
1354
+ const rfbInputEnabled = rfbStream?.inputAllowed === true && attachmentInputAllowed && !machineLocked && !controlUnavailable && computer.session?.capabilities?.pointerInput === true && computer.session?.capabilities?.keyboardInput === true;
1057
1355
  const rfbCapability = useMemo2(() => {
1058
1356
  if (!rfbStream || !frames.attachment) return null;
1059
1357
  const bounds = computer.selectedTarget?.bounds;
1060
1358
  return {
1061
1359
  transport: "vnc-ws",
1062
1360
  client: "novnc",
1063
- mode: "interactive",
1361
+ mode: rfbInputEnabled ? "interactive" : "read-only",
1064
1362
  url: rfbStream.url,
1065
1363
  token: null,
1066
1364
  expiresAt: frames.attachment.expiresAt,
@@ -1072,10 +1370,9 @@ function ComputerViewer({
1072
1370
  sharedSessionIds: [],
1073
1371
  reason: null
1074
1372
  };
1075
- }, [computer.selectedTarget?.bounds, frames.attachment, rfbStream]);
1076
- const machineLocked = selectedRegistrySession?.failureCode === "machine_locked";
1373
+ }, [computer.selectedTarget?.bounds, frames.attachment, rfbInputEnabled, rfbStream]);
1077
1374
  const currentIds = useMemo2(() => new Set(relevant.map((session) => session.id)), [relevant]);
1078
- useEffect4(() => {
1375
+ useEffect5(() => {
1079
1376
  for (const intervention of interventions.interventions) {
1080
1377
  if (seenInterventionIdsRef.current.has(intervention.id)) continue;
1081
1378
  seenInterventionIdsRef.current.add(intervention.id);
@@ -1085,7 +1382,7 @@ function ComputerViewer({
1085
1382
  });
1086
1383
  }
1087
1384
  }, [interventions.interventions, onNotify]);
1088
- const resolveIntervention = useCallback4(
1385
+ const resolveIntervention = useCallback5(
1089
1386
  (intervention, outcome) => {
1090
1387
  void interventions.resolve(intervention.id, {
1091
1388
  expectedVersion: intervention.version,
@@ -1099,7 +1396,7 @@ function ComputerViewer({
1099
1396
  },
1100
1397
  [interventions, notifyError, onNotify]
1101
1398
  );
1102
- const createComputer = useCallback4(() => {
1399
+ const createComputer = useCallback5(() => {
1103
1400
  if (createInFlightRef.current) return;
1104
1401
  createInFlightRef.current = true;
1105
1402
  setCreateError(null);
@@ -1115,7 +1412,7 @@ function ComputerViewer({
1115
1412
  setCreating(false);
1116
1413
  });
1117
1414
  }, [createSession, notifyError, selectComputerSession, sessionId]);
1118
- useEffect4(() => {
1415
+ useEffect5(() => {
1119
1416
  if (!enabled || registry.loading || registry.refreshing || liveRelevant.length > 0 || !connectedPlacementLoss || recreatedPlacementLossRef.current.has(connectedPlacementLoss.id)) {
1120
1417
  return;
1121
1418
  }
@@ -1132,13 +1429,13 @@ function ComputerViewer({
1132
1429
  registry.refreshing,
1133
1430
  sessionId
1134
1431
  ]);
1135
- useEffect4(() => {
1432
+ useEffect5(() => {
1136
1433
  if (relevant.length > 0) {
1137
1434
  autoCreateSessionRef.current = sessionId;
1138
1435
  emptyCatalogConfirmationRef.current = null;
1139
1436
  }
1140
1437
  }, [relevant.length, sessionId]);
1141
- useEffect4(() => {
1438
+ useEffect5(() => {
1142
1439
  if (!enabled || registry.loading || registry.refreshing || registry.error || relevant.length > 0 || suppressGenericCreate || autoCreateSessionRef.current === sessionId) {
1143
1440
  return;
1144
1441
  }
@@ -1160,41 +1457,62 @@ function ComputerViewer({
1160
1457
  sessionId,
1161
1458
  suppressGenericCreate
1162
1459
  ]);
1163
- const perform = useCallback4(
1164
- async (action, frame) => {
1460
+ const perform = useCallback5(
1461
+ async (action, frame, operationId) => {
1462
+ if (computer.controlError) throw computer.controlError;
1463
+ if (inputDenied) throw new Error("Desktop is view only. Refresh to check desktop controls.");
1464
+ let receipt;
1165
1465
  if (action.type === "pointer") {
1166
1466
  if (!frame) throw new Error("Desktop view is not ready for pointer input.");
1167
- await actFromFrame(action, frame);
1467
+ receipt = await actFromFrame(action, frame, operationId);
1168
1468
  } else {
1169
- await act(action);
1469
+ receipt = await act(action, operationId);
1170
1470
  }
1471
+ if (!isStaleComputerActionResult(receipt) && receipt.state !== "completed") {
1472
+ throw computerActionReceiptError(receipt);
1473
+ }
1474
+ return receipt;
1171
1475
  },
1172
- [act, actFromFrame]
1476
+ [act, actFromFrame, computer.controlError, inputDenied]
1173
1477
  );
1174
- const copyFromRfb = useCallback4(
1478
+ const reconnect = () => {
1479
+ void refreshComputer();
1480
+ if (appTarget) appInputPosture.refresh();
1481
+ else frames.reconnect();
1482
+ };
1483
+ const copyFromRfb = useCallback5(
1175
1484
  (event) => {
1176
- if (!rfbStream || computer.session?.capabilities?.clipboard !== true) return;
1485
+ if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
1486
+ return;
1177
1487
  event.preventDefault();
1178
1488
  event.stopPropagation();
1179
- void perform({ type: "clipboard", operation: "copy" }, null).then(() => computer.readClipboard()).then(async (clipboard) => {
1489
+ void (async () => {
1490
+ const receipt = await perform({ type: "clipboard", operation: "copy" }, null);
1491
+ if (isStaleComputerActionResult(receipt)) return;
1492
+ const clipboard = await computer.readClipboard();
1493
+ if (isStaleComputerActionResult(receipt) || isStaleComputerActionResult(clipboard)) return;
1180
1494
  if (clipboard.text && !await copyTextToClipboard(clipboard.text)) {
1181
1495
  throw new Error("Desktop text could not be copied to the local clipboard");
1182
1496
  }
1183
- }).catch((cause) => notifyError(cause, "Could not copy from the desktop."));
1497
+ })().catch((cause) => notifyError(cause, "Could not copy from the desktop."));
1184
1498
  },
1185
- [computer, notifyError, perform, rfbStream]
1499
+ [computer, notifyError, perform, rfbInputEnabled, rfbStream]
1186
1500
  );
1187
- const pasteIntoRfb = useCallback4(
1501
+ const pasteIntoRfb = useCallback5(
1188
1502
  (text) => {
1189
- if (!rfbStream || computer.session?.capabilities?.clipboard !== true) return false;
1503
+ if (!rfbInputEnabled || !rfbStream || computer.session?.capabilities?.clipboard !== true)
1504
+ return false;
1190
1505
  void (async () => {
1191
- await perform({ type: "clipboard", operation: "write", text }, null);
1192
- assertExactComputerClipboard(await computer.readClipboard(), text);
1506
+ const receipt = await perform({ type: "clipboard", operation: "write", text }, null);
1507
+ if (isStaleComputerActionResult(receipt)) return;
1508
+ const clipboard = await computer.readClipboard();
1509
+ if (isStaleComputerActionResult(receipt) || isStaleComputerActionResult(clipboard)) return;
1510
+ assertExactComputerClipboard(clipboard, text);
1193
1511
  await perform({ type: "clipboard", operation: "paste" }, null);
1194
1512
  })().catch((cause) => notifyError(cause, "Could not paste into the desktop."));
1195
1513
  return true;
1196
1514
  },
1197
- [computer, notifyError, perform, rfbStream]
1515
+ [computer, notifyError, perform, rfbInputEnabled, rfbStream]
1198
1516
  );
1199
1517
  const hideGenericCreate = suppressGenericCreate && liveRelevant.length === 0;
1200
1518
  const generationLossSelected = selectedRegistrySession !== null && isAttachedChromeGenerationLoss(selectedRegistrySession);
@@ -1213,24 +1531,24 @@ function ComputerViewer({
1213
1531
  if (liveSessions.length === 0) {
1214
1532
  if (hideGenericCreate) {
1215
1533
  return /* @__PURE__ */ jsx("div", { className: cn("grid h-full place-items-center bg-og-bg p-6", className), children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
1216
- /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-status-error" }),
1534
+ /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-danger" }),
1217
1535
  /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "Chrome reconnected\u2014open a fresh browser/desktop." }),
1218
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." })
1536
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." })
1219
1537
  ] }) });
1220
1538
  }
1221
1539
  if (renderEmpty) return renderEmpty(createComputer, creating);
1222
1540
  const error = createError ?? registry.error;
1223
1541
  return /* @__PURE__ */ jsx("div", { className: cn("grid h-full place-items-center bg-og-bg p-6", className), children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
1224
- /* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-muted", children: error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-5 text-og-status-error" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-5 animate-spin" }) }),
1542
+ /* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted", children: error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-5 animate-spin" }) }),
1225
1543
  /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: error ? "Desktop didn\u2019t open" : "Opening desktop\u2026" }),
1226
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: error?.message ?? "Preparing this agent\u2019s desktop. It will appear here when ready." }),
1544
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: error?.message ?? "Preparing this agent\u2019s desktop. It will appear here when ready." }),
1227
1545
  error ? /* @__PURE__ */ jsxs(
1228
1546
  "button",
1229
1547
  {
1230
1548
  type: "button",
1231
1549
  onClick: createError ? createComputer : () => void refreshRegistry(),
1232
1550
  disabled: creating || registry.refreshing,
1233
- className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm bg-og-accent-deep px-3 text-og-control font-medium text-og-accent-fg transition hover:brightness-110 disabled:opacity-50",
1551
+ className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-3 text-og-control font-medium transition hover:bg-og-primary-hover disabled:opacity-50",
1234
1552
  children: [
1235
1553
  creating || registry.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(RotateCcwIcon, { className: "size-3.5" }),
1236
1554
  "Try again"
@@ -1239,122 +1557,167 @@ function ComputerViewer({
1239
1557
  ) : null
1240
1558
  ] }) });
1241
1559
  }
1242
- return /* @__PURE__ */ jsxs("div", { className: cn("flex h-full min-h-0 flex-col overflow-hidden bg-og-bg", className), children: [
1243
- /* @__PURE__ */ jsx(
1244
- ComputerToolbar,
1245
- {
1246
- sessions: liveSessions,
1247
- relevantSessionIds: currentIds,
1248
- selectedSessionId: selection?.sessionId ?? null,
1249
- creating,
1250
- refreshing: registry.refreshing,
1251
- interventionCounts,
1252
- onSelect: (computerSessionId) => selectComputerSession({ sessionId: computerSessionId, pinned: true }),
1253
- onFollow: () => {
1254
- if (relevant[0]) selectComputerSession({ sessionId: relevant[0].id, pinned: false });
1255
- },
1256
- onCreate: hideGenericCreate ? void 0 : createComputer,
1257
- onRefresh: () => void Promise.all([refreshRegistry(), refreshComputer()])
1258
- }
1259
- ),
1260
- /* @__PURE__ */ jsx(
1261
- InteractionInterventionBanner,
1262
- {
1263
- interventions: selectedInterventions,
1264
- activeTargetId: computer.selectedTarget?.id ?? null,
1265
- mutating: interventions.mutating,
1266
- onOpen: (intervention) => void computer.selectTarget(intervention.targetId).catch((cause) => notifyError(cause, "Could not open the requested desktop view.")),
1267
- onResolve: resolveIntervention
1268
- }
1269
- ),
1270
- !selectedRegistrySession ? /* @__PURE__ */ jsx(
1271
- ComputerUnselectedPanel,
1272
- {
1273
- peerCount: liveSessions.length,
1274
- creating,
1275
- error: createError,
1276
- generationLoss: suppressGenericCreate,
1277
- onCreate: createComputer
1278
- }
1279
- ) : !controllerReady ? /* @__PURE__ */ jsx(
1280
- ComputerLifecyclePanel,
1281
- {
1282
- session: selectedRegistrySession,
1283
- creating,
1284
- onRefresh: registry.refresh,
1285
- onRetry: createComputer
1286
- }
1287
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1288
- /* @__PURE__ */ jsx(
1289
- ComputerTargetRail,
1290
- {
1291
- targets: computer.targets,
1292
- selectedTargetId: computer.selectedTarget?.id ?? null,
1293
- loading: computer.loading,
1294
- onSelect: (targetId) => void computer.selectTarget(targetId).catch((cause) => notifyError(cause, "Could not switch desktop views."))
1295
- }
1560
+ return /* @__PURE__ */ jsxs(
1561
+ "div",
1562
+ {
1563
+ className: cn(
1564
+ "@container/computer-viewer relative flex h-full min-h-0 min-w-0 flex-col overflow-hidden bg-og-bg",
1565
+ className
1296
1566
  ),
1297
- /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1", children: [
1298
- rfbStream ? /* @__PURE__ */ jsx("div", { className: "relative min-h-0 flex-1 bg-black", onCopyCapture: copyFromRfb, children: /* @__PURE__ */ jsx(
1299
- DesktopViewer,
1567
+ children: [
1568
+ /* @__PURE__ */ jsx(
1569
+ ComputerToolbar,
1300
1570
  {
1301
- capability: rfbCapability,
1302
- interactive: true,
1303
- showControlToggle: false,
1304
- webSocketProtocols: rfbStream.protocols,
1305
- onPasteText: pasteIntoRfb,
1306
- targetPlatform: computer.session?.platform ?? null,
1307
- className: "h-full"
1571
+ sessions: liveSessions,
1572
+ relevantSessionIds: currentIds,
1573
+ selectedSessionId: selection?.sessionId ?? null,
1574
+ creating,
1575
+ refreshing: registry.refreshing || inputPosturePending,
1576
+ interventionCounts,
1577
+ targets: controllerReady ? computer.targets : [],
1578
+ selectedTargetId: computer.selectedTarget?.id ?? null,
1579
+ targetsLoading: controllerReady && computer.loading,
1580
+ onSelectTarget: (targetId) => {
1581
+ void computer.selectTarget(targetId).catch((cause) => notifyError(cause, "Could not switch desktop views."));
1582
+ },
1583
+ onSelect: (computerSessionId) => selectComputerSession({ sessionId: computerSessionId, pinned: true }),
1584
+ onFollow: () => {
1585
+ if (relevant[0]) selectComputerSession({ sessionId: relevant[0].id, pinned: false });
1586
+ },
1587
+ onCreate: hideGenericCreate ? void 0 : createComputer,
1588
+ onRefresh: () => {
1589
+ if (appTarget) {
1590
+ appInputPosture.refresh();
1591
+ } else if ((connectionState === "error" || inputDenied) && !generationLossSelected && !generationLossFrames) {
1592
+ frames.reconnect();
1593
+ }
1594
+ void Promise.all([refreshRegistry(), refreshComputer()]);
1595
+ }
1308
1596
  }
1309
- ) }) : /* @__PURE__ */ jsx(
1310
- ComputerViewport,
1597
+ ),
1598
+ /* @__PURE__ */ jsx(
1599
+ InteractionInterventionBanner,
1311
1600
  {
1312
- frame: machineLocked ? null : displayedFrame,
1313
- observation: computer.observation,
1314
- target: computer.selectedTarget,
1315
- machineLocked,
1316
- connectionState: frames.state,
1317
- connectionError: frames.error ?? computer.error,
1318
- mutating: computer.mutating,
1319
- backgroundActions: computer.session?.capabilities?.backgroundActions === true,
1320
- clipboardEnabled: computer.session?.capabilities?.clipboard === true,
1321
- onAction: perform,
1322
- onReadClipboard: computer.readClipboard,
1323
- onReconnect: generationLossSelected || generationLossFrames ? () => void 0 : frames.reconnect,
1324
- onError: (cause) => notifyError(cause, "Desktop input failed.")
1601
+ interventions: selectedInterventions,
1602
+ activeTargetId: computer.selectedTarget?.id ?? null,
1603
+ mutating: interventions.mutating,
1604
+ onOpen: (intervention) => void computer.selectTarget(intervention.targetId).catch((cause) => notifyError(cause, "Could not open the requested desktop view.")),
1605
+ onResolve: resolveIntervention
1325
1606
  }
1326
1607
  ),
1327
- showControls ? /* @__PURE__ */ jsx(
1328
- ComputerSemanticPanel,
1608
+ !selectedRegistrySession ? /* @__PURE__ */ jsx(
1609
+ ComputerUnselectedPanel,
1329
1610
  {
1330
- observation: computer.observation,
1331
- mutating: computer.mutating,
1332
- onAction: (action) => void perform(action, null).catch(
1333
- (cause) => notifyError(cause, "Desktop action failed.")
1334
- )
1611
+ peerCount: liveSessions.length,
1612
+ creating,
1613
+ error: createError,
1614
+ generationLoss: suppressGenericCreate,
1615
+ onCreate: createComputer
1335
1616
  }
1336
- ) : null
1337
- ] }),
1338
- /* @__PURE__ */ jsx(
1339
- ComputerStatusBar,
1340
- {
1341
- session: computer.session,
1342
- target: computer.selectedTarget,
1343
- connectionState: frames.state,
1344
- refreshing: registry.refreshing,
1345
- showControls,
1346
- controlCount: semanticNodes(computer.observation).length,
1347
- onToggleControls: () => setShowControls((current) => !current)
1348
- }
1349
- )
1350
- ] })
1351
- ] });
1617
+ ) : !controllerReady ? /* @__PURE__ */ jsx(
1618
+ ComputerLifecyclePanel,
1619
+ {
1620
+ session: selectedRegistrySession,
1621
+ creating,
1622
+ onRefresh: registry.refresh,
1623
+ onRetry: createComputer
1624
+ }
1625
+ ) : /* @__PURE__ */ jsxs(Fragment, { children: [
1626
+ /* @__PURE__ */ jsxs("div", { className: "flex min-h-0 flex-1 flex-col @xl/computer-viewer:flex-row", children: [
1627
+ rfbStream ? /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 bg-black", onCopyCapture: copyFromRfb, children: [
1628
+ /* @__PURE__ */ jsx(
1629
+ DesktopViewer,
1630
+ {
1631
+ capability: rfbCapability,
1632
+ interactive: rfbInputEnabled,
1633
+ showControlToggle: false,
1634
+ webSocketProtocols: rfbStream.protocols,
1635
+ onPasteText: pasteIntoRfb,
1636
+ targetPlatform: computer.session?.platform ?? null,
1637
+ className: "h-full"
1638
+ }
1639
+ ),
1640
+ !rfbInputEnabled ? /* @__PURE__ */ jsx("div", { className: "pointer-events-none absolute bottom-3 left-1/2 -translate-x-1/2 rounded-full border border-white/10 bg-black/70 px-3 py-1 text-[11px] text-white/80 backdrop-blur", children: controlUnavailable ? /* @__PURE__ */ jsxs(Fragment, { children: [
1641
+ "Desktop controls unavailable",
1642
+ " ",
1643
+ /* @__PURE__ */ jsx(
1644
+ "button",
1645
+ {
1646
+ type: "button",
1647
+ onClick: reconnect,
1648
+ className: "pointer-events-auto ml-2 font-medium underline",
1649
+ children: "Reconnect"
1650
+ }
1651
+ )
1652
+ ] }) : "View only \xB7 direct input unavailable" }) : null
1653
+ ] }) : /* @__PURE__ */ jsx(
1654
+ ComputerViewport,
1655
+ {
1656
+ frame: machineLocked ? null : displayedFrame,
1657
+ observation: computer.observation,
1658
+ target: computer.selectedTarget,
1659
+ machineLocked,
1660
+ controlUnavailable,
1661
+ inputAllowed: !inputDenied,
1662
+ connectionState,
1663
+ connectionError: computer.controlError ?? appInputPosture.error ?? frames.error ?? computer.error,
1664
+ mutating: computer.mutating,
1665
+ backgroundActions: computer.session?.capabilities?.backgroundActions === true,
1666
+ backgroundInput: computer.session?.capabilities?.backgroundInput === true,
1667
+ allowForegroundInputWithinOwnedSeat: computer.session?.platform === "linux" && computer.session.adapter === "opengeni.cua.linux.v1" && computer.session.seatId === `linux-virtual:${computer.session.id}` && Boolean(computer.session.displayId),
1668
+ clipboardEnabled: attachmentInputAllowed && computer.session?.capabilities?.clipboard === true,
1669
+ pointerInput: attachmentInputAllowed && computer.session?.capabilities?.pointerInput === true,
1670
+ pointerClickContinuation: attachmentInputAllowed && computer.session?.capabilities?.pointerClickContinuation === true,
1671
+ keyboardInput: attachmentInputAllowed && computer.session?.capabilities?.keyboardInput === true,
1672
+ onAction: perform,
1673
+ onReadClipboard: computer.readClipboard,
1674
+ onReconnect: generationLossSelected || generationLossFrames ? () => void 0 : reconnect,
1675
+ onError: (cause) => notifyError(cause, "Desktop input failed.")
1676
+ },
1677
+ JSON.stringify([
1678
+ selection?.sessionId,
1679
+ computer.selectedTarget?.id,
1680
+ computer.selectedTarget?.targetGeneration,
1681
+ frames.attachment?.controllerGeneration
1682
+ ])
1683
+ ),
1684
+ showControls ? /* @__PURE__ */ jsx(
1685
+ ComputerSemanticPanel,
1686
+ {
1687
+ observation: computer.observation,
1688
+ mutating: computer.mutating || controlUnavailable || inputDenied,
1689
+ controlUnavailable,
1690
+ inputAllowed: !inputDenied,
1691
+ onAction: (action) => void perform(action, null).catch(
1692
+ (cause) => notifyError(cause, "Desktop action failed.")
1693
+ )
1694
+ }
1695
+ ) : null
1696
+ ] }),
1697
+ /* @__PURE__ */ jsx(
1698
+ ComputerStatusBar,
1699
+ {
1700
+ session: computer.session,
1701
+ target: computer.selectedTarget,
1702
+ connectionState,
1703
+ controlUnavailable,
1704
+ inputAllowed: !inputDenied,
1705
+ refreshing: registry.refreshing,
1706
+ showControls,
1707
+ controlCount: semanticControls(computer.observation).length,
1708
+ onToggleControls: () => setShowControls((current) => !current)
1709
+ }
1710
+ )
1711
+ ] })
1712
+ ]
1713
+ }
1714
+ );
1352
1715
  }
1353
1716
  function ComputerUnselectedPanel(props) {
1354
1717
  return /* @__PURE__ */ jsx("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm", children: [
1355
- /* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-muted", children: props.error || props.generationLoss ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4.5 text-og-status-error" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
1718
+ /* @__PURE__ */ jsx("span", { className: "mx-auto grid size-10 place-items-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted", children: props.error || props.generationLoss ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4.5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
1356
1719
  /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: props.generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : props.error ? "Desktop didn\u2019t open" : "Opening desktop\u2026" }),
1357
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: props.generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : props.error?.message ?? (props.peerCount === 1 ? "Preparing this agent\u2019s desktop. One peer desktop remains available from the menu." : `Preparing this agent\u2019s desktop. ${props.peerCount} peer desktops remain available from the menu.`) }),
1720
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: props.generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : props.error?.message ?? (props.peerCount === 1 ? "Preparing this agent\u2019s desktop. One peer desktop remains available from the menu." : `Preparing this agent\u2019s desktop. ${props.peerCount} peer desktops remain available from the menu.`) }),
1358
1721
  props.error && !props.generationLoss ? /* @__PURE__ */ jsxs(
1359
1722
  "button",
1360
1723
  {
@@ -1371,59 +1734,95 @@ function ComputerUnselectedPanel(props) {
1371
1734
  ] }) });
1372
1735
  }
1373
1736
  function ComputerToolbar(props) {
1374
- const detailsRef = useRef4(null);
1737
+ const detailsRef = useViewerMenuDismiss();
1375
1738
  const selected = props.sessions.find((session) => session.id === props.selectedSessionId);
1376
1739
  const current = props.sessions.filter((session) => props.relevantSessionIds.has(session.id));
1377
1740
  const others = props.sessions.filter((session) => !props.relevantSessionIds.has(session.id));
1378
- const choose = (id) => {
1379
- props.onSelect(id);
1741
+ const closeMenu = (restoreFocus) => {
1380
1742
  detailsRef.current?.removeAttribute("open");
1743
+ if (restoreFocus) detailsRef.current?.querySelector("summary")?.focus();
1381
1744
  };
1382
- return /* @__PURE__ */ jsxs("div", { className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-surface-0 px-2", children: [
1383
- /* @__PURE__ */ jsxs("details", { ref: detailsRef, className: "relative min-w-0", children: [
1384
- /* @__PURE__ */ jsxs("summary", { className: "flex h-7 max-w-52 cursor-pointer list-none items-center gap-2 rounded-og-sm px-2 text-og-control text-og-fg transition hover:bg-og-surface-2 [&::-webkit-details-marker]:hidden", children: [
1385
- /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0 text-og-muted" }),
1745
+ const choose = (id, restoreFocus) => {
1746
+ props.onSelect(id);
1747
+ closeMenu(restoreFocus);
1748
+ };
1749
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-surface-1 px-2", children: [
1750
+ /* @__PURE__ */ jsxs("details", { ref: detailsRef, className: "min-w-0", children: [
1751
+ /* @__PURE__ */ jsxs("summary", { className: "flex h-7 max-w-32 cursor-pointer list-none items-center gap-2 rounded-og-sm px-2 text-og-control text-og-fg transition hover:bg-og-surface-2 @sm/computer-viewer:max-w-52 [&::-webkit-details-marker]:hidden", children: [
1752
+ /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
1386
1753
  /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: selected?.name === "Computer" ? "Desktop" : selected?.name ?? "Desktop" }),
1387
1754
  (props.interventionCounts.get(selected?.id ?? "") ?? 0) > 0 ? /* @__PURE__ */ jsx("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-waiting" }) : null,
1388
- /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-subtle" })
1755
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
1389
1756
  ] }),
1390
- /* @__PURE__ */ jsxs("div", { className: "absolute left-0 top-8 z-30 w-72 overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
1391
- /* @__PURE__ */ jsx(
1392
- ComputerSessionGroup,
1393
- {
1394
- label: "Current agent",
1395
- sessions: current,
1396
- selectedId: props.selectedSessionId,
1397
- interventionCounts: props.interventionCounts,
1398
- onSelect: choose
1399
- }
1400
- ),
1401
- /* @__PURE__ */ jsx(
1402
- ComputerSessionGroup,
1403
- {
1404
- label: "Other agents",
1405
- sessions: others,
1406
- selectedId: props.selectedSessionId,
1407
- interventionCounts: props.interventionCounts,
1408
- onSelect: choose
1409
- }
1410
- ),
1411
- /* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1 border-t border-og-border pt-1", children: [
1412
- current.length > 0 ? /* @__PURE__ */ jsx(MenuButton, { onClick: props.onFollow, children: "Follow agent" }) : null,
1413
- props.onCreate ? /* @__PURE__ */ jsxs(MenuButton, { onClick: props.onCreate, disabled: props.creating, children: [
1414
- /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
1415
- " New desktop"
1416
- ] }) : null
1757
+ /* @__PURE__ */ jsxs("div", { className: "absolute left-2 top-10 z-30 flex max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] flex-col overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
1758
+ /* @__PURE__ */ jsxs("div", { className: "min-h-0 max-h-96 overflow-y-auto overscroll-contain", children: [
1759
+ /* @__PURE__ */ jsx(
1760
+ ComputerSessionGroup,
1761
+ {
1762
+ label: "Current agent",
1763
+ sessions: current,
1764
+ selectedId: props.selectedSessionId,
1765
+ interventionCounts: props.interventionCounts,
1766
+ onSelect: choose
1767
+ }
1768
+ ),
1769
+ /* @__PURE__ */ jsx(
1770
+ ComputerSessionGroup,
1771
+ {
1772
+ label: "Other agents",
1773
+ sessions: others,
1774
+ selectedId: props.selectedSessionId,
1775
+ interventionCounts: props.interventionCounts,
1776
+ onSelect: choose
1777
+ }
1778
+ )
1779
+ ] }),
1780
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex shrink-0 gap-1 border-t border-og-border pt-1", children: [
1781
+ current.length > 0 ? /* @__PURE__ */ jsx(
1782
+ MenuButton,
1783
+ {
1784
+ onClick: (event) => {
1785
+ props.onFollow();
1786
+ closeMenu(event.detail === 0);
1787
+ },
1788
+ children: "Follow agent"
1789
+ }
1790
+ ) : null,
1791
+ props.onCreate ? /* @__PURE__ */ jsxs(
1792
+ MenuButton,
1793
+ {
1794
+ onClick: (event) => {
1795
+ props.onCreate?.();
1796
+ closeMenu(event.detail === 0);
1797
+ },
1798
+ disabled: props.creating,
1799
+ children: [
1800
+ /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
1801
+ " New desktop"
1802
+ ]
1803
+ }
1804
+ ) : null
1417
1805
  ] })
1418
1806
  ] })
1419
1807
  ] }),
1420
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-subtle", children: selected ? `${platformLabel(selected)} \xB7 ${placementLabel(selected)}` : "" }),
1808
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-fg-subtle", children: selected ? `${platformLabel(selected)} \xB7 ${placementLabel(selected)}` : "" }),
1809
+ /* @__PURE__ */ jsx(
1810
+ ComputerTargetPicker,
1811
+ {
1812
+ targets: props.targets,
1813
+ selectedTargetId: props.selectedTargetId,
1814
+ loading: props.targetsLoading,
1815
+ onSelect: props.onSelectTarget
1816
+ }
1817
+ ),
1421
1818
  /* @__PURE__ */ jsx(
1422
1819
  "button",
1423
1820
  {
1424
1821
  type: "button",
1425
1822
  onClick: props.onRefresh,
1426
- className: "grid size-7 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg",
1823
+ disabled: props.refreshing,
1824
+ "aria-busy": props.refreshing,
1825
+ className: "grid size-7 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
1427
1826
  "aria-label": "Refresh desktops",
1428
1827
  children: /* @__PURE__ */ jsx(RefreshCwIcon, { className: cn("size-3.5", props.refreshing && "animate-spin") })
1429
1828
  }
@@ -1434,7 +1833,7 @@ function ComputerToolbar(props) {
1434
1833
  type: "button",
1435
1834
  onClick: props.onCreate,
1436
1835
  disabled: props.creating,
1437
- className: "grid size-7 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
1836
+ className: "hidden size-7 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40 @sm/computer-viewer:grid",
1438
1837
  "aria-label": "Open a new desktop",
1439
1838
  children: props.creating ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
1440
1839
  }
@@ -1444,14 +1843,15 @@ function ComputerToolbar(props) {
1444
1843
  function ComputerSessionGroup(props) {
1445
1844
  if (props.sessions.length === 0) return null;
1446
1845
  return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
1447
- /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: props.label }),
1846
+ /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: props.label }),
1448
1847
  props.sessions.map((session) => {
1449
1848
  const interventionCount = props.interventionCounts.get(session.id) ?? 0;
1450
1849
  return /* @__PURE__ */ jsxs(
1451
1850
  "button",
1452
1851
  {
1453
1852
  type: "button",
1454
- onClick: () => props.onSelect(session.id),
1853
+ onClick: (event) => props.onSelect(session.id, event.detail === 0),
1854
+ "aria-pressed": session.id === props.selectedId,
1455
1855
  className: cn(
1456
1856
  "flex w-full items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
1457
1857
  session.id === props.selectedId && "bg-og-surface-2"
@@ -1468,7 +1868,7 @@ function ComputerSessionGroup(props) {
1468
1868
  ),
1469
1869
  /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1470
1870
  /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: session.name }),
1471
- /* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-subtle", children: [
1871
+ /* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: [
1472
1872
  platformLabel(session),
1473
1873
  " \xB7 ",
1474
1874
  placementLabel(session)
@@ -1489,32 +1889,81 @@ function MenuButton(props) {
1489
1889
  type: "button",
1490
1890
  onClick: props.onClick,
1491
1891
  disabled: props.disabled,
1492
- className: "flex h-7 flex-1 items-center justify-center gap-1 rounded-og-sm px-2 text-og-control text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
1892
+ className: "flex h-7 flex-1 items-center justify-center gap-1 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
1493
1893
  children: props.children
1494
1894
  }
1495
1895
  );
1496
1896
  }
1497
- function ComputerTargetRail(props) {
1498
- const visualTargets = props.targets.filter(isRenderableComputerView);
1499
- return /* @__PURE__ */ jsxs(
1500
- "div",
1501
- {
1502
- className: "flex h-10 shrink-0 items-center gap-1 overflow-x-auto border-b border-og-border bg-og-surface-0 px-2",
1503
- "aria-label": "Desktop views",
1504
- children: [
1505
- visualTargets.map((target) => /* @__PURE__ */ jsxs(
1897
+ function ComputerTargetPicker(props) {
1898
+ const screens = props.targets.filter((target) => target.kind === "screen");
1899
+ const advancedTargets = props.targets.filter(
1900
+ (target) => target.kind === "app" || target.kind === "window" && isRenderableComputerView(target)
1901
+ );
1902
+ const advancedRef = useViewerMenuDismiss(advancedTargets.length > 0);
1903
+ const selected = props.targets.find((target) => target.id === props.selectedTargetId);
1904
+ const choose = (id, restoreFocus = false) => {
1905
+ props.onSelect(id);
1906
+ if (advancedRef.current?.open) {
1907
+ advancedRef.current.removeAttribute("open");
1908
+ if (restoreFocus) advancedRef.current.querySelector("summary")?.focus();
1909
+ }
1910
+ };
1911
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1912
+ screens.length > 1 ? /* @__PURE__ */ jsxs(
1913
+ "select",
1914
+ {
1915
+ "aria-label": "Screen",
1916
+ value: selected?.kind === "screen" ? selected.id : "",
1917
+ onChange: (event) => choose(event.currentTarget.value),
1918
+ className: "h-7 min-w-0 max-w-28 rounded-og-sm border border-og-border bg-og-surface-1 px-1.5 text-og-control text-og-fg outline-hidden focus:border-og-accent @sm/computer-viewer:max-w-40",
1919
+ children: [
1920
+ /* @__PURE__ */ jsx("option", { value: "", disabled: true, hidden: true, children: "Screen" }),
1921
+ screens.map((screen, index) => /* @__PURE__ */ jsx("option", { value: screen.id, children: screen.title || `Screen ${index + 1}` }, screen.id))
1922
+ ]
1923
+ }
1924
+ ) : screens[0] && selected?.kind !== "screen" ? /* @__PURE__ */ jsxs(
1925
+ "button",
1926
+ {
1927
+ type: "button",
1928
+ onClick: () => choose(screens[0].id),
1929
+ className: "flex h-7 shrink-0 items-center gap-1.5 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
1930
+ children: [
1931
+ /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5" }),
1932
+ " Full desktop"
1933
+ ]
1934
+ }
1935
+ ) : null,
1936
+ advancedTargets.length > 0 ? /* @__PURE__ */ jsxs("details", { ref: advancedRef, className: "shrink-0", children: [
1937
+ /* @__PURE__ */ jsxs(
1938
+ "summary",
1939
+ {
1940
+ "aria-label": "Advanced desktop views",
1941
+ title: "Advanced desktop views",
1942
+ className: "flex h-7 cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden",
1943
+ children: [
1944
+ /* @__PURE__ */ jsx(PanelsTopLeftIcon, { className: "size-3.5" }),
1945
+ /* @__PURE__ */ jsx("span", { className: "hidden @sm/computer-viewer:inline", children: "Advanced" }),
1946
+ /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3" })
1947
+ ]
1948
+ }
1949
+ ),
1950
+ /* @__PURE__ */ jsxs("div", { className: "absolute right-2 top-10 z-30 flex max-h-[calc(100%-3rem)] w-64 max-w-[calc(100%-1rem)] flex-col overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 shadow-xl", children: [
1951
+ /* @__PURE__ */ jsx("p", { className: "shrink-0 px-2 py-1 text-og-xs text-og-fg-subtle", children: "Apps and windows" }),
1952
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 max-h-80 overflow-y-auto overscroll-contain", children: advancedTargets.map((target) => /* @__PURE__ */ jsxs(
1506
1953
  "button",
1507
1954
  {
1508
1955
  type: "button",
1509
- onClick: () => props.onSelect(target.id),
1956
+ onClick: (event) => choose(target.id, event.detail === 0),
1957
+ "aria-pressed": target.id === props.selectedTargetId,
1510
1958
  className: cn(
1511
- "flex h-7 max-w-56 shrink-0 items-center gap-1.5 rounded-og-sm border px-2 text-og-control transition",
1512
- target.id === props.selectedTargetId ? "border-og-border-strong bg-og-surface-2 text-og-fg" : "border-transparent text-og-muted hover:bg-og-surface-1 hover:text-og-fg"
1959
+ "flex w-full min-w-0 items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
1960
+ target.id === props.selectedTargetId && "bg-og-surface-2"
1513
1961
  ),
1514
- "aria-pressed": target.id === props.selectedTargetId,
1515
1962
  children: [
1516
- target.kind === "screen" ? /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0" }) : /* @__PURE__ */ jsx(PanelsTopLeftIcon, { className: "size-3.5 shrink-0" }),
1517
- /* @__PURE__ */ jsx("span", { className: "truncate", children: target.title || target.applicationId || target.kind }),
1963
+ /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
1964
+ /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: target.title || target.applicationId || target.kind }),
1965
+ /* @__PURE__ */ jsx("span", { className: "block text-og-xs text-og-fg-subtle", children: target.kind === "app" ? "App controls" : "Window view" })
1966
+ ] }),
1518
1967
  target.focused ? /* @__PURE__ */ jsx(
1519
1968
  "span",
1520
1969
  {
@@ -1525,12 +1974,11 @@ function ComputerTargetRail(props) {
1525
1974
  ]
1526
1975
  },
1527
1976
  target.id
1528
- )),
1529
- props.loading ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "ml-1 size-3.5 animate-spin text-og-muted" }) : null,
1530
- !props.loading && visualTargets.length === 0 ? /* @__PURE__ */ jsx("span", { className: "text-og-xs text-og-subtle", children: "Waiting for apps and screens\u2026" }) : null
1531
- ]
1532
- }
1533
- );
1977
+ )) })
1978
+ ] })
1979
+ ] }) : null,
1980
+ props.loading ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 shrink-0 animate-spin text-og-fg-muted" }) : null
1981
+ ] });
1534
1982
  }
1535
1983
  function isRenderableComputerView(target) {
1536
1984
  if (target.kind === "screen") return true;
@@ -1545,9 +1993,9 @@ function ComputerLifecyclePanel(props) {
1545
1993
  const failed = ["failed", "lost", "repair_required"].includes(props.session.lifecycle);
1546
1994
  const retryCreatesSession = ["failed", "lost"].includes(props.session.lifecycle);
1547
1995
  return /* @__PURE__ */ jsx("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
1548
- failed ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-status-error" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mx-auto size-5 animate-spin text-og-muted" }),
1996
+ failed ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "mx-auto size-5 text-og-danger" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "mx-auto size-5 animate-spin text-og-fg-muted" }),
1549
1997
  /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : failed ? "Desktop needs attention" : lifecycleLabel(props.session.lifecycle) }),
1550
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : computerFailureMessage(props.session) ?? "The desktop is being prepared on its placement. It will appear here when ready." }),
1998
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: generationLoss ? "This Chrome profile is live again, but the previous desktop cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : computerFailureMessage(props.session) ?? "The desktop is being prepared on its placement. It will appear here when ready." }),
1551
1999
  failed && !generationLoss ? /* @__PURE__ */ jsxs(
1552
2000
  "button",
1553
2001
  {
@@ -1564,25 +2012,65 @@ function ComputerLifecyclePanel(props) {
1564
2012
  ] }) });
1565
2013
  }
1566
2014
  function ComputerViewport(props) {
1567
- const canvasRef = useRef4(null);
1568
- const inputRef = useRef4(null);
1569
- const pointerStartRef = useRef4(null);
1570
- const clickTimerRef = useRef4(null);
1571
- const lastClickRef = useRef4(null);
1572
- const wheelRef = useRef4(null);
1573
- const pendingTextRef = useRef4(null);
1574
- const actionRef = useRef4(props.onAction);
1575
- const readClipboardRef = useRef4(props.onReadClipboard);
1576
- const errorRef = useRef4(props.onError);
1577
- const actionTailRef = useRef4(Promise.resolve());
1578
- const queuedFrameRef = useRef4(null);
1579
- const decodingFrameRef = useRef4(false);
1580
- const mountedRef = useRef4(true);
2015
+ const canvasRef = useRef5(null);
2016
+ const inputRef = useRef5(null);
2017
+ const composingRef = useRef5(false);
2018
+ const pointerStartRef = useRef5(null);
2019
+ const clickTimerRef = useRef5(null);
2020
+ const lastClickRef = useRef5(null);
2021
+ const wheelRef = useRef5(null);
2022
+ const pendingTextRef = useRef5(null);
2023
+ const actionRef = useRef5(props.onAction);
2024
+ const targetRef = useRef5(props.target);
2025
+ const readClipboardRef = useRef5(props.onReadClipboard);
2026
+ const errorRef = useRef5(props.onError);
2027
+ const actionTailRef = useRef5(Promise.resolve());
2028
+ const actionQueueEpochRef = useRef5(0);
2029
+ const queuedTextRef = useRef5(null);
2030
+ const queuedFrameRef = useRef5(null);
2031
+ const currentFrameRef = useRef5(props.frame);
2032
+ const paintedFrameRef = useRef5(null);
2033
+ const [paintedFrame, setPaintedFrame] = useState5(null);
2034
+ const decodingFrameRef = useRef5(false);
2035
+ const mountedRef = useRef5(true);
1581
2036
  actionRef.current = props.onAction;
2037
+ targetRef.current = props.target;
1582
2038
  readClipboardRef.current = props.onReadClipboard;
1583
2039
  errorRef.current = props.onError;
1584
- const rawInputEnabled = !props.backgroundActions || props.target?.kind === "screen" || props.target?.focused === true;
1585
- const paintQueuedFrames = useCallback4(() => {
2040
+ const streamFailed = props.connectionState === "error";
2041
+ const rawInputEnabled = props.target?.kind !== "app" && !streamFailed && !props.controlUnavailable && !props.machineLocked && (props.backgroundInput || // CUA may activate within its allocated Linux desktop. This does not
2042
+ // grant foreground input to an attached physical desktop.
2043
+ props.allowForegroundInputWithinOwnedSeat || !props.backgroundActions || props.target?.kind === "screen" || props.target?.focused === true);
2044
+ const clearBufferedInput = useCallback5(() => {
2045
+ if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
2046
+ if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
2047
+ if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
2048
+ clickTimerRef.current = null;
2049
+ wheelRef.current = null;
2050
+ pendingTextRef.current = null;
2051
+ queuedTextRef.current = null;
2052
+ pointerStartRef.current = null;
2053
+ lastClickRef.current = null;
2054
+ composingRef.current = false;
2055
+ if (inputRef.current) inputRef.current.value = "";
2056
+ }, []);
2057
+ const pointerInputEnabled = rawInputEnabled && props.pointerInput;
2058
+ const keyboardInputEnabled = rawInputEnabled && props.keyboardInput;
2059
+ useLayoutEffect(() => {
2060
+ const canvas = canvasRef.current;
2061
+ const viewport = canvas?.parentElement;
2062
+ if (!canvas || !viewport) return;
2063
+ const fit = () => fitComputerCanvas(canvas);
2064
+ fit();
2065
+ if (typeof ResizeObserver === "undefined") {
2066
+ window.addEventListener("resize", fit);
2067
+ return () => window.removeEventListener("resize", fit);
2068
+ }
2069
+ const observer = new ResizeObserver(fit);
2070
+ observer.observe(viewport);
2071
+ return () => observer.disconnect();
2072
+ }, []);
2073
+ const paintQueuedFrames = useCallback5(() => {
1586
2074
  if (decodingFrameRef.current) return;
1587
2075
  decodingFrameRef.current = true;
1588
2076
  void (async () => {
@@ -1598,8 +2086,10 @@ function ComputerViewport(props) {
1598
2086
  const bitmap = await createImageBitmap(blob);
1599
2087
  try {
1600
2088
  const canvas2 = canvasRef.current;
1601
- if (mountedRef.current && canvas2) {
2089
+ if (mountedRef.current && canvas2 && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current)) {
1602
2090
  paintCanvas(canvas2, bitmap, frame.width, frame.height);
2091
+ paintedFrameRef.current = frame;
2092
+ setPaintedFrame(frame);
1603
2093
  }
1604
2094
  } finally {
1605
2095
  bitmap.close();
@@ -1609,11 +2099,14 @@ function ComputerViewport(props) {
1609
2099
  objectUrl = URL.createObjectURL(blob);
1610
2100
  const image = await loadImage(objectUrl);
1611
2101
  const canvas = canvasRef.current;
1612
- if (mountedRef.current && canvas) {
2102
+ if (mountedRef.current && canvas && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current)) {
1613
2103
  paintCanvas(canvas, image, frame.width, frame.height);
2104
+ paintedFrameRef.current = frame;
2105
+ setPaintedFrame(frame);
1614
2106
  }
1615
2107
  } catch (cause) {
1616
- if (mountedRef.current) errorRef.current(cause);
2108
+ if (mountedRef.current && currentFrameRef.current && sameComputerTarget(frame, currentFrameRef.current))
2109
+ errorRef.current(cause);
1617
2110
  } finally {
1618
2111
  if (objectUrl) URL.revokeObjectURL(objectUrl);
1619
2112
  }
@@ -1624,52 +2117,109 @@ function ComputerViewport(props) {
1624
2117
  }
1625
2118
  })();
1626
2119
  }, []);
1627
- useEffect4(() => {
2120
+ useLayoutEffect(() => {
1628
2121
  mountedRef.current = true;
1629
2122
  return () => {
1630
2123
  mountedRef.current = false;
2124
+ actionQueueEpochRef.current += 1;
1631
2125
  queuedFrameRef.current = null;
2126
+ clearBufferedInput();
1632
2127
  };
1633
- }, []);
1634
- useEffect4(() => {
1635
- if (!props.frame) return;
2128
+ }, [clearBufferedInput]);
2129
+ useLayoutEffect(() => {
2130
+ if (currentFrameRef.current && !props.frame) {
2131
+ actionQueueEpochRef.current += 1;
2132
+ clearBufferedInput();
2133
+ }
2134
+ currentFrameRef.current = props.frame;
2135
+ if (!props.frame) {
2136
+ queuedFrameRef.current = null;
2137
+ paintedFrameRef.current = null;
2138
+ setPaintedFrame(null);
2139
+ return;
2140
+ }
1636
2141
  queuedFrameRef.current = props.frame;
1637
2142
  paintQueuedFrames();
1638
- }, [paintQueuedFrames, props.frame]);
1639
- useEffect4(
1640
- () => () => {
1641
- if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
1642
- if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
1643
- if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
1644
- },
1645
- []
1646
- );
1647
- const enqueue = useCallback4(
1648
- (action, frame, after) => {
1649
- actionTailRef.current = actionTailRef.current.catch(() => void 0).then(async () => {
1650
- await actionRef.current(action, frame);
1651
- await after?.();
1652
- }).catch((cause) => errorRef.current(cause));
2143
+ }, [clearBufferedInput, paintQueuedFrames, props.frame]);
2144
+ useLayoutEffect(() => {
2145
+ if (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) {
2146
+ actionQueueEpochRef.current += 1;
2147
+ clearBufferedInput();
2148
+ }
2149
+ }, [clearBufferedInput, props.keyboardInput, props.pointerInput, rawInputEnabled]);
2150
+ const enqueue = useCallback5(
2151
+ (action, frame, after, continuation) => {
2152
+ const epoch = actionQueueEpochRef.current;
2153
+ const plainText = action.type === "keyboard" && action.action === "type";
2154
+ const queuedText = queuedTextRef.current;
2155
+ if (plainText && !frame && !after && !continuation && queuedText?.epoch === epoch && queuedText.delivery.completion === actionTailRef.current && queuedText.action.value.length + action.value.length <= 1e6) {
2156
+ queuedText.action.value += action.value;
2157
+ return queuedText.delivery;
2158
+ }
2159
+ queuedTextRef.current = null;
2160
+ const dispatch = actionRef.current;
2161
+ const admittedTarget = targetRef.current;
2162
+ const isCurrent = () => mountedRef.current && epoch === actionQueueEpochRef.current;
2163
+ const operationId = crypto.randomUUID();
2164
+ let markStarted;
2165
+ const started = new Promise((resolve) => {
2166
+ markStarted = resolve;
2167
+ });
2168
+ const previous = actionTailRef.current;
2169
+ const ready = continuation && previous === continuation.completion ? continuation.started : previous.then(() => true);
2170
+ const delivery = ready.then(async (firstStarted) => {
2171
+ if (queuedTextRef.current?.action === action) queuedTextRef.current = null;
2172
+ if (!firstStarted || !isCurrent()) {
2173
+ markStarted(false);
2174
+ return;
2175
+ }
2176
+ const pending = dispatch(action, frame, operationId);
2177
+ markStarted(true);
2178
+ const receipt = await pending;
2179
+ const isCurrentResult = () => isCurrent() && !isStaleComputerActionResult(receipt);
2180
+ if (!isCurrentResult()) return;
2181
+ const observedTarget = receipt.observation?.target;
2182
+ if (!admittedTarget || receipt.computerSessionId !== admittedTarget.computerSessionId || receipt.controllerGeneration !== admittedTarget.controllerGeneration || receipt.targetId !== admittedTarget.id || observedTarget && (observedTarget.computerSessionId !== admittedTarget.computerSessionId || observedTarget.controllerGeneration !== admittedTarget.controllerGeneration || observedTarget.id !== admittedTarget.id || observedTarget.targetGeneration !== admittedTarget.targetGeneration)) {
2183
+ actionQueueEpochRef.current += 1;
2184
+ clearBufferedInput();
2185
+ return;
2186
+ }
2187
+ await after?.(isCurrentResult);
2188
+ }).catch((cause) => {
2189
+ markStarted(false);
2190
+ if (!isCurrent() || isStaleComputerActionResult(cause)) return;
2191
+ actionQueueEpochRef.current += 1;
2192
+ clearBufferedInput();
2193
+ errorRef.current(cause);
2194
+ });
2195
+ const completion = continuation ? Promise.all([previous, delivery]).then(() => void 0) : delivery;
2196
+ actionTailRef.current = completion;
2197
+ const queued = { operationId, started, completion };
2198
+ if (plainText && !frame && !after && !continuation) {
2199
+ queuedTextRef.current = { action, epoch, delivery: queued };
2200
+ }
2201
+ return queued;
1653
2202
  },
1654
- []
2203
+ [clearBufferedInput]
1655
2204
  );
1656
- const point = useCallback4(
2205
+ const point = useCallback5(
1657
2206
  (frame, clientX, clientY) => computerPoint(canvasRef.current, frame, clientX, clientY),
1658
2207
  []
1659
2208
  );
1660
- const flushPendingText = useCallback4(() => {
2209
+ const flushPendingText = useCallback5(() => {
1661
2210
  const pending = pendingTextRef.current;
1662
2211
  if (!pending) return;
1663
2212
  clearTimeout(pending.timer);
1664
2213
  pendingTextRef.current = null;
1665
2214
  enqueue({ type: "keyboard", action: "type", value: pending.text }, null);
1666
2215
  }, [enqueue]);
1667
- const flushPendingClick = useCallback4(() => {
2216
+ const flushPendingClick = useCallback5(() => {
1668
2217
  const pending = lastClickRef.current;
1669
2218
  if (!pending) return;
1670
2219
  if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
1671
2220
  clickTimerRef.current = null;
1672
2221
  lastClickRef.current = null;
2222
+ if (!pending.pending) return;
1673
2223
  enqueue(
1674
2224
  {
1675
2225
  type: "pointer",
@@ -1681,23 +2231,47 @@ function ComputerViewport(props) {
1681
2231
  pending.frame
1682
2232
  );
1683
2233
  }, [enqueue]);
2234
+ const flushPendingWheel = useCallback5(() => {
2235
+ const batch = wheelRef.current;
2236
+ if (!batch) return;
2237
+ if (batch.timer) clearTimeout(batch.timer);
2238
+ wheelRef.current = null;
2239
+ enqueue(
2240
+ {
2241
+ type: "pointer",
2242
+ frameId: batch.frame.frameId,
2243
+ action: "scroll",
2244
+ x: batch.x,
2245
+ y: batch.y,
2246
+ deltaX: batch.deltaX,
2247
+ deltaY: batch.deltaY
2248
+ },
2249
+ batch.frame
2250
+ );
2251
+ }, [enqueue]);
1684
2252
  const pointerDown = (event) => {
1685
- if (!props.frame || props.mutating || !rawInputEnabled || event.button !== 0) return;
2253
+ const frame = paintedFrameRef.current;
2254
+ if (!frame || !props.pointerClickContinuation && props.mutating || !rawInputEnabled || event.button !== 0)
2255
+ return;
2256
+ event.preventDefault();
2257
+ if (keyboardInputEnabled) inputRef.current?.focus({ preventScroll: true });
2258
+ if (!pointerInputEnabled) return;
2259
+ flushPendingWheel();
1686
2260
  flushPendingText();
1687
2261
  pointerStartRef.current = {
1688
2262
  x: event.clientX,
1689
2263
  y: event.clientY,
1690
2264
  pointerId: event.pointerId,
1691
- frame: props.frame
2265
+ frame
1692
2266
  };
1693
2267
  event.currentTarget.setPointerCapture?.(event.pointerId);
1694
- inputRef.current?.focus({ preventScroll: true });
1695
2268
  };
1696
2269
  const pointerUp = (event) => {
1697
- if (!rawInputEnabled) return;
2270
+ if (!pointerInputEnabled) return;
1698
2271
  const start = pointerStartRef.current;
1699
2272
  pointerStartRef.current = null;
1700
2273
  if (!start || start.pointerId !== event.pointerId) return;
2274
+ const canvasRect = event.currentTarget.getBoundingClientRect();
1701
2275
  const from = point(start.frame, start.x, start.y);
1702
2276
  const to = point(start.frame, event.clientX, event.clientY);
1703
2277
  if (!from || !to) return;
@@ -1719,7 +2293,7 @@ function ComputerViewport(props) {
1719
2293
  }
1720
2294
  const now = Date.now();
1721
2295
  const previous = lastClickRef.current;
1722
- if (previous && now - previous.at < 280 && Math.hypot(previous.x - to.x, previous.y - to.y) < 6 && sameFrameFence(previous.frame, start.frame)) {
2296
+ if (previous && now - previous.at < 280 && previous.pending === !props.pointerClickContinuation && (props.pointerClickContinuation ? Math.hypot(previous.clientX - event.clientX, previous.clientY - event.clientY) < 6 && previous.canvasRect.left === canvasRect.left && previous.canvasRect.top === canvasRect.top && previous.canvasRect.width === canvasRect.width && previous.canvasRect.height === canvasRect.height && sameComputerTarget(previous.frame, start.frame) && previous.frame.width === start.frame.width && previous.frame.height === start.frame.height : Math.hypot(previous.x - to.x, previous.y - to.y) < 6 && sameFrameFence(previous.frame, start.frame))) {
1723
2297
  if (clickTimerRef.current) clearTimeout(clickTimerRef.current);
1724
2298
  clickTimerRef.current = null;
1725
2299
  lastClickRef.current = null;
@@ -1727,15 +2301,45 @@ function ComputerViewport(props) {
1727
2301
  {
1728
2302
  type: "pointer",
1729
2303
  frameId: start.frame.frameId,
1730
- action: "double_click",
2304
+ action: props.pointerClickContinuation ? "click" : "double_click",
2305
+ ...props.pointerClickContinuation ? { clickCount: 2 } : {},
2306
+ ...previous.delivery ? { continuationOfOperationId: previous.delivery.operationId } : {},
2307
+ x: to.x,
2308
+ y: to.y
2309
+ },
2310
+ start.frame,
2311
+ void 0,
2312
+ previous.delivery ?? void 0
2313
+ );
2314
+ return;
2315
+ }
2316
+ flushPendingClick();
2317
+ lastClickRef.current = {
2318
+ at: now,
2319
+ x: to.x,
2320
+ y: to.y,
2321
+ frame: start.frame,
2322
+ clientX: event.clientX,
2323
+ clientY: event.clientY,
2324
+ canvasRect,
2325
+ pending: !props.pointerClickContinuation,
2326
+ delivery: null
2327
+ };
2328
+ if (props.pointerClickContinuation) {
2329
+ const delivery = enqueue(
2330
+ {
2331
+ type: "pointer",
2332
+ frameId: start.frame.frameId,
2333
+ action: "click",
2334
+ clickCount: 1,
1731
2335
  x: to.x,
1732
2336
  y: to.y
1733
2337
  },
1734
2338
  start.frame
1735
2339
  );
2340
+ if (lastClickRef.current) lastClickRef.current.delivery = delivery;
1736
2341
  return;
1737
2342
  }
1738
- lastClickRef.current = { at: now, x: to.x, y: to.y, frame: start.frame };
1739
2343
  clickTimerRef.current = setTimeout(() => {
1740
2344
  clickTimerRef.current = null;
1741
2345
  lastClickRef.current = null;
@@ -1753,9 +2357,10 @@ function ComputerViewport(props) {
1753
2357
  };
1754
2358
  const contextMenu = (event) => {
1755
2359
  event.preventDefault();
1756
- if (!rawInputEnabled) return;
1757
- const frame = props.frame;
2360
+ if (!pointerInputEnabled) return;
2361
+ const frame = paintedFrameRef.current;
1758
2362
  if (!frame) return;
2363
+ flushPendingWheel();
1759
2364
  flushPendingText();
1760
2365
  flushPendingClick();
1761
2366
  const at = point(frame, event.clientX, event.clientY);
@@ -1774,44 +2379,34 @@ function ComputerViewport(props) {
1774
2379
  }
1775
2380
  };
1776
2381
  const wheel = (event) => {
1777
- if (!rawInputEnabled) return;
1778
- const frame = props.frame;
2382
+ if (!pointerInputEnabled) return;
2383
+ const frame = paintedFrameRef.current;
1779
2384
  if (!frame) return;
1780
2385
  const at = point(frame, event.clientX, event.clientY);
1781
2386
  if (!at) return;
1782
2387
  flushPendingText();
1783
2388
  flushPendingClick();
1784
2389
  event.preventDefault();
1785
- const pending = wheelRef.current;
1786
- if (pending?.timer) clearTimeout(pending.timer);
2390
+ let pending = wheelRef.current;
2391
+ if (pending && (!sameComputerTarget(pending.frame, frame) || Math.hypot(pending.x - at.x, pending.y - at.y) > 6)) {
2392
+ flushPendingWheel();
2393
+ pending = null;
2394
+ }
1787
2395
  wheelRef.current = {
1788
2396
  x: at.x,
1789
2397
  y: at.y,
1790
- deltaX: (pending && sameFrameFence(pending.frame, frame) ? pending.deltaX : 0) + event.deltaX,
1791
- deltaY: (pending && sameFrameFence(pending.frame, frame) ? pending.deltaY : 0) + event.deltaY,
2398
+ deltaX: (pending?.deltaX ?? 0) + event.deltaX,
2399
+ deltaY: (pending?.deltaY ?? 0) + event.deltaY,
1792
2400
  frame,
1793
- timer: setTimeout(() => {
1794
- const batch = wheelRef.current;
1795
- wheelRef.current = null;
1796
- if (batch) {
1797
- enqueue(
1798
- {
1799
- type: "pointer",
1800
- frameId: batch.frame.frameId,
1801
- action: "scroll",
1802
- x: batch.x,
1803
- y: batch.y,
1804
- deltaX: batch.deltaX,
1805
- deltaY: batch.deltaY
1806
- },
1807
- batch.frame
1808
- );
1809
- }
1810
- }, 45)
2401
+ timer: pending?.timer ?? setTimeout(flushPendingWheel, 45)
1811
2402
  };
1812
2403
  };
1813
2404
  const keyDown = (event) => {
1814
- if (!rawInputEnabled) return;
2405
+ if (!keyboardInputEnabled) return;
2406
+ if (composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) {
2407
+ return;
2408
+ }
2409
+ flushPendingWheel();
1815
2410
  const command = event.metaKey || event.ctrlKey;
1816
2411
  if (props.clipboardEnabled && command && !event.altKey && ["c", "v"].includes(event.key.toLowerCase())) {
1817
2412
  flushPendingText();
@@ -1825,12 +2420,15 @@ function ComputerViewport(props) {
1825
2420
  enqueue({ type: "keyboard", action: "press", value: key }, null);
1826
2421
  };
1827
2422
  const copy = (event) => {
1828
- if (!props.clipboardEnabled || !rawInputEnabled) return;
2423
+ if (!props.clipboardEnabled || !keyboardInputEnabled) return;
1829
2424
  event.preventDefault();
2425
+ flushPendingWheel();
1830
2426
  flushPendingText();
1831
2427
  flushPendingClick();
1832
- enqueue({ type: "clipboard", operation: "copy" }, null, async () => {
1833
- const clipboard = await readClipboardRef.current();
2428
+ const readClipboard = readClipboardRef.current;
2429
+ enqueue({ type: "clipboard", operation: "copy" }, null, async (isCurrent) => {
2430
+ const clipboard = await readClipboard();
2431
+ if (!isCurrent()) return;
1834
2432
  if (!clipboard.text) return;
1835
2433
  if (!await copyTextToClipboard(clipboard.text)) {
1836
2434
  throw new Error("Desktop text could not be copied to the local clipboard");
@@ -1838,18 +2436,25 @@ function ComputerViewport(props) {
1838
2436
  });
1839
2437
  };
1840
2438
  const paste = (event) => {
1841
- if (!props.clipboardEnabled || !rawInputEnabled) return;
2439
+ if (!props.clipboardEnabled || !keyboardInputEnabled) return;
1842
2440
  event.preventDefault();
2441
+ flushPendingWheel();
1843
2442
  flushPendingText();
1844
2443
  flushPendingClick();
1845
2444
  const text = event.clipboardData.getData("text/plain");
1846
- enqueue({ type: "clipboard", operation: "write", text }, null, async () => {
1847
- assertExactComputerClipboard(await readClipboardRef.current(), text);
1848
- await actionRef.current({ type: "clipboard", operation: "paste" }, null);
2445
+ const readClipboard = readClipboardRef.current;
2446
+ const dispatch = actionRef.current;
2447
+ enqueue({ type: "clipboard", operation: "write", text }, null, async (isCurrent) => {
2448
+ const clipboard = await readClipboard();
2449
+ if (!isCurrent()) return;
2450
+ assertExactComputerClipboard(clipboard, text);
2451
+ await dispatch({ type: "clipboard", operation: "paste" }, null);
1849
2452
  });
1850
2453
  };
1851
- const input = (value) => {
1852
- if (!value || !rawInputEnabled) return;
2454
+ const input = (value, nativeComposing = false) => {
2455
+ if (composingRef.current || nativeComposing) return;
2456
+ if (!value || !keyboardInputEnabled) return;
2457
+ flushPendingWheel();
1853
2458
  flushPendingClick();
1854
2459
  const pending = pendingTextRef.current;
1855
2460
  if (pending) {
@@ -1864,7 +2469,11 @@ function ComputerViewport(props) {
1864
2469
  }
1865
2470
  if (inputRef.current) inputRef.current.value = "";
1866
2471
  };
1867
- const showCanvas = props.frame !== null && !props.machineLocked;
2472
+ const compositionEnd = (event) => {
2473
+ composingRef.current = false;
2474
+ input(event.currentTarget.value || event.data);
2475
+ };
2476
+ const showCanvas = props.frame !== null && paintedFrame !== null && sameComputerTarget(props.frame, paintedFrame) && !props.machineLocked && !streamFailed;
1868
2477
  return /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
1869
2478
  /* @__PURE__ */ jsx(
1870
2479
  "canvas",
@@ -1873,7 +2482,7 @@ function ComputerViewport(props) {
1873
2482
  className: cn(
1874
2483
  "absolute inset-0 m-auto max-h-full max-w-full touch-none focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent",
1875
2484
  !showCanvas && "invisible",
1876
- !rawInputEnabled && "cursor-default"
2485
+ !pointerInputEnabled && "cursor-default"
1877
2486
  ),
1878
2487
  onPointerDown: pointerDown,
1879
2488
  onPointerUp: pointerUp,
@@ -1890,7 +2499,11 @@ function ComputerViewport(props) {
1890
2499
  {
1891
2500
  ref: inputRef,
1892
2501
  defaultValue: "",
1893
- onInput: (event) => input(event.currentTarget.value),
2502
+ onInput: (event) => input(event.currentTarget.value, event.nativeEvent.isComposing),
2503
+ onCompositionStart: () => {
2504
+ composingRef.current = true;
2505
+ },
2506
+ onCompositionEnd: compositionEnd,
1894
2507
  onKeyDown: keyDown,
1895
2508
  onCopy: copy,
1896
2509
  onPaste: paste,
@@ -1899,7 +2512,7 @@ function ComputerViewport(props) {
1899
2512
  autoCapitalize: "off",
1900
2513
  autoCorrect: "off",
1901
2514
  spellCheck: false,
1902
- disabled: !rawInputEnabled
2515
+ disabled: !keyboardInputEnabled
1903
2516
  }
1904
2517
  ),
1905
2518
  !showCanvas ? /* @__PURE__ */ jsx(
@@ -1907,6 +2520,8 @@ function ComputerViewport(props) {
1907
2520
  {
1908
2521
  observation: props.observation,
1909
2522
  machineLocked: props.machineLocked,
2523
+ controlUnavailable: props.controlUnavailable,
2524
+ inputAllowed: props.inputAllowed,
1910
2525
  connectionState: props.connectionState,
1911
2526
  error: props.connectionError,
1912
2527
  onAction: (action) => enqueue(action, null),
@@ -1917,16 +2532,16 @@ function ComputerViewport(props) {
1917
2532
  /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }),
1918
2533
  " Acting"
1919
2534
  ] }) : null,
1920
- showCanvas && !rawInputEnabled ? /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-1/2 flex -translate-x-1/2 items-center gap-2 rounded-full border border-white/10 bg-black/70 py-1 pl-3 pr-1 text-[11px] text-white/80 backdrop-blur", children: [
1921
- /* @__PURE__ */ jsx("span", { children: "Background view \xB7 use native controls" }),
1922
- props.target ? /* @__PURE__ */ jsx(
2535
+ showCanvas && (!rawInputEnabled || !props.pointerInput || !props.keyboardInput) ? /* @__PURE__ */ jsxs("div", { className: "absolute bottom-3 left-1/2 flex w-max max-w-[calc(100%-1.5rem)] -translate-x-1/2 items-center gap-2 rounded-full border border-white/10 bg-black/70 py-1 pl-3 pr-1 text-[11px] text-white/80 backdrop-blur", children: [
2536
+ /* @__PURE__ */ jsx("span", { children: !rawInputEnabled && (props.pointerInput || props.keyboardInput) ? "Background view \xB7 use app controls" : !props.pointerInput && !props.keyboardInput ? "View only \xB7 mouse and keyboard unavailable" : props.keyboardInput ? "Keyboard only \xB7 mouse unavailable" : "Mouse only \xB7 keyboard unavailable" }),
2537
+ !rawInputEnabled && props.target && (props.pointerInput || props.keyboardInput) ? /* @__PURE__ */ jsx(
1923
2538
  "button",
1924
2539
  {
1925
2540
  type: "button",
1926
2541
  disabled: props.mutating,
1927
2542
  onClick: () => enqueue({ type: "focus", targetId: props.target.id }, null),
1928
- className: "rounded-full bg-white/10 px-2 py-0.5 font-medium text-white transition hover:bg-white/20 disabled:opacity-50",
1929
- children: "Control directly"
2543
+ className: "shrink-0 whitespace-nowrap rounded-full bg-white/10 px-2 py-0.5 font-medium text-white transition hover:bg-white/20 disabled:opacity-50",
2544
+ children: "Bring to front"
1930
2545
  }
1931
2546
  ) : null
1932
2547
  ] }) : null
@@ -1940,21 +2555,23 @@ function assertExactComputerClipboard(clipboard, expected) {
1940
2555
  function ComputerViewportFallback(props) {
1941
2556
  if (props.machineLocked) {
1942
2557
  return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
1943
- /* @__PURE__ */ jsx(LockKeyholeIcon, { className: "mx-auto size-6 text-og-muted" }),
2558
+ /* @__PURE__ */ jsx(LockKeyholeIcon, { className: "mx-auto size-6 text-og-fg-muted" }),
1944
2559
  /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "Machine locked" }),
1945
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: "Live frames and input stay private until someone unlocks the machine." })
2560
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: "Live frames and input stay private until someone unlocks the machine." })
1946
2561
  ] }) });
1947
2562
  }
2563
+ const appControls = props.observation?.target.kind === "app";
1948
2564
  const controlFailure = interactionControlFailureFromError(props.error);
1949
- const interactive = semanticNodes(props.observation).filter((node) => semanticAction(node) !== null).slice(0, 10);
1950
- return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-0 p-4 shadow-lg", children: [
2565
+ const interactive = semanticControls(props.observation).filter((node) => semanticAction(node) !== null).slice(0, 10);
2566
+ return /* @__PURE__ */ jsx("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-1 p-4 shadow-lg", children: [
1951
2567
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
1952
- props.error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4 text-og-status-error" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4 animate-spin text-og-muted" }),
1953
- /* @__PURE__ */ jsx("p", { className: "text-og-menu font-medium text-og-fg", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop." : props.error ? "Live view disconnected" : computerConnectionLabel(props.connectionState) })
2568
+ props.error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4 text-og-danger" }) : appControls ? /* @__PURE__ */ jsx(PanelsTopLeftIcon, { className: "size-4 text-og-fg-muted" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4 animate-spin text-og-fg-muted" }),
2569
+ /* @__PURE__ */ jsx("p", { className: "text-og-menu font-medium text-og-fg", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop." : !props.inputAllowed ? "View only" : props.controlUnavailable ? "Desktop controls unavailable" : props.error ? "Live view disconnected" : appControls ? "App controls" : computerConnectionLabel(props.connectionState) })
1954
2570
  ] }),
1955
- props.error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-muted", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : controlFailure?.message ?? props.error.message }) : null,
1956
- interactive.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-3", children: [
1957
- /* @__PURE__ */ jsx("p", { className: "mb-2 text-og-xs text-og-subtle", children: "Native controls remain available" }),
2571
+ props.error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: isAttachedChromeGenerationLossError(props.error) ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : controlFailure?.message ?? props.error.message }) : null,
2572
+ !props.inputAllowed ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: "Refresh to check desktop controls." }) : null,
2573
+ interactive.length > 0 && !props.controlUnavailable && props.inputAllowed ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-3", children: [
2574
+ /* @__PURE__ */ jsx("p", { className: "mb-2 text-og-xs text-og-fg-subtle", children: "App controls remain available" }),
1958
2575
  /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: interactive.map((node) => /* @__PURE__ */ jsx(
1959
2576
  "button",
1960
2577
  {
@@ -1974,20 +2591,21 @@ function ComputerViewportFallback(props) {
1974
2591
  {
1975
2592
  type: "button",
1976
2593
  onClick: props.onReconnect,
1977
- className: "mt-3 text-og-control font-medium text-og-accent hover:underline",
2594
+ className: "mt-3 text-og-control font-medium og-link",
1978
2595
  children: controlFailure?.retryable === false ? "Try again" : "Reconnect"
1979
2596
  }
1980
2597
  ) : null
1981
2598
  ] }) });
1982
2599
  }
1983
2600
  function ComputerSemanticPanel(props) {
1984
- const nodes = semanticNodes(props.observation).slice(0, 100);
1985
- return /* @__PURE__ */ jsxs("aside", { className: "w-64 shrink-0 overflow-y-auto border-l border-og-border bg-og-surface-0 p-2", children: [
1986
- /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-1.5 px-1 text-og-xs font-medium uppercase tracking-[0.1em] text-og-subtle", children: [
2601
+ const nodes = semanticControls(props.observation).slice(0, 100);
2602
+ return /* @__PURE__ */ jsxs("aside", { className: "box-border max-h-[40%] w-full shrink-0 overflow-y-auto border-t border-og-border bg-og-surface-1 p-2 @xl/computer-viewer:max-h-none @xl/computer-viewer:w-64 @xl/computer-viewer:border-t-0 @xl/computer-viewer:border-l", children: [
2603
+ /* @__PURE__ */ jsxs("div", { className: "mb-2 flex items-center gap-1.5 px-1 text-og-xs font-medium uppercase tracking-[0.1em] text-og-fg-subtle", children: [
1987
2604
  /* @__PURE__ */ jsx(KeyboardIcon, { className: "size-3" }),
1988
- " Native controls"
2605
+ " App controls"
1989
2606
  ] }),
1990
- nodes.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-muted", children: "This view has no accessibility controls. Use the live image instead." }) : /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: nodes.map((node) => /* @__PURE__ */ jsx(
2607
+ !props.inputAllowed ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "View only \xB7 refresh to check desktop controls." }) : props.controlUnavailable ? /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "Reconnect to use app controls." }) : null,
2608
+ nodes.length === 0 ? props.controlUnavailable ? null : /* @__PURE__ */ jsx("p", { className: "px-1 py-2 text-og-control leading-5 text-og-fg-muted", children: "This view has no accessibility controls. Use the live image instead." }) : /* @__PURE__ */ jsx("div", { className: "space-y-0.5", children: nodes.map((node) => /* @__PURE__ */ jsx(
1991
2609
  ComputerSemanticControl,
1992
2610
  {
1993
2611
  node,
@@ -2003,8 +2621,8 @@ function ComputerSemanticControl(props) {
2003
2621
  const editable = node.actions.includes("set_value");
2004
2622
  const action = semanticAction(node);
2005
2623
  const observedValue = semanticValue(node) ?? "";
2006
- const [value, setValue] = useState4(() => observedValue);
2007
- useEffect4(() => {
2624
+ const [value, setValue] = useState5(() => observedValue);
2625
+ useEffect5(() => {
2008
2626
  setValue(observedValue);
2009
2627
  }, [node.ref, observedValue]);
2010
2628
  if (editable) {
@@ -2014,6 +2632,7 @@ function ComputerSemanticControl(props) {
2014
2632
  className: "rounded-og-sm px-2 py-1.5 hover:bg-og-surface-2",
2015
2633
  onSubmit: (event) => {
2016
2634
  event.preventDefault();
2635
+ if (props.mutating) return;
2017
2636
  props.onAction({
2018
2637
  type: "semantic",
2019
2638
  locator: { kind: "ref", ref: node.ref },
@@ -2022,7 +2641,7 @@ function ComputerSemanticControl(props) {
2022
2641
  });
2023
2642
  },
2024
2643
  children: [
2025
- /* @__PURE__ */ jsx("label", { className: "block truncate text-[10px] uppercase text-og-subtle", children: node.name || node.identifier || node.role }),
2644
+ /* @__PURE__ */ jsx("label", { className: "block truncate text-[10px] uppercase text-og-fg-subtle", children: node.name || node.identifier || node.role }),
2026
2645
  /* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1", children: [
2027
2646
  /* @__PURE__ */ jsx(
2028
2647
  "input",
@@ -2058,7 +2677,7 @@ function ComputerSemanticControl(props) {
2058
2677
  },
2059
2678
  className: "flex w-full items-start gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2 disabled:cursor-default disabled:hover:bg-transparent",
2060
2679
  children: [
2061
- /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] uppercase text-og-subtle", children: node.role }),
2680
+ /* @__PURE__ */ jsx("span", { className: "mt-0.5 text-[10px] uppercase text-og-fg-subtle", children: node.role }),
2062
2681
  /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-control text-og-fg", children: node.name || semanticValue(node) || node.identifier || "Unnamed control" })
2063
2682
  ]
2064
2683
  }
@@ -2066,7 +2685,7 @@ function ComputerSemanticControl(props) {
2066
2685
  }
2067
2686
  function ComputerStatusBar(props) {
2068
2687
  const screen = props.target?.kind === "screen";
2069
- return /* @__PURE__ */ jsxs("div", { className: "flex h-8 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-0 px-2 text-og-xs text-og-subtle", children: [
2688
+ return /* @__PURE__ */ jsxs("div", { className: "flex h-8 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-2 text-og-xs text-og-fg-subtle", children: [
2070
2689
  /* @__PURE__ */ jsx(
2071
2690
  "span",
2072
2691
  {
@@ -2076,11 +2695,11 @@ function ComputerStatusBar(props) {
2076
2695
  )
2077
2696
  }
2078
2697
  ),
2079
- /* @__PURE__ */ jsx("span", { children: props.connectionState === "live" ? "Live" : computerConnectionLabel(props.connectionState) }),
2080
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: screen ? "Full screen \xB7 input may move pointer and focus" : props.session?.capabilities?.backgroundActions ? "Window \xB7 semantic controls stay in the background" : props.target?.kind ?? "Desktop" }),
2698
+ /* @__PURE__ */ jsx("span", { children: !props.inputAllowed ? "View only" : props.controlUnavailable ? "Controls unavailable" : props.target?.kind === "app" ? "App controls" : props.connectionState === "live" ? "Live" : computerConnectionLabel(props.connectionState) }),
2699
+ /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: !props.inputAllowed ? "Refresh to check desktop controls" : props.controlUnavailable ? "Reconnect to use desktop input" : screen ? "Full screen \xB7 input may move pointer and focus" : props.target?.kind === "app" ? props.session?.capabilities?.backgroundActions ? "App \xB7 controls work in the background" : "App controls" : props.session?.capabilities?.backgroundInput ? "Window \xB7 clicks and typing stay in the background" : props.session?.capabilities?.backgroundActions ? "Window \xB7 app controls work in the background" : props.target?.kind ?? "Desktop" }),
2081
2700
  screen ? /* @__PURE__ */ jsx(MousePointer2Icon, { className: "size-3", "aria-hidden": true }) : null,
2082
2701
  props.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }) : null,
2083
- /* @__PURE__ */ jsxs(
2702
+ props.controlCount > 0 || props.showControls ? /* @__PURE__ */ jsxs(
2084
2703
  "button",
2085
2704
  {
2086
2705
  type: "button",
@@ -2095,7 +2714,7 @@ function ComputerStatusBar(props) {
2095
2714
  props.controlCount
2096
2715
  ]
2097
2716
  }
2098
- )
2717
+ ) : null
2099
2718
  ] });
2100
2719
  }
2101
2720
  function ComputerNotice(props) {
@@ -2103,7 +2722,7 @@ function ComputerNotice(props) {
2103
2722
  "div",
2104
2723
  {
2105
2724
  className: cn(
2106
- "grid h-full place-items-center bg-og-bg text-og-control text-og-muted",
2725
+ "grid h-full place-items-center bg-og-bg text-og-control text-og-fg-muted",
2107
2726
  props.className
2108
2727
  ),
2109
2728
  children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
@@ -2113,11 +2732,30 @@ function ComputerNotice(props) {
2113
2732
  }
2114
2733
  );
2115
2734
  }
2116
- function semanticNodes(observation) {
2735
+ var structuralRoles = /* @__PURE__ */ new Set([
2736
+ "frame",
2737
+ "panel",
2738
+ "section",
2739
+ "document",
2740
+ "document web",
2741
+ "static",
2742
+ "static text",
2743
+ "heading",
2744
+ "paragraph",
2745
+ "notification",
2746
+ "separator",
2747
+ "tool bar",
2748
+ "page tab list"
2749
+ ]);
2750
+ function semanticControls(observation) {
2117
2751
  if (observation?.semantic?.kind !== "snapshot") return [];
2118
2752
  const result = [];
2119
2753
  const visit = (node) => {
2120
- result.push(node);
2754
+ const role = node.role.trim().toLowerCase();
2755
+ const label = node.name?.trim() || node.identifier?.trim();
2756
+ if (node.actions.includes("set_value") || label && label.toLowerCase() !== role && !structuralRoles.has(role) && semanticAction(node) !== null) {
2757
+ result.push(node);
2758
+ }
2121
2759
  node.children?.forEach(visit);
2122
2760
  };
2123
2761
  observation.semantic.roots.forEach(visit);
@@ -2138,11 +2776,24 @@ function semanticValue(node) {
2138
2776
  return typeof node.value === "string" ? node.value : null;
2139
2777
  }
2140
2778
  function paintCanvas(canvas, image, width, height) {
2779
+ const dimensionsChanged = canvas.width !== width || canvas.height !== height;
2141
2780
  canvas.width = width;
2142
2781
  canvas.height = height;
2143
2782
  const context = canvas.getContext("2d", { alpha: false });
2144
2783
  if (!context) throw new Error("Desktop canvas is unavailable.");
2145
2784
  context.drawImage(image, 0, 0, width, height);
2785
+ if (dimensionsChanged) fitComputerCanvas(canvas);
2786
+ }
2787
+ function fitComputerCanvas(canvas) {
2788
+ const viewport = canvas.parentElement;
2789
+ if (!viewport || canvas.width <= 0 || canvas.height <= 0) return;
2790
+ const scale = Math.min(
2791
+ viewport.clientWidth / canvas.width,
2792
+ viewport.clientHeight / canvas.height
2793
+ );
2794
+ if (!Number.isFinite(scale) || scale <= 0) return;
2795
+ canvas.style.width = `${canvas.width * scale}px`;
2796
+ canvas.style.height = `${canvas.height * scale}px`;
2146
2797
  }
2147
2798
  function loadImage(url) {
2148
2799
  return new Promise((resolve, reject) => {
@@ -2189,7 +2840,10 @@ function computerKey(event) {
2189
2840
  return parts.join("+");
2190
2841
  }
2191
2842
  function sameFrameFence(left, right) {
2192
- return left.frameId === right.frameId && left.computerSessionId === right.computerSessionId && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration;
2843
+ return left.frameId === right.frameId && sameComputerTarget(left, right);
2844
+ }
2845
+ function sameComputerTarget(left, right) {
2846
+ return left.computerSessionId === right.computerSessionId && left.controllerGeneration === right.controllerGeneration && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration;
2193
2847
  }
2194
2848
  function isLiveComputer2(session) {
2195
2849
  return !["ending", "ended", "failed", "lost"].includes(session.lifecycle);
@@ -2202,6 +2856,8 @@ function computerFailureMessage(session) {
2202
2856
  return "The desktop connection was lost. Try opening it again.";
2203
2857
  case "controller_transition_expired":
2204
2858
  return session.placement.kind === "attached_device" ? "Chrome reconnected\u2014open a fresh browser/desktop. Use Browser \u2192 New browser \u2192 Connected Chrome." : "The desktop connection expired. Open a new desktop.";
2859
+ case "idle_released":
2860
+ return "The desktop stopped after it went unused. Open a new desktop.";
2205
2861
  case null:
2206
2862
  return null;
2207
2863
  default:
@@ -2291,10 +2947,10 @@ function computerConnectionLabel(state) {
2291
2947
  }
2292
2948
 
2293
2949
  export {
2294
- useComputerFrameStream,
2295
- useComputerSession,
2296
2950
  useComputerSessions,
2951
+ useComputerSession,
2952
+ useComputerFrameStream,
2297
2953
  ComputerViewer,
2298
2954
  computerKey
2299
2955
  };
2300
- //# sourceMappingURL=chunk-ELIYWBZR.js.map
2956
+ //# sourceMappingURL=chunk-GUUXOISA.js.map