@opengeni/react 3.3.2 → 3.4.1-canary.38960406141001

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,41 +1,48 @@
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
- STREAM_CLOSE_REASON_NORMAL,
10
- STREAM_KIND_BROWSER,
11
- STREAM_ROLE_CLIENT,
12
- decodeStreamFrame,
13
- decodeStreamOpenAck,
14
- encodeStreamClose,
15
- encodeStreamOpen,
16
15
  interactionHostPlatform
17
- } from "./chunk-5YK2ZDIG.js";
16
+ } from "./chunk-ZUWF4NF7.js";
18
17
  import {
19
18
  usePageLiveActivity
20
- } from "./chunk-KZ73RL76.js";
19
+ } from "./chunk-QHUE7E4N.js";
21
20
  import {
22
21
  useEmbeddedBrowserInteraction
23
- } from "./chunk-NEXLLTP4.js";
24
- import {
25
- copyTextToClipboard
26
- } from "./chunk-JVYQRRC4.js";
22
+ } from "./chunk-G76LSA76.js";
27
23
  import {
28
24
  formatBytes
29
- } from "./chunk-VTOREEXF.js";
25
+ } from "./chunk-BLL56F7R.js";
26
+ import {
27
+ STREAM_CLOSE_REASON_NORMAL,
28
+ STREAM_KIND_BROWSER,
29
+ STREAM_ROLE_CLIENT,
30
+ decodeStreamFrame,
31
+ decodeStreamOpenAck,
32
+ encodeStreamClose,
33
+ encodeStreamOpen
34
+ } from "./chunk-TNEZNMSN.js";
30
35
  import {
31
36
  cn
32
- } from "./chunk-22KP6LR5.js";
37
+ } from "./chunk-TXS65BB2.js";
33
38
 
34
39
  // src/components/browser-viewer.tsx
35
40
  import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
36
41
  import {
37
42
  BugIcon,
38
43
  ArchiveIcon,
44
+ ArrowLeftIcon,
45
+ ArrowRightIcon,
39
46
  CheckIcon,
40
47
  ChevronDownIcon,
41
48
  CircleAlertIcon,
@@ -54,11 +61,12 @@ import {
54
61
  } from "lucide-react";
55
62
  import {
56
63
  useCallback as useCallback8,
57
- useEffect as useEffect8,
64
+ useEffect as useEffect9,
58
65
  useId,
66
+ useLayoutEffect as useLayoutEffect2,
59
67
  useMemo as useMemo2,
60
- useRef as useRef8,
61
- useState as useState8
68
+ useRef as useRef9,
69
+ useState as useState9
62
70
  } from "react";
63
71
 
64
72
  // src/hooks/use-browser-frame-stream.ts
@@ -74,7 +82,8 @@ var RELAY_TAG_CLOSE = 4;
74
82
  var MAX_AUTOMATIC_RECONNECTS_WITHOUT_A_FRAME = 2;
75
83
  function useBrowserFrameStream(options) {
76
84
  const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
77
- const enabled = options.enabled ?? true;
85
+ const pageLive = usePageLiveActivity();
86
+ const enabled = (options.enabled ?? true) && pageLive;
78
87
  const [nonce, setNonce] = useState(0);
79
88
  const [result, setResult] = useState({
80
89
  state: "idle",
@@ -296,6 +305,7 @@ function useBrowserFrameStream(options) {
296
305
  activeStream.kind === "relay" ? activeStream.url : browserFrameSocketUrl(activeAttachment, streamRef.current),
297
306
  activeStream.kind === "direct_websocket" ? [...activeStream.protocols] : []
298
307
  );
308
+ latestRef.current = { key: "", sequence: -1 };
299
309
  socket = openedSocket;
300
310
  socketHandlers = {
301
311
  open: () => onOpen(openedSocket),
@@ -346,7 +356,9 @@ function useBrowserFrameStream(options) {
346
356
  browserSessionId: attachment.browserSessionId,
347
357
  controllerGeneration: attachment.controllerGeneration,
348
358
  targetId: attachment.targetId,
349
- expiresAt: attachment.expiresAt
359
+ expiresAt: attachment.expiresAt,
360
+ ...attachment.fencedInputBatches === true ? { fencedInputBatches: true } : {},
361
+ ...attachment.focusedInputObservations === true ? { focusedInputObservations: true } : {}
350
362
  },
351
363
  error: null
352
364
  }));
@@ -855,14 +867,35 @@ function useBrowserSession(options) {
855
867
  const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
856
868
  const browserSessionId = options.browserSessionId;
857
869
  const enabled = (options.enabled ?? true) && browserSessionId !== null;
870
+ const sourceRef = useRef5({ client, workspaceId, browserSessionId, enabled });
871
+ if (sourceRef.current.client !== client || sourceRef.current.workspaceId !== workspaceId || sourceRef.current.browserSessionId !== browserSessionId || sourceRef.current.enabled !== enabled) {
872
+ sourceRef.current = { client, workspaceId, browserSessionId, enabled };
873
+ }
874
+ const source = sourceRef.current;
875
+ const semanticObservationEnabled = options.semanticObservationEnabled ?? true;
876
+ const semanticObservationEnabledRef = useRef5(semanticObservationEnabled);
877
+ semanticObservationEnabledRef.current = semanticObservationEnabled;
878
+ const previousSemanticObservationRef = useRef5({
879
+ browserSessionId,
880
+ enabled: semanticObservationEnabled
881
+ });
858
882
  const pageLive = usePageLiveActivity();
859
883
  const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
860
- const [state, setState] = useState5(() => emptyState4(browserSessionId, enabled));
861
- const visible = state.browserSessionId === browserSessionId ? state : emptyState4(browserSessionId, enabled);
884
+ const [state, setState] = useState5(() => ({ ...emptyState4(browserSessionId, enabled), source }));
885
+ const visible = state.source === source && state.browserSessionId === browserSessionId ? state : { ...emptyState4(browserSessionId, enabled), source };
862
886
  const refreshBlocked = isNonRetryableInteractionError(visible.error);
887
+ const inputFailureRef = useRef5(visible.inputFailure);
888
+ inputFailureRef.current = visible.inputFailure;
863
889
  const selectedTargetIdRef = useRef5(visible.selectedTargetId);
864
890
  selectedTargetIdRef.current = visible.selectedTargetId;
891
+ const selectionRevisionRef = useRef5(0);
892
+ const actionResultOrderRef = useRef5({ next: 0, settled: 0 });
893
+ const selectedTargetRef = useRef5(
894
+ visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null
895
+ );
896
+ selectedTargetRef.current = visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null;
865
897
  const observationRef = useRef5({ browserSessionId, observation: visible.observation });
898
+ const lastObservationAtRef = useRef5(0);
866
899
  if (observationRef.current.browserSessionId !== browserSessionId) {
867
900
  observationRef.current = {
868
901
  browserSessionId,
@@ -877,106 +910,189 @@ function useBrowserSession(options) {
877
910
  });
878
911
  const mutationRef = useRef5({
879
912
  browserSessionId,
880
- count: 0
913
+ source,
914
+ count: 0,
915
+ needsInventoryReconcile: false
881
916
  });
882
917
  const mountedRef = useRef5(true);
918
+ const isCurrentSource = useCallback5(
919
+ () => mountedRef.current && sourceRef.current === source,
920
+ [source]
921
+ );
883
922
  const invalidateRefresh = useCallback5(() => {
884
923
  const id = requestRef.current.id + 1;
885
924
  requestRef.current.controller?.abort();
886
925
  requestRef.current = { id, controller: null };
887
926
  }, []);
888
- const refresh = useCallback5(async () => {
889
- if (!enabled || !browserSessionId) return;
890
- const id = requestRef.current.id + 1;
891
- requestRef.current.controller?.abort();
892
- const controller = new AbortController();
893
- requestRef.current = { id, controller };
894
- try {
895
- const [session, targetResponse] = await Promise.all([
896
- client.getBrowserSession(workspaceId, browserSessionId, {
897
- signal: controller.signal
898
- }),
899
- client.listBrowserTargets(workspaceId, browserSessionId, {
900
- signal: controller.signal
901
- })
902
- ]);
903
- if (!mountedRef.current || requestRef.current.id !== id) return;
904
- const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
905
- selectedTargetIdRef.current = selected?.id ?? null;
906
- observationRef.current = { browserSessionId, observation: null };
907
- setState(
908
- (current) => current.browserSessionId === browserSessionId ? {
909
- ...current,
910
- session,
911
- targets: targetResponse.targets,
912
- selectedTargetId: selected?.id ?? null,
913
- observation: null,
914
- loading: false,
915
- error: null
916
- } : current
917
- );
918
- if (!selected) return;
927
+ const refreshInventory = useCallback5(
928
+ async (checkInput = false, observeSelectedTarget = true) => {
929
+ if (!enabled || !browserSessionId || !isCurrentSource()) return;
930
+ const checkedInputFailure = inputFailureRef.current;
931
+ const id = requestRef.current.id + 1;
932
+ requestRef.current.controller?.abort();
933
+ const controller = new AbortController();
934
+ requestRef.current = { id, controller };
919
935
  try {
920
- const observation = await client.observeBrowserTarget(
921
- workspaceId,
936
+ const [session, targetResponse] = await Promise.all([
937
+ client.getBrowserSession(workspaceId, browserSessionId, {
938
+ signal: controller.signal
939
+ }),
940
+ client.listBrowserTargets(workspaceId, browserSessionId, {
941
+ signal: controller.signal
942
+ })
943
+ ]);
944
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
945
+ const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
946
+ selectedTargetIdRef.current = selected?.id ?? null;
947
+ selectedTargetRef.current = selected;
948
+ const previousObservation = observationRef.current.browserSessionId === browserSessionId ? observationRef.current.observation : null;
949
+ const retainedObservation = selected && previousObservation && sameObservationTarget(previousObservation, selected) ? previousObservation : null;
950
+ observationRef.current = {
922
951
  browserSessionId,
923
- selected.id,
924
- { signal: controller.signal }
925
- );
926
- if (!mountedRef.current || requestRef.current.id !== id) return;
927
- observationRef.current = { browserSessionId, observation };
952
+ observation: retainedObservation
953
+ };
928
954
  setState(
929
- (current) => current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? { ...current, observation } : current
955
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
956
+ ...current,
957
+ session,
958
+ targets: targetResponse.targets,
959
+ selectedTargetId: selected?.id ?? null,
960
+ observation: retainedObservation,
961
+ loading: false,
962
+ error: null,
963
+ // Media and background polling cannot confirm an input outcome.
964
+ // Only an explicit fresh check clears the notice it began with.
965
+ inputFailure: checkInput && session.id === browserSessionId && targetResponse.browserSessionId === browserSessionId && current.inputFailure === checkedInputFailure ? null : current.inputFailure
966
+ } : current
930
967
  );
968
+ if (!selected || !observeSelectedTarget || !semanticObservationEnabledRef.current) return;
969
+ try {
970
+ const observation = await client.observeBrowserTarget(
971
+ workspaceId,
972
+ browserSessionId,
973
+ selected.id,
974
+ { signal: controller.signal }
975
+ );
976
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
977
+ const currentSelected = selectedTargetRef.current;
978
+ if (observationRef.current.browserSessionId !== browserSessionId || currentSelected?.id !== selected.id || observation.browserSessionId !== browserSessionId || observation.target.id !== selected.id || observation.target.controllerGeneration !== currentSelected.controllerGeneration || observation.target.targetGeneration !== currentSelected.targetGeneration)
979
+ return;
980
+ selectedTargetRef.current = observation.target;
981
+ observationRef.current = { browserSessionId, observation };
982
+ lastObservationAtRef.current = Date.now();
983
+ setState(
984
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? {
985
+ ...current,
986
+ observation,
987
+ targets: replaceTarget(current.targets, observation.target)
988
+ } : current
989
+ );
990
+ } catch (cause) {
991
+ if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id)
992
+ return;
993
+ void cause;
994
+ }
931
995
  } catch (cause) {
932
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
933
- return;
934
- void cause;
996
+ if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
997
+ setState(
998
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
999
+ ...current,
1000
+ loading: false,
1001
+ error: cause instanceof Error ? cause : new Error(String(cause))
1002
+ } : current
1003
+ );
1004
+ } finally {
1005
+ if (requestRef.current.id === id) {
1006
+ requestRef.current = { id, controller: null };
1007
+ }
935
1008
  }
936
- } catch (cause) {
937
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
1009
+ },
1010
+ [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]
1011
+ );
1012
+ const refresh = useCallback5(async () => await refreshInventory(true), [refreshInventory]);
1013
+ const refreshSemantic = useCallback5(async () => {
1014
+ if (!enabled || !browserSessionId || !isCurrentSource()) return;
1015
+ const target = selectedTargetRef.current;
1016
+ if (!target) return;
1017
+ const id = requestRef.current.id + 1;
1018
+ requestRef.current.controller?.abort();
1019
+ const controller = new AbortController();
1020
+ requestRef.current = { id, controller };
1021
+ try {
1022
+ const observation = await client.observeBrowserTarget(
1023
+ workspaceId,
1024
+ browserSessionId,
1025
+ target.id,
1026
+ { signal: controller.signal }
1027
+ );
1028
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
1029
+ if (!sameObservationTarget(observation, selectedTargetRef.current)) return;
1030
+ observationRef.current = { browserSessionId, observation };
1031
+ lastObservationAtRef.current = Date.now();
938
1032
  setState(
939
- (current) => current.browserSessionId === browserSessionId ? {
940
- ...current,
941
- loading: false,
942
- error: cause instanceof Error ? cause : new Error(String(cause))
943
- } : current
1033
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
944
1034
  );
1035
+ } catch {
945
1036
  } finally {
946
- if (requestRef.current.id === id) {
947
- requestRef.current = { id, controller: null };
948
- }
1037
+ if (requestRef.current.id === id) requestRef.current = { id, controller: null };
949
1038
  }
950
- }, [browserSessionId, client, enabled, workspaceId]);
1039
+ }, [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]);
951
1040
  useEffect5(() => {
952
1041
  mountedRef.current = true;
953
1042
  if (!enabled) {
954
1043
  invalidateRefresh();
955
- setState(emptyState4(browserSessionId, false));
1044
+ setState({ ...emptyState4(browserSessionId, false), source });
956
1045
  return;
957
1046
  }
958
1047
  setState(
959
- (current) => current.browserSessionId === browserSessionId ? { ...current, loading: true } : emptyState4(browserSessionId, true)
1048
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, loading: true } : { ...emptyState4(browserSessionId, true), source }
960
1049
  );
961
- void refresh();
1050
+ void refreshInventory();
962
1051
  return () => {
963
1052
  mountedRef.current = false;
964
1053
  invalidateRefresh();
965
1054
  };
966
- }, [browserSessionId, enabled, invalidateRefresh, refresh]);
1055
+ }, [browserSessionId, enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]);
1056
+ useEffect5(() => {
1057
+ const previous = previousSemanticObservationRef.current;
1058
+ previousSemanticObservationRef.current = {
1059
+ browserSessionId,
1060
+ enabled: semanticObservationEnabled
1061
+ };
1062
+ if (enabled && semanticObservationEnabled && previous.browserSessionId === browserSessionId && !previous.enabled) {
1063
+ const currentObservation = observationRef.current.observation;
1064
+ if (currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) && Date.now() - lastObservationAtRef.current < pollIntervalMs) {
1065
+ return;
1066
+ }
1067
+ observationRef.current = { browserSessionId, observation: null };
1068
+ setState(
1069
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
1070
+ );
1071
+ void refreshSemantic();
1072
+ }
1073
+ }, [
1074
+ browserSessionId,
1075
+ enabled,
1076
+ isCurrentSource,
1077
+ pollIntervalMs,
1078
+ refreshSemantic,
1079
+ semanticObservationEnabled,
1080
+ source
1081
+ ]);
967
1082
  useEffect5(() => {
968
1083
  if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
969
1084
  const timer = setInterval(() => {
970
- if (!requestRef.current.controller) void refresh();
1085
+ if (!requestRef.current.controller) void refreshInventory();
971
1086
  }, pollIntervalMs);
972
1087
  return () => clearInterval(timer);
973
- }, [enabled, pageLive, pollIntervalMs, refresh, refreshBlocked, visible.mutating]);
1088
+ }, [enabled, pageLive, pollIntervalMs, refreshInventory, refreshBlocked, visible.mutating]);
974
1089
  useEffect5(() => {
975
1090
  if (!enabled || !browserSessionId || !pageLive || refreshBlocked) return;
976
1091
  let disposed = false;
977
1092
  const heartbeat = () => {
1093
+ if (!isCurrentSource()) return;
978
1094
  void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
979
- if (!disposed) void refresh();
1095
+ if (!disposed) void refreshInventory();
980
1096
  });
981
1097
  };
982
1098
  const timer = setInterval(heartbeat, 3e4);
@@ -984,148 +1100,216 @@ function useBrowserSession(options) {
984
1100
  disposed = true;
985
1101
  clearInterval(timer);
986
1102
  };
987
- }, [browserSessionId, client, enabled, pageLive, refresh, refreshBlocked, workspaceId]);
1103
+ }, [
1104
+ browserSessionId,
1105
+ client,
1106
+ enabled,
1107
+ isCurrentSource,
1108
+ pageLive,
1109
+ refreshInventory,
1110
+ refreshBlocked,
1111
+ workspaceId
1112
+ ]);
988
1113
  const runMutation = useCallback5(
989
- async (scopeBrowserSessionId, operation) => {
1114
+ async (scopeBrowserSessionId, operation, selectionRead = false, changesInventory = false) => {
1115
+ if (!enabled || !isCurrentSource()) {
1116
+ throw new Error("The browser source is no longer selected.");
1117
+ }
1118
+ const selectionRevision = ++selectionRevisionRef.current;
990
1119
  invalidateRefresh();
991
- if (mutationRef.current.browserSessionId !== scopeBrowserSessionId) {
1120
+ const readRequestId = requestRef.current.id;
1121
+ if (mutationRef.current.browserSessionId !== scopeBrowserSessionId || mutationRef.current.source !== source) {
992
1122
  mutationRef.current = {
993
1123
  browserSessionId: scopeBrowserSessionId,
994
- count: 0
1124
+ source,
1125
+ count: 0,
1126
+ needsInventoryReconcile: false
995
1127
  };
996
1128
  }
997
1129
  mutationRef.current.count += 1;
998
1130
  setState(
999
- (current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating: true, error: null } : current
1131
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating: true, error: null } : current
1000
1132
  );
1001
1133
  try {
1002
- return await operation();
1134
+ return await operation(selectionRevision, readRequestId);
1003
1135
  } catch (cause) {
1004
1136
  const error = cause instanceof Error ? cause : new Error(String(cause));
1005
- setState(
1006
- (current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current
1007
- );
1137
+ if (!selectionRead || requestRef.current.id === readRequestId) {
1138
+ setState(
1139
+ (current) => isCurrentSource() && selectionRevisionRef.current === selectionRevision && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current
1140
+ );
1141
+ }
1008
1142
  throw error;
1009
1143
  } finally {
1010
- if (mutationRef.current.browserSessionId === scopeBrowserSessionId) {
1144
+ if (mutationRef.current.browserSessionId === scopeBrowserSessionId && mutationRef.current.source === source) {
1145
+ if (changesInventory && (selectionRevisionRef.current !== selectionRevision || requestRef.current.id !== readRequestId)) {
1146
+ mutationRef.current.needsInventoryReconcile = true;
1147
+ }
1011
1148
  mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
1012
1149
  const mutating = mutationRef.current.count > 0;
1013
1150
  setState(
1014
- (current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
1151
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
1015
1152
  );
1153
+ if (!mutating && mutationRef.current.needsInventoryReconcile && isCurrentSource()) {
1154
+ mutationRef.current.needsInventoryReconcile = false;
1155
+ void refreshInventory(false, false);
1156
+ }
1016
1157
  }
1017
1158
  }
1018
1159
  },
1019
- [invalidateRefresh]
1160
+ [enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]
1020
1161
  );
1021
1162
  const selectTarget = useCallback5(
1022
1163
  async (targetId) => {
1023
1164
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1024
- return await runMutation(browserSessionId, async () => {
1025
- let targets = visible.targets;
1026
- let target;
1027
- let observation;
1028
- try {
1029
- observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
1030
- target = observation.target;
1031
- } catch (cause) {
1032
- if (!isMissingBrowserTarget(cause)) throw cause;
1033
- const response = await client.listBrowserTargets(workspaceId, browserSessionId);
1034
- targets = response.targets;
1035
- const fallback = chooseTarget(targets, null);
1036
- if (!fallback) {
1037
- selectedTargetIdRef.current = null;
1038
- observationRef.current = { browserSessionId, observation: null };
1039
- setState(
1040
- (current) => current.browserSessionId === browserSessionId ? {
1041
- ...current,
1042
- targets,
1043
- selectedTargetId: null,
1044
- observation: null
1045
- } : current
1165
+ return await runMutation(
1166
+ browserSessionId,
1167
+ async (selectionRevision, readRequestId) => {
1168
+ const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1169
+ const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1170
+ let targets = visible.targets;
1171
+ let target;
1172
+ let observation;
1173
+ try {
1174
+ observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
1175
+ target = observation.target;
1176
+ } catch (cause) {
1177
+ if (!isCurrentSelectionRead() || !isMissingBrowserTarget(cause)) throw cause;
1178
+ const response = await client.listBrowserTargets(workspaceId, browserSessionId);
1179
+ if (!isCurrentSelectionRead()) throw cause;
1180
+ targets = response.targets;
1181
+ const fallback = chooseTarget(targets, null);
1182
+ if (!fallback) {
1183
+ selectedTargetIdRef.current = null;
1184
+ selectedTargetRef.current = null;
1185
+ observationRef.current = { browserSessionId, observation: null };
1186
+ setState(
1187
+ (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1188
+ ...current,
1189
+ targets,
1190
+ selectedTargetId: null,
1191
+ observation: null
1192
+ } : current
1193
+ );
1194
+ throw cause;
1195
+ }
1196
+ observation = await client.selectBrowserTarget(
1197
+ workspaceId,
1198
+ browserSessionId,
1199
+ fallback.id
1046
1200
  );
1047
- throw cause;
1201
+ target = observation.target;
1048
1202
  }
1049
- observation = await client.selectBrowserTarget(
1050
- workspaceId,
1051
- browserSessionId,
1052
- fallback.id
1203
+ if (!isCurrentSelectionRead()) return target;
1204
+ selectedTargetIdRef.current = target.id;
1205
+ selectedTargetRef.current = target;
1206
+ observationRef.current = { browserSessionId, observation };
1207
+ lastObservationAtRef.current = Date.now();
1208
+ setState(
1209
+ (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1210
+ ...current,
1211
+ targets: replaceTarget(targets, target).map((candidate) => ({
1212
+ ...candidate,
1213
+ selected: candidate.id === target.id
1214
+ })),
1215
+ selectedTargetId: target.id,
1216
+ observation
1217
+ } : current
1053
1218
  );
1054
- target = observation.target;
1055
- }
1056
- selectedTargetIdRef.current = target.id;
1057
- observationRef.current = { browserSessionId, observation };
1058
- setState(
1059
- (current) => current.browserSessionId === browserSessionId ? {
1060
- ...current,
1061
- targets: targets.map((candidate) => ({
1062
- ...candidate,
1063
- selected: candidate.id === target.id
1064
- })),
1065
- selectedTargetId: target.id,
1066
- observation
1067
- } : current
1068
- );
1069
- return target;
1070
- });
1219
+ return target;
1220
+ },
1221
+ true
1222
+ );
1071
1223
  },
1072
- [browserSessionId, client, runMutation, visible.targets, workspaceId]
1224
+ [browserSessionId, client, isCurrentSource, runMutation, source, visible.targets, workspaceId]
1073
1225
  );
1074
1226
  const openTarget = useCallback5(
1075
1227
  async (url) => {
1076
1228
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1077
- return await runMutation(browserSessionId, async () => {
1078
- const observation = await client.openBrowserTarget(
1079
- workspaceId,
1080
- browserSessionId,
1081
- url === void 0 ? {} : { url }
1082
- );
1083
- const target = observation.target;
1084
- selectedTargetIdRef.current = target.id;
1085
- observationRef.current = { browserSessionId, observation };
1086
- setState(
1087
- (current) => current.browserSessionId === browserSessionId ? {
1088
- ...current,
1089
- targets: [
1090
- ...current.targets.map((candidate) => ({
1091
- ...candidate,
1092
- selected: false
1093
- })),
1094
- target
1095
- ],
1096
- selectedTargetId: target.id,
1097
- observation
1098
- } : current
1099
- );
1100
- return target;
1101
- });
1229
+ return await runMutation(
1230
+ browserSessionId,
1231
+ async (selectionRevision, readRequestId) => {
1232
+ const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1233
+ const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1234
+ const observation = await client.openBrowserTarget(
1235
+ workspaceId,
1236
+ browserSessionId,
1237
+ url === void 0 ? {} : { url }
1238
+ );
1239
+ const target = observation.target;
1240
+ if (!isCurrentSource()) return target;
1241
+ if (!isCurrentSelectionRead()) return target;
1242
+ selectedTargetIdRef.current = target.id;
1243
+ selectedTargetRef.current = target;
1244
+ observationRef.current = { browserSessionId, observation };
1245
+ lastObservationAtRef.current = Date.now();
1246
+ setState(
1247
+ (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1248
+ ...current,
1249
+ targets: [
1250
+ ...current.targets.map((candidate) => ({
1251
+ ...candidate,
1252
+ selected: false
1253
+ })),
1254
+ target
1255
+ ],
1256
+ selectedTargetId: target.id,
1257
+ observation
1258
+ } : current
1259
+ );
1260
+ return target;
1261
+ },
1262
+ false,
1263
+ true
1264
+ );
1102
1265
  },
1103
- [browserSessionId, client, runMutation, workspaceId]
1266
+ [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1104
1267
  );
1105
1268
  const closeTarget = useCallback5(
1106
1269
  async (targetId) => {
1107
1270
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1108
- await runMutation(browserSessionId, async () => {
1109
- const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
1110
- const selected = chooseTarget(response.targets, null);
1111
- const observation = selected ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id) : null;
1112
- selectedTargetIdRef.current = selected?.id ?? null;
1113
- observationRef.current = { browserSessionId, observation };
1114
- setState(
1115
- (current) => current.browserSessionId === browserSessionId ? {
1116
- ...current,
1117
- targets: response.targets,
1118
- selectedTargetId: selected?.id ?? null,
1119
- observation
1120
- } : current
1121
- );
1122
- });
1271
+ await runMutation(
1272
+ browserSessionId,
1273
+ async (selectionRevision, readRequestId) => {
1274
+ const isCurrentSelection = () => isCurrentSource() && selectionRevisionRef.current === selectionRevision;
1275
+ const isCurrentSelectionRead = () => isCurrentSelection() && requestRef.current.id === readRequestId;
1276
+ const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
1277
+ if (!isCurrentSource()) return;
1278
+ if (!isCurrentSelectionRead()) return;
1279
+ const selected = chooseTarget(response.targets, selectedTargetIdRef.current);
1280
+ const observed = selected ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id) : null;
1281
+ if (!isCurrentSource()) return;
1282
+ if (!isCurrentSelectionRead()) return;
1283
+ const observation = observed && selected && observed.browserSessionId === browserSessionId && observed.target.id === selected.id && observed.target.controllerGeneration === selected.controllerGeneration && observed.target.targetGeneration === selected.targetGeneration ? observed : null;
1284
+ const currentTarget = observation?.target ?? selected;
1285
+ selectedTargetIdRef.current = currentTarget?.id ?? null;
1286
+ selectedTargetRef.current = currentTarget;
1287
+ observationRef.current = { browserSessionId, observation };
1288
+ if (observation) lastObservationAtRef.current = Date.now();
1289
+ setState(
1290
+ (current) => isCurrentSelection() && current.source === source && current.browserSessionId === browserSessionId ? {
1291
+ ...current,
1292
+ targets: currentTarget ? replaceTarget(response.targets, currentTarget) : response.targets,
1293
+ selectedTargetId: currentTarget?.id ?? null,
1294
+ observation
1295
+ } : current
1296
+ );
1297
+ },
1298
+ false,
1299
+ true
1300
+ );
1123
1301
  },
1124
- [browserSessionId, client, runMutation, workspaceId]
1302
+ [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1125
1303
  );
1126
1304
  const dispatchAction = useCallback5(
1127
- async (action, operationId, frame) => {
1305
+ async (action, operationId, frame, observationMode = "none") => {
1128
1306
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1307
+ if (!enabled || !isCurrentSource()) {
1308
+ throw new Error("The browser source is no longer selected.");
1309
+ }
1310
+ if (!selectedTargetRef.current) {
1311
+ throw new Error("The browser page is not ready for input.");
1312
+ }
1129
1313
  if (frame && frame.browserSessionId !== browserSessionId) {
1130
1314
  throw new Error("The displayed browser frame belongs to another BrowserSession.");
1131
1315
  }
@@ -1135,7 +1319,7 @@ function useBrowserSession(options) {
1135
1319
  expectedTargetGeneration: frame.targetGeneration,
1136
1320
  expectedDocumentGeneration: frame.documentGeneration,
1137
1321
  expectedFrameId: frame.frameId
1138
- } : currentObservation ? {
1322
+ } : currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) ? {
1139
1323
  targetId: currentObservation.target.id,
1140
1324
  expectedTargetGeneration: currentObservation.target.targetGeneration,
1141
1325
  expectedDocumentGeneration: currentObservation.target.documentGeneration,
@@ -1144,47 +1328,113 @@ function useBrowserSession(options) {
1144
1328
  if (!fence) {
1145
1329
  throw new Error("The browser page is not ready for input.");
1146
1330
  }
1331
+ const expectedControllerGeneration = frame?.controllerGeneration ?? currentObservation.target.controllerGeneration;
1332
+ const selectionRevision = selectionRevisionRef.current;
1333
+ const resultOrder = ++actionResultOrderRef.current.next;
1334
+ let resultDocumentGeneration = fence.expectedDocumentGeneration;
1335
+ const isCurrentResultView = () => {
1336
+ const selected = selectedTargetRef.current;
1337
+ return isCurrentSource() && selectionRevisionRef.current === selectionRevision && selected?.browserSessionId === browserSessionId && selected.id === fence.targetId && selected.controllerGeneration === expectedControllerGeneration && selected.targetGeneration === fence.expectedTargetGeneration && (selected.documentGeneration === fence.expectedDocumentGeneration || selected.documentGeneration === resultDocumentGeneration);
1338
+ };
1339
+ const canProjectResult = () => isCurrentResultView() && resultOrder >= actionResultOrderRef.current.settled;
1340
+ const settleResult = () => {
1341
+ if (!canProjectResult()) return false;
1342
+ actionResultOrderRef.current.settled = resultOrder;
1343
+ invalidateRefresh();
1344
+ return true;
1345
+ };
1147
1346
  try {
1148
1347
  const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
1149
1348
  operationId,
1150
1349
  ...fence,
1151
- observationMode: "none",
1350
+ observationMode,
1152
1351
  action
1153
1352
  });
1154
- if (receipt.observation) {
1353
+ resultDocumentGeneration = receipt.observation?.target.documentGeneration ?? fence.expectedDocumentGeneration;
1354
+ const settled = receipt.state === "completed" || receipt.state === "failed" || receipt.state === "outcome_unknown";
1355
+ if (!canProjectResult() || settled && !settleResult()) return receipt;
1356
+ const receivedObservation = receipt.observation;
1357
+ const selectedTarget = selectedTargetRef.current;
1358
+ let projectedObservation = null;
1359
+ if (settled && receivedObservation && canProjectResult() && receipt.browserSessionId === browserSessionId && receipt.controllerGeneration === expectedControllerGeneration && receipt.targetId === fence.targetId && observationRef.current.browserSessionId === browserSessionId && receivedObservation.browserSessionId === browserSessionId && selectedTarget?.browserSessionId === browserSessionId && selectedTarget.id === fence.targetId && receivedObservation.target.id === fence.targetId && selectedTarget.controllerGeneration === expectedControllerGeneration && receivedObservation.target.controllerGeneration === expectedControllerGeneration && selectedTarget.targetGeneration === fence.expectedTargetGeneration && receivedObservation.target.targetGeneration === fence.expectedTargetGeneration && (selectedTarget.documentGeneration === fence.expectedDocumentGeneration || selectedTarget.documentGeneration === receivedObservation.target.documentGeneration)) {
1360
+ projectedObservation = receivedObservation;
1155
1361
  observationRef.current = {
1156
1362
  browserSessionId,
1157
- observation: receipt.observation
1363
+ observation: receivedObservation
1158
1364
  };
1159
- setState(
1160
- (current) => current.browserSessionId === browserSessionId ? {
1161
- ...current,
1162
- observation: receipt.observation,
1163
- targets: replaceTarget(current.targets, receipt.observation.target)
1164
- } : current
1165
- );
1365
+ selectedTargetRef.current = receivedObservation.target;
1366
+ lastObservationAtRef.current = Date.now();
1166
1367
  }
1368
+ setState(
1369
+ (current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? {
1370
+ ...current,
1371
+ actionResultOrder: settled ? resultOrder : current.actionResultOrder,
1372
+ error: receipt.state === "completed" ? null : current.error,
1373
+ inputFailure: receipt.state === "completed" ? current.inputFailure : {
1374
+ operationId: receipt.operationId,
1375
+ state: receipt.state,
1376
+ error: receipt.error
1377
+ },
1378
+ ...projectedObservation ? {
1379
+ observation: projectedObservation,
1380
+ targets: replaceTarget(current.targets, projectedObservation.target)
1381
+ } : {}
1382
+ } : current
1383
+ );
1167
1384
  return receipt;
1168
1385
  } catch (cause) {
1169
1386
  const error = cause instanceof Error ? cause : new Error(String(cause));
1170
- setState(
1171
- (current) => current.browserSessionId === browserSessionId ? { ...current, error } : current
1172
- );
1387
+ if (settleResult()) {
1388
+ setState(
1389
+ (current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? { ...current, error, actionResultOrder: resultOrder } : current
1390
+ );
1391
+ }
1173
1392
  throw error;
1174
1393
  }
1175
1394
  },
1176
- [browserSessionId, client, workspaceId]
1395
+ [browserSessionId, client, enabled, invalidateRefresh, isCurrentSource, source, workspaceId]
1177
1396
  );
1178
1397
  const act = useCallback5(
1179
1398
  async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
1180
1399
  [dispatchAction]
1181
1400
  );
1182
1401
  const actFromFrame = useCallback5(
1183
- async (action, frame, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, frame),
1402
+ async (action, frame, operationId = crypto.randomUUID(), observationMode = "none") => await dispatchAction(action, operationId, frame, observationMode),
1403
+ [dispatchAction]
1404
+ );
1405
+ const observeForInput = useCallback5(async () => {
1406
+ const target = selectedTargetRef.current;
1407
+ if (!enabled || !isCurrentSource()) {
1408
+ throw new Error("The browser source is no longer selected.");
1409
+ }
1410
+ if (!browserSessionId || !target) throw new Error("No browser tab is selected.");
1411
+ const observation = await client.observeBrowserTarget(workspaceId, browserSessionId, target.id);
1412
+ if (!isCurrentSource() || observation.browserSessionId !== browserSessionId || !sameObservationTarget(observation, selectedTargetRef.current)) {
1413
+ throw new Error("The browser page changed. Open the options again.");
1414
+ }
1415
+ return observation;
1416
+ }, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
1417
+ const actFromObservation = useCallback5(
1418
+ async (action, observation) => {
1419
+ if (!sameObservationTarget(observation, selectedTargetRef.current)) {
1420
+ throw new Error("The browser page changed. Open the options again.");
1421
+ }
1422
+ return await dispatchAction(action, crypto.randomUUID(), {
1423
+ browserSessionId: observation.browserSessionId,
1424
+ controllerGeneration: observation.target.controllerGeneration,
1425
+ targetId: observation.target.id,
1426
+ targetGeneration: observation.target.targetGeneration,
1427
+ documentGeneration: observation.target.documentGeneration,
1428
+ frameId: observation.frameId
1429
+ });
1430
+ },
1184
1431
  [dispatchAction]
1185
1432
  );
1186
1433
  const diagnostics = useCallback5(
1187
1434
  async (diagnosticOptions = {}) => {
1435
+ if (!enabled || !isCurrentSource()) {
1436
+ throw new Error("The browser source is no longer selected.");
1437
+ }
1188
1438
  const targetId = selectedTargetIdRef.current;
1189
1439
  if (!browserSessionId || !targetId) {
1190
1440
  throw new Error("No browser tab is selected.");
@@ -1196,16 +1446,19 @@ function useBrowserSession(options) {
1196
1446
  diagnosticOptions
1197
1447
  );
1198
1448
  },
1199
- [browserSessionId, client, workspaceId]
1449
+ [browserSessionId, client, enabled, isCurrentSource, workspaceId]
1200
1450
  );
1201
1451
  const readClipboard = useCallback5(async () => {
1452
+ if (!enabled || !isCurrentSource()) {
1453
+ throw new Error("The browser source is no longer selected.");
1454
+ }
1202
1455
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1203
1456
  const clipboard = await client.readBrowserClipboard(workspaceId, browserSessionId);
1204
- if (clipboard.browserSessionId !== browserSessionId) {
1457
+ if (!isCurrentSource() || clipboard.browserSessionId !== browserSessionId) {
1205
1458
  throw new Error("Browser clipboard belongs to another BrowserSession.");
1206
1459
  }
1207
1460
  return clipboard;
1208
- }, [browserSessionId, client, workspaceId]);
1461
+ }, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
1209
1462
  return {
1210
1463
  session: visible.session,
1211
1464
  targets: visible.targets,
@@ -1214,7 +1467,10 @@ function useBrowserSession(options) {
1214
1467
  loading: visible.loading,
1215
1468
  mutating: visible.mutating,
1216
1469
  error: visible.error,
1470
+ inputFailure: visible.inputFailure,
1217
1471
  refresh,
1472
+ observeForInput,
1473
+ actFromObservation,
1218
1474
  selectTarget,
1219
1475
  openTarget,
1220
1476
  closeTarget,
@@ -1233,7 +1489,9 @@ function emptyState4(browserSessionId, loading) {
1233
1489
  observation: null,
1234
1490
  loading,
1235
1491
  mutating: false,
1236
- error: null
1492
+ error: null,
1493
+ inputFailure: null,
1494
+ actionResultOrder: 0
1237
1495
  };
1238
1496
  }
1239
1497
  function chooseTarget(targets, preferredId) {
@@ -1244,6 +1502,9 @@ function replaceTarget(targets, target) {
1244
1502
  next.push(target);
1245
1503
  return next.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
1246
1504
  }
1505
+ function sameObservationTarget(observation, target) {
1506
+ return target !== null && observation.browserSessionId === target.browserSessionId && observation.target.id === target.id && observation.target.controllerGeneration === target.controllerGeneration && observation.target.targetGeneration === target.targetGeneration && observation.target.documentGeneration === target.documentGeneration;
1507
+ }
1247
1508
  function isMissingBrowserTarget(error) {
1248
1509
  return error instanceof OpenGeniApiError && error.code === "target_not_found";
1249
1510
  }
@@ -1264,14 +1525,16 @@ function useBrowserSessions(options = {}) {
1264
1525
  id: 0,
1265
1526
  controller: null
1266
1527
  });
1528
+ const loadQueueRef = useRef6(null);
1267
1529
  const mountedRef = useRef6(true);
1268
1530
  const visibleState = state.workspaceId === workspaceId ? state : emptyState5(workspaceId, enabled);
1269
1531
  const cancelLoad = useCallback6(() => {
1532
+ loadQueueRef.current = null;
1270
1533
  const id = requestRef.current.id + 1;
1271
1534
  requestRef.current.controller?.abort();
1272
1535
  requestRef.current = { id, controller: null };
1273
1536
  }, []);
1274
- const load = useCallback6(
1537
+ const loadOnce = useCallback6(
1275
1538
  async (foreground) => {
1276
1539
  if (!enabled) return;
1277
1540
  const id = requestRef.current.id + 1;
@@ -1317,6 +1580,30 @@ function useBrowserSessions(options = {}) {
1317
1580
  },
1318
1581
  [client, enabled, workspaceId]
1319
1582
  );
1583
+ const load = useCallback6(
1584
+ (foreground) => {
1585
+ const current = loadQueueRef.current;
1586
+ if (current) {
1587
+ current.pending = true;
1588
+ return current.promise;
1589
+ }
1590
+ const queue = { pending: false, promise: Promise.resolve() };
1591
+ loadQueueRef.current = queue;
1592
+ queue.promise = Promise.resolve().then(async () => {
1593
+ while (loadQueueRef.current === queue) {
1594
+ queue.pending = false;
1595
+ await loadOnce(foreground);
1596
+ foreground = false;
1597
+ if (!queue.pending) break;
1598
+ }
1599
+ if (loadQueueRef.current === queue) loadQueueRef.current = null;
1600
+ }).finally(() => {
1601
+ if (loadQueueRef.current === queue) loadQueueRef.current = null;
1602
+ });
1603
+ return queue.promise;
1604
+ },
1605
+ [loadOnce]
1606
+ );
1320
1607
  const refresh = useCallback6(async () => await load(false), [load]);
1321
1608
  useEffect6(() => {
1322
1609
  mountedRef.current = true;
@@ -1644,11 +1931,270 @@ function browserKey(event, hostPlatform = interactionHostPlatform()) {
1644
1931
  return parts.join("+");
1645
1932
  }
1646
1933
 
1647
- // src/components/browser-viewer.tsx
1934
+ // src/components/browser-select-control.tsx
1935
+ import { useEffect as useEffect8, useLayoutEffect, useRef as useRef8, useState as useState8 } from "react";
1648
1936
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
1937
+ function focusedSelect(observation) {
1938
+ if (observation.semantic?.kind !== "snapshot") return null;
1939
+ const pending = [...observation.semantic.roots];
1940
+ while (pending.length) {
1941
+ const node = pending.pop();
1942
+ if (node.children) pending.push(...node.children);
1943
+ if (node.ref !== observation.focusedRef || !node.actions.includes("select") || node.native?.platform !== "dom")
1944
+ continue;
1945
+ const data = node.native.data;
1946
+ if (!data || data.kind !== "native-select" || typeof data.multiple !== "boolean" || typeof data.disabled !== "boolean" || !Array.isArray(data.options) || data.options.length > 200)
1947
+ return null;
1948
+ if (data.options.some(
1949
+ (option) => !option || typeof option.value !== "string" || typeof option.label !== "string" || typeof option.selected !== "boolean" || typeof option.disabled !== "boolean"
1950
+ ))
1951
+ return null;
1952
+ return { node, control: data };
1953
+ }
1954
+ return null;
1955
+ }
1956
+ function BrowserSelectControl(props) {
1957
+ const [snapshot, setSnapshot] = useState8(null);
1958
+ const [open, setOpen] = useState8(false);
1959
+ const [busy, setBusy] = useState8(false);
1960
+ const [error, setError] = useState8(null);
1961
+ const [values, setValues] = useState8([]);
1962
+ const [anchor, setAnchor] = useState8(null);
1963
+ const popupRef = useRef8(null);
1964
+ const [position, setPosition] = useState8(null);
1965
+ const panelRef = useRef8(null);
1966
+ const request = useRef8(0);
1967
+ const optionKeys = useRef8(/* @__PURE__ */ new WeakMap());
1968
+ const nextOptionKey = useRef8(0);
1969
+ const optionKey = (option) => {
1970
+ let key = optionKeys.current.get(option);
1971
+ if (key === void 0) {
1972
+ key = ++nextOptionKey.current;
1973
+ optionKeys.current.set(option, key);
1974
+ }
1975
+ return key;
1976
+ };
1977
+ useEffect8(
1978
+ () => () => {
1979
+ request.current += 1;
1980
+ },
1981
+ []
1982
+ );
1983
+ useEffect8(() => {
1984
+ if (props.activation === void 0) return;
1985
+ ++request.current;
1986
+ const observation = props.activation?.observation;
1987
+ const selected2 = observation ? focusedSelect(observation) : null;
1988
+ setSnapshot(selected2 && observation ? observation : null);
1989
+ setAnchor(selected2 ? props.activation.anchor : null);
1990
+ setOpen(Boolean(selected2));
1991
+ setBusy(false);
1992
+ setError(null);
1993
+ setValues(
1994
+ selected2?.control.options.filter((option) => option.selected).map((option) => option.value) ?? []
1995
+ );
1996
+ }, [props.activation]);
1997
+ useEffect8(() => {
1998
+ if (open && !busy) panelRef.current?.focus({ preventScroll: true });
1999
+ }, [open, busy]);
2000
+ useLayoutEffect(() => {
2001
+ if (!anchor || !open) {
2002
+ setPosition(null);
2003
+ return;
2004
+ }
2005
+ const place = () => {
2006
+ const popup = popupRef.current;
2007
+ const parent = popup?.parentElement;
2008
+ if (!popup || !parent) return;
2009
+ setPosition({
2010
+ x: Math.max(12, Math.min(anchor.x, parent.clientWidth - popup.offsetWidth - 12)),
2011
+ y: Math.max(12, Math.min(anchor.y, parent.clientHeight - popup.offsetHeight - 12)),
2012
+ height: Math.max(48, parent.clientHeight - 60)
2013
+ });
2014
+ };
2015
+ place();
2016
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(place);
2017
+ if (popupRef.current?.parentElement) observer?.observe(popupRef.current.parentElement);
2018
+ if (popupRef.current) observer?.observe(popupRef.current);
2019
+ return () => observer?.disconnect();
2020
+ }, [anchor, open, busy, snapshot, error]);
2021
+ const selected = snapshot ? focusedSelect(snapshot) : null;
2022
+ const ambiguousValue = (value) => (selected?.control.options.filter((option) => option.value === value || option.label === value).length ?? 0) !== 1;
2023
+ const load = async () => {
2024
+ const current = ++request.current;
2025
+ setOpen(true);
2026
+ setBusy(true);
2027
+ setSnapshot(null);
2028
+ setError(null);
2029
+ setAnchor(null);
2030
+ try {
2031
+ const observation = await props.observe();
2032
+ if (current !== request.current) return;
2033
+ setSnapshot(observation);
2034
+ setValues(
2035
+ focusedSelect(observation)?.control.options.filter((option) => option.selected).map((option) => option.value) ?? []
2036
+ );
2037
+ } catch (cause) {
2038
+ if (current === request.current)
2039
+ setError(cause instanceof Error ? cause.message : "Could not read options.");
2040
+ } finally {
2041
+ if (current === request.current) setBusy(false);
2042
+ }
2043
+ };
2044
+ const apply = async (next) => {
2045
+ if (!snapshot || !selected || busy) return;
2046
+ const current = ++request.current;
2047
+ setBusy(true);
2048
+ setError(null);
2049
+ try {
2050
+ const receipt = await props.act(
2051
+ { type: "select", locator: { kind: "ref", ref: selected.node.ref }, values: next },
2052
+ snapshot
2053
+ );
2054
+ if (receipt.state !== "completed")
2055
+ throw new Error(receipt.error?.message ?? "Selection did not complete.");
2056
+ if (current === request.current) {
2057
+ setOpen(false);
2058
+ setSnapshot(null);
2059
+ props.onDismiss?.();
2060
+ }
2061
+ } catch (cause) {
2062
+ if (current === request.current) {
2063
+ setError(
2064
+ cause instanceof Error ? cause.message : "Selection failed. Read the options again."
2065
+ );
2066
+ setSnapshot(null);
2067
+ }
2068
+ } finally {
2069
+ if (current === request.current) setBusy(false);
2070
+ }
2071
+ };
2072
+ if (props.manualFallback === false && !open) return null;
2073
+ return /* @__PURE__ */ jsxs(
2074
+ "div",
2075
+ {
2076
+ ref: popupRef,
2077
+ className: "absolute z-10 max-w-[calc(100%-1.5rem)] text-og-control",
2078
+ style: anchor && open ? {
2079
+ left: position?.x ?? anchor.x,
2080
+ top: position?.y ?? anchor.y
2081
+ } : { bottom: 12, left: 12 },
2082
+ children: [
2083
+ props.manualFallback !== false && (!anchor || !open) || error ? /* @__PURE__ */ jsx(
2084
+ "button",
2085
+ {
2086
+ type: "button",
2087
+ className: "rounded-og-sm border border-og-border bg-og-bg px-2 py-1 text-og-fg",
2088
+ disabled: busy,
2089
+ onClick: () => void load(),
2090
+ "aria-expanded": open,
2091
+ children: "Choose option"
2092
+ }
2093
+ ) : null,
2094
+ open ? /* @__PURE__ */ jsxs(
2095
+ "section",
2096
+ {
2097
+ ref: panelRef,
2098
+ tabIndex: -1,
2099
+ "aria-label": "Page selection options",
2100
+ style: position ? { maxHeight: Math.min(288, position.height) } : void 0,
2101
+ onKeyDown: (event) => {
2102
+ if (event.key !== "Escape") return;
2103
+ event.preventDefault();
2104
+ event.stopPropagation();
2105
+ ++request.current;
2106
+ setOpen(false);
2107
+ setSnapshot(null);
2108
+ setBusy(false);
2109
+ props.onDismiss?.();
2110
+ },
2111
+ className: "mt-1 max-h-72 w-72 max-w-full overflow-auto rounded-og-sm border border-og-border bg-og-bg p-3 text-og-fg shadow-lg",
2112
+ children: [
2113
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
2114
+ /* @__PURE__ */ jsx("strong", { children: selected?.node.name || "Page options" }),
2115
+ /* @__PURE__ */ jsx(
2116
+ "button",
2117
+ {
2118
+ type: "button",
2119
+ disabled: busy,
2120
+ onClick: () => {
2121
+ ++request.current;
2122
+ setOpen(false);
2123
+ setSnapshot(null);
2124
+ props.onDismiss?.();
2125
+ },
2126
+ "aria-label": "Close page options",
2127
+ children: "Close"
2128
+ }
2129
+ )
2130
+ ] }),
2131
+ busy ? /* @__PURE__ */ jsx("p", { role: "status", children: "Loading\u2026" }) : error ? /* @__PURE__ */ jsx("p", { role: "alert", children: error }) : !selected ? /* @__PURE__ */ jsx("p", { children: "Click a dropdown in the page, then choose \u201CChoose option\u201D." }) : selected.control.disabled ? /* @__PURE__ */ jsx("p", { children: "This control is disabled." }) : /* @__PURE__ */ jsxs(Fragment, { children: [
2132
+ selected.control.options.map((option) => {
2133
+ const ambiguous = ambiguousValue(option.value);
2134
+ const disabled = busy || option.disabled || ambiguous;
2135
+ return selected.control.multiple ? /* @__PURE__ */ jsxs("label", { className: "my-1 flex items-center gap-2", children: [
2136
+ /* @__PURE__ */ jsx(
2137
+ "input",
2138
+ {
2139
+ type: "checkbox",
2140
+ disabled,
2141
+ checked: values.includes(option.value),
2142
+ onChange: (event) => setValues(
2143
+ event.target.checked ? [...values, option.value] : values.filter((value) => value !== option.value)
2144
+ )
2145
+ }
2146
+ ),
2147
+ option.label || "(empty)",
2148
+ ambiguous ? " (ambiguous value)" : ""
2149
+ ] }, optionKey(option)) : /* @__PURE__ */ jsxs(
2150
+ "button",
2151
+ {
2152
+ type: "button",
2153
+ disabled,
2154
+ "aria-pressed": option.selected,
2155
+ className: "my-1 block w-full rounded-og-sm border border-og-border px-2 py-1 text-left disabled:opacity-50",
2156
+ onClick: () => void apply([option.value]),
2157
+ children: [
2158
+ option.label || "(empty)",
2159
+ option.selected ? " \u2713" : "",
2160
+ ambiguous ? " (ambiguous value)" : ""
2161
+ ]
2162
+ },
2163
+ optionKey(option)
2164
+ );
2165
+ }),
2166
+ selected.control.multiple ? /* @__PURE__ */ jsx(
2167
+ "button",
2168
+ {
2169
+ type: "button",
2170
+ disabled: busy || values.some(ambiguousValue),
2171
+ onClick: () => void apply(values),
2172
+ children: "Apply selection"
2173
+ }
2174
+ ) : null
2175
+ ] })
2176
+ ]
2177
+ }
2178
+ ) : null
2179
+ ]
2180
+ }
2181
+ );
2182
+ }
2183
+
2184
+ // src/components/browser-viewer.tsx
2185
+ import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
2186
+ var STALE_BROWSER_END_MAX_ATTEMPTS = 3;
2187
+ var STALE_BROWSER_END_RETRY_BASE_MS = 5e3;
2188
+ function staleBrowserEndRetryDelayMs(cause, failedAttempts) {
2189
+ const status = cause?.status;
2190
+ if (typeof status === "number" && status >= 400 && status < 500) return null;
2191
+ if (failedAttempts >= STALE_BROWSER_END_MAX_ATTEMPTS) return null;
2192
+ return STALE_BROWSER_END_RETRY_BASE_MS * 2 ** (failedAttempts - 1);
2193
+ }
1649
2194
  function BrowserViewer({
1650
2195
  sessionId,
1651
2196
  enabled = true,
2197
+ active = true,
1652
2198
  className,
1653
2199
  onNotify,
1654
2200
  webSocketFactory,
@@ -1682,9 +2228,10 @@ function BrowserViewer({
1682
2228
  () => attachedChromeGenerationLoss(registry.sessions),
1683
2229
  [registry.sessions]
1684
2230
  );
1685
- const endedGenerationLossRef = useRef8(/* @__PURE__ */ new Set());
2231
+ const endedGenerationLossRef = useRef9(/* @__PURE__ */ new Set());
2232
+ const endStaleBrowserAttemptsRef = useRef9(/* @__PURE__ */ new Map());
1686
2233
  const endStaleBrowser = registry.end;
1687
- useEffect8(() => {
2234
+ useEffect9(() => {
1688
2235
  if (!enabled) return;
1689
2236
  for (const session of registry.sessions) {
1690
2237
  if (session.lifecycle !== "lost" || session.failureCode !== "controller_transition_expired" || session.placement.kind !== "attached_device") {
@@ -1692,20 +2239,22 @@ function BrowserViewer({
1692
2239
  }
1693
2240
  if (endedGenerationLossRef.current.has(session.id)) continue;
1694
2241
  endedGenerationLossRef.current.add(session.id);
1695
- void endStaleBrowser(session.id).catch(() => {
1696
- endedGenerationLossRef.current.delete(session.id);
2242
+ void endStaleBrowser(session.id).catch((cause) => {
2243
+ const attempts = (endStaleBrowserAttemptsRef.current.get(session.id) ?? 0) + 1;
2244
+ endStaleBrowserAttemptsRef.current.set(session.id, attempts);
2245
+ const delay = staleBrowserEndRetryDelayMs(cause, attempts);
2246
+ if (delay === null) return;
2247
+ setTimeout(() => endedGenerationLossRef.current.delete(session.id), delay);
1697
2248
  });
1698
2249
  }
1699
2250
  }, [enabled, endStaleBrowser, registry.sessions]);
1700
- const replacementChromeDevice = useMemo2(() => {
1701
- if (!attachedGenerationLoss) return null;
1702
- return attached.devices.find((candidate) => candidate.id === attachedGenerationLoss.deviceId) ?? null;
1703
- }, [attached.devices, attachedGenerationLoss]);
1704
2251
  const relevant = useMemo2(
1705
2252
  () => registry.relevantSessions.filter((session) => isLiveBrowser2(session)),
1706
2253
  [registry.relevantSessions]
1707
2254
  );
1708
- const [selection, setSelection] = useState8(
2255
+ const latestRelevantBrowser = registry.relevantSessions[0];
2256
+ const unavailableBrowser = latestRelevantBrowser?.lifecycle === "lost" || latestRelevantBrowser?.lifecycle === "failed" ? latestRelevantBrowser : void 0;
2257
+ const [selection, setSelection] = useState9(
1709
2258
  () => initialBrowserSessionId ? { sessionId: initialBrowserSessionId, pinned: true } : null
1710
2259
  );
1711
2260
  const selectBrowserSession = useCallback8(
@@ -1720,16 +2269,17 @@ function BrowserViewer({
1720
2269
  enabled,
1721
2270
  resourceKind: "browser_session"
1722
2271
  });
1723
- const [creating, setCreating] = useState8(false);
1724
- const [savingProfile, setSavingProfile] = useState8(false);
1725
- const [baseRevisionOrdinal, setBaseRevisionOrdinal] = useState8(null);
1726
- const [profileHistory, setProfileHistory] = useState8(null);
1727
- const [resumeAttempt, setResumeAttempt] = useState8(null);
1728
- const resumeAttemptsRef = useRef8(/* @__PURE__ */ new Map());
1729
- const previousSessionIdRef = useRef8(sessionId);
1730
- const seenInterventionIdsRef = useRef8(/* @__PURE__ */ new Set());
1731
- const diagnosticRequestRef = useRef8(0);
1732
- const [diagnosticsView, setDiagnosticsView] = useState8(null);
2272
+ const [creating, setCreating] = useState9(false);
2273
+ const [savingProfile, setSavingProfile] = useState9(false);
2274
+ const [baseRevisionOrdinal, setBaseRevisionOrdinal] = useState9(null);
2275
+ const [profileHistory, setProfileHistory] = useState9(null);
2276
+ const [resumeAttempt, setResumeAttempt] = useState9(null);
2277
+ const resumeAttemptsRef = useRef9(/* @__PURE__ */ new Map());
2278
+ const previousSessionIdRef = useRef9(sessionId);
2279
+ const seenInterventionIdsRef = useRef9(/* @__PURE__ */ new Set());
2280
+ const diagnosticRequestRef = useRef9(0);
2281
+ const viewportFocusHandoffRef = useRef9(null);
2282
+ const [diagnosticsView, setDiagnosticsView] = useState9(null);
1733
2283
  const notifyError = useCallback8(
1734
2284
  (cause, fallback) => {
1735
2285
  const message = cause instanceof Error ? cause.message : fallback;
@@ -1737,7 +2287,7 @@ function BrowserViewer({
1737
2287
  },
1738
2288
  [onNotify]
1739
2289
  );
1740
- useEffect8(() => {
2290
+ useEffect9(() => {
1741
2291
  if (previousSessionIdRef.current !== sessionId) {
1742
2292
  previousSessionIdRef.current = sessionId;
1743
2293
  setSelection(
@@ -1745,7 +2295,7 @@ function BrowserViewer({
1745
2295
  );
1746
2296
  }
1747
2297
  }, [initialBrowserSessionId, sessionId]);
1748
- useEffect8(() => {
2298
+ useEffect9(() => {
1749
2299
  if (!enabled || registry.loading) return;
1750
2300
  const selectedStillLive = liveSessions.some((session) => session.id === selection?.sessionId);
1751
2301
  if (selection?.pinned && selectedStillLive) return;
@@ -1776,6 +2326,7 @@ function BrowserViewer({
1776
2326
  );
1777
2327
  const controllerReady = selectedRegistrySession?.lifecycle === "active";
1778
2328
  const resumeSession = registry.resume;
2329
+ const [hasLiveFrame, setHasLiveFrame] = useState9(false);
1779
2330
  const wakeBrowser = useCallback8(
1780
2331
  async (session, retry = false) => {
1781
2332
  const current = resumeAttemptsRef.current.get(session.id);
@@ -1814,25 +2365,22 @@ function BrowserViewer({
1814
2365
  },
1815
2366
  [notifyError, resumeSession]
1816
2367
  );
1817
- useEffect8(() => {
2368
+ useEffect9(() => {
1818
2369
  if (!selectedRegistrySession) return;
1819
2370
  if (selectedRegistrySession.lifecycle === "active") {
1820
2371
  resumeAttemptsRef.current.delete(selectedRegistrySession.id);
1821
2372
  setResumeAttempt(
1822
2373
  (visible) => visible?.sessionId === selectedRegistrySession.id ? null : visible
1823
2374
  );
1824
- return;
1825
2375
  }
1826
- if (selectedRegistrySession.lifecycle === "suspended") {
1827
- void wakeBrowser(selectedRegistrySession);
1828
- }
1829
- }, [selectedRegistrySession, wakeBrowser]);
2376
+ }, [selectedRegistrySession]);
1830
2377
  const browser = useBrowserSession({
1831
2378
  ...override,
1832
2379
  browserSessionId: selection?.sessionId ?? null,
1833
- enabled: enabled && selection !== null && controllerReady
2380
+ enabled: enabled && selection !== null && controllerReady,
2381
+ semanticObservationEnabled: active && !hasLiveFrame
1834
2382
  });
1835
- useEffect8(() => {
2383
+ useEffect9(() => {
1836
2384
  if (!isSourcePlacementChangedError(browser.error, "browser_session")) return;
1837
2385
  void refreshRegistry();
1838
2386
  }, [browser.error, refreshRegistry]);
@@ -1842,7 +2390,7 @@ function BrowserViewer({
1842
2390
  enabled: enabled && diagnosticsView !== null && controllerReady && browser.session?.capabilities.downloads === true
1843
2391
  });
1844
2392
  const loadBrowserDiagnostics = browser.diagnostics;
1845
- useEffect8(() => {
2393
+ useEffect9(() => {
1846
2394
  for (const intervention of interventions.interventions) {
1847
2395
  if (seenInterventionIdsRef.current.has(intervention.id)) continue;
1848
2396
  seenInterventionIdsRef.current.add(intervention.id);
@@ -1874,13 +2422,26 @@ function BrowserViewer({
1874
2422
  stream: { format: "jpeg", quality: 76, maxWidth: 1920, maxHeight: 1200 },
1875
2423
  ...webSocketFactory ? { webSocketFactory } : {}
1876
2424
  });
1877
- const displayedFrame = frameMatchesSelectedTarget(
2425
+ const receivedFrame = frameMatchesSelectedTarget(
1878
2426
  frames.frame,
1879
2427
  browser.session,
1880
2428
  browser.selectedTarget
1881
2429
  ) ? frames.frame : null;
1882
- const supportsLiveFrames = browser.session?.capabilities.liveFrames === true;
1883
- const displayConnectionState = supportsLiveFrames ? frames.state === "live" && !displayedFrame ? "connecting" : frames.state : "semantic";
2430
+ const displayedFrame = useMemo2(
2431
+ () => receivedFrame ? { ...receivedFrame, data: receivedFrame.data.slice().buffer } : null,
2432
+ [receivedFrame]
2433
+ );
2434
+ const frameIsLive = frames.state === "live" && displayedFrame !== null;
2435
+ useEffect9(() => {
2436
+ setHasLiveFrame(frameIsLive);
2437
+ }, [frameIsLive]);
2438
+ const supportsLiveFrames = (browser.session ?? selectedRegistrySession)?.capabilities.liveFrames === true;
2439
+ const controlUnavailable = isInteractionControlUnavailable(browser.error);
2440
+ const connectionError = controlUnavailable ? browser.error : frames.error ?? browser.error;
2441
+ const selectedPlacement = (browser.session ?? selectedRegistrySession)?.placement;
2442
+ const selectedChromeGenerationLoss = selectedPlacement?.kind === "attached_device" && isAttachedChromeGenerationLossError(connectionError);
2443
+ const replacementChromeDevice = selectedChromeGenerationLoss && selectedPlacement.kind === "attached_device" ? attached.devices.find((candidate) => candidate.id === selectedPlacement.deviceId) : null;
2444
+ const displayConnectionState = connectionError && (!frameIsLive || controlUnavailable) ? "error" : !browser.session ? "connecting" : supportsLiveFrames ? frames.state === "live" && !displayedFrame ? "connecting" : frames.state : "semantic";
1884
2445
  const selectedProfile = useMemo2(
1885
2446
  () => profiles.identities.find(
1886
2447
  (identity) => identity.id === (browser.session?.identityId ?? selectedRegistrySession?.identityId)
@@ -1893,7 +2454,7 @@ function BrowserViewer({
1893
2454
  ) : [],
1894
2455
  [selectedProfile, siteAuth.connections]
1895
2456
  );
1896
- useEffect8(() => {
2457
+ useEffect9(() => {
1897
2458
  diagnosticRequestRef.current += 1;
1898
2459
  setDiagnosticsView(null);
1899
2460
  }, [browser.session?.id, browser.selectedTarget?.id]);
@@ -1943,7 +2504,7 @@ function BrowserViewer({
1943
2504
  },
1944
2505
  [downloads, onNotify]
1945
2506
  );
1946
- useEffect8(() => {
2507
+ useEffect9(() => {
1947
2508
  const identityId = browser.session?.identityId ?? selectedRegistrySession?.identityId;
1948
2509
  const revisionId = browser.session?.baseRevisionId ?? selectedRegistrySession?.baseRevisionId;
1949
2510
  if (!identityId) {
@@ -2126,26 +2687,29 @@ function BrowserViewer({
2126
2687
  );
2127
2688
  if (!enabled) return null;
2128
2689
  if (registry.loading && liveSessions.length === 0) {
2129
- return /* @__PURE__ */ jsx(
2690
+ return /* @__PURE__ */ jsx2(
2130
2691
  BrowserNotice,
2131
2692
  {
2132
- icon: /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "animate-spin" }),
2693
+ icon: /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "animate-spin" }),
2133
2694
  text: "Finding browsers\u2026",
2134
2695
  ...className ? { className } : {}
2135
2696
  }
2136
2697
  );
2137
2698
  }
2138
2699
  if (liveSessions.length === 0) {
2139
- if (renderEmpty) return /* @__PURE__ */ jsx(Fragment, { children: renderEmpty(() => createBrowser(), creating) });
2140
- return /* @__PURE__ */ jsx(
2700
+ if (renderEmpty && !unavailableBrowser)
2701
+ return /* @__PURE__ */ jsx2(Fragment2, { children: renderEmpty(() => createBrowser(), creating) });
2702
+ return /* @__PURE__ */ jsx2(
2141
2703
  "div",
2142
2704
  {
2143
2705
  className: cn("flex h-full min-h-0 items-center justify-center bg-og-bg p-6", className),
2144
- children: /* @__PURE__ */ jsxs("div", { className: "max-w-sm text-center", children: [
2145
- /* @__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: /* @__PURE__ */ jsx(Globe2Icon, { className: "size-4.5" }) }),
2146
- /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: attachedGenerationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : "No browser open" }),
2147
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: attachedGenerationLoss ? "This Chrome profile is live again, but the previous browser cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : "A browser appears here when this agent\u2014or another agent in the workspace\u2014opens one." }),
2148
- /* @__PURE__ */ jsx(
2706
+ children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm text-center", children: [
2707
+ /* @__PURE__ */ jsx2("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: /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-4.5" }) }),
2708
+ unavailableBrowser && !attachedGenerationLoss ? /* @__PURE__ */ jsx2(BrowserUnavailableNotice, { session: unavailableBrowser }) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2709
+ /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: attachedGenerationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : "No browser open" }),
2710
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: attachedGenerationLoss ? "This Chrome profile is live again, but the previous browser cannot move to the new connection. Use Browser \u2192 New browser \u2192 Connected Chrome." : "A browser appears here when this agent\u2014or another agent in the workspace\u2014opens one." })
2711
+ ] }),
2712
+ /* @__PURE__ */ jsx2(
2149
2713
  BrowserLaunchMenu,
2150
2714
  {
2151
2715
  attachedBridges: onlineAttachedBridges,
@@ -2159,17 +2723,17 @@ function BrowserViewer({
2159
2723
  prominent: true
2160
2724
  }
2161
2725
  ),
2162
- registry.error ? /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-control text-og-status-error", children: registry.error.message }) : null
2726
+ registry.error ? /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-control text-og-danger", children: registry.error.message }) : null
2163
2727
  ] })
2164
2728
  }
2165
2729
  );
2166
2730
  }
2167
- return /* @__PURE__ */ jsxs(
2731
+ return /* @__PURE__ */ jsxs2(
2168
2732
  "div",
2169
2733
  {
2170
2734
  className: cn("relative flex h-full min-h-0 flex-col overflow-hidden bg-og-bg", className),
2171
2735
  children: [
2172
- /* @__PURE__ */ jsx(
2736
+ /* @__PURE__ */ jsx2(
2173
2737
  BrowserToolbar,
2174
2738
  {
2175
2739
  sessions: liveSessions,
@@ -2199,7 +2763,7 @@ function BrowserViewer({
2199
2763
  setupUrl: browserExtensionSetupUrl
2200
2764
  }
2201
2765
  ),
2202
- /* @__PURE__ */ jsx(
2766
+ /* @__PURE__ */ jsx2(
2203
2767
  InteractionInterventionBanner,
2204
2768
  {
2205
2769
  interventions: selectedInterventions,
@@ -2209,22 +2773,23 @@ function BrowserViewer({
2209
2773
  onResolve: resolveIntervention
2210
2774
  }
2211
2775
  ),
2212
- !selectedRegistrySession ? /* @__PURE__ */ jsx(
2776
+ !selectedRegistrySession ? /* @__PURE__ */ jsx2(
2213
2777
  BrowserUnselectedPanel,
2214
2778
  {
2779
+ unavailableBrowser,
2215
2780
  peerCount: liveSessions.length,
2216
2781
  creating,
2217
2782
  onCreate: () => createBrowser()
2218
2783
  }
2219
- ) : !controllerReady ? /* @__PURE__ */ jsx(
2784
+ ) : !controllerReady ? /* @__PURE__ */ jsx2(
2220
2785
  BrowserLifecyclePanel,
2221
2786
  {
2222
2787
  session: selectedRegistrySession,
2223
2788
  attempt: resumeAttempt?.sessionId === selectedRegistrySession.id ? resumeAttempt.attempt : null,
2224
2789
  onResume: () => void wakeBrowser(selectedRegistrySession, true)
2225
2790
  }
2226
- ) : /* @__PURE__ */ jsxs(Fragment, { children: [
2227
- /* @__PURE__ */ jsx(
2791
+ ) : /* @__PURE__ */ jsxs2(Fragment2, { children: [
2792
+ /* @__PURE__ */ jsx2(
2228
2793
  BrowserTabs,
2229
2794
  {
2230
2795
  targets: browser.targets,
@@ -2236,12 +2801,28 @@ function BrowserViewer({
2236
2801
  onOpen: () => void browser.openTarget(HUMAN_BROWSER_HOME_URL).catch((cause) => notifyError(cause, "Could not open a tab."))
2237
2802
  }
2238
2803
  ),
2239
- /* @__PURE__ */ jsx(
2804
+ /* @__PURE__ */ jsx2(
2240
2805
  BrowserAddressBar,
2241
2806
  {
2242
2807
  target: browser.selectedTarget,
2243
2808
  loading: browser.loading || browser.mutating || savingProfile,
2244
2809
  onNavigate: (url) => void browser.act({ type: "navigate", url }).catch((cause) => notifyError(cause, "Could not navigate.")),
2810
+ onHistory: async (direction) => {
2811
+ try {
2812
+ const action = { type: "history", direction };
2813
+ if (receivedFrame) await browser.actFromFrame(action, receivedFrame);
2814
+ else {
2815
+ if (!browser.observation)
2816
+ throw new Error("The browser page is not ready for input.");
2817
+ await browser.actFromObservation(action, browser.observation);
2818
+ }
2819
+ } catch (cause) {
2820
+ notifyError(
2821
+ cause,
2822
+ direction === "back" ? "Could not go back." : "Could not go forward."
2823
+ );
2824
+ }
2825
+ },
2245
2826
  onReload: () => {
2246
2827
  const url = browser.selectedTarget?.url;
2247
2828
  if (url)
@@ -2249,42 +2830,116 @@ function BrowserViewer({
2249
2830
  }
2250
2831
  }
2251
2832
  ),
2252
- /* @__PURE__ */ jsx(
2253
- BrowserViewport,
2833
+ browser.inputFailure ? /* @__PURE__ */ jsxs2(
2834
+ "div",
2254
2835
  {
2255
- frame: displayedFrame,
2256
- connectionState: displayConnectionState,
2257
- supportsLiveFrames,
2258
- connectionError: frames.error,
2259
- observation: browser.observation,
2260
- mutating: browser.mutating || savingProfile,
2261
- activityLabel: savingProfile ? "Saving browser version\u2026" : void 0,
2262
- clipboardEnabled: browser.session?.capabilities.clipboard === true,
2263
- onAction: async (action, frame) => {
2264
- const receipt = frame ? await browser.actFromFrame(action, frame) : await browser.act(action);
2265
- if (receipt.state !== "completed") {
2266
- throw new Error(receipt.error?.message ?? "Browser input did not complete.");
2267
- }
2268
- return receipt;
2269
- },
2270
- onReadClipboard: browser.readClipboard,
2271
- onReconnect: attachedGenerationLoss || isAttachedChromeGenerationLossError(frames.error) ? () => {
2272
- if (replacementChromeDevice) {
2273
- createBrowser({ kind: "attached", device: replacementChromeDevice });
2274
- }
2275
- } : frames.reconnect,
2276
- reconnectLabel: (attachedGenerationLoss || isAttachedChromeGenerationLossError(frames.error)) && replacementChromeDevice ? "Open a fresh Connected Chrome" : void 0,
2277
- reconnectMessage: attachedGenerationLoss || isAttachedChromeGenerationLossError(frames.error) ? "Chrome reconnected\u2014open a fresh browser/desktop." : void 0,
2278
- onError: (cause) => notifyError(cause, "Browser input failed.")
2836
+ role: "alert",
2837
+ "aria-label": "Browser input status",
2838
+ className: "flex shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-1 px-3 py-2 text-og-xs",
2839
+ children: [
2840
+ /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4 shrink-0 text-og-status-failed" }),
2841
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
2842
+ /* @__PURE__ */ jsx2("span", { className: "font-medium", children: browser.inputFailure.state === "failed" ? "Browser input failed" : "Input result unknown" }),
2843
+ /* @__PURE__ */ jsx2("p", { className: "text-og-fg-subtle", children: browser.inputFailure.error?.message ?? "Inspect the page before continuing." })
2844
+ ] }),
2845
+ /* @__PURE__ */ jsx2(
2846
+ "button",
2847
+ {
2848
+ type: "button",
2849
+ onClick: () => void browser.refresh(),
2850
+ className: "shrink-0 rounded border border-og-border px-2 py-1 transition hover:bg-og-surface-2",
2851
+ children: "Check browser"
2852
+ }
2853
+ )
2854
+ ]
2279
2855
  }
2280
- ),
2281
- /* @__PURE__ */ jsx(
2856
+ ) : null,
2857
+ /* @__PURE__ */ jsxs2("div", { className: "relative flex min-h-0 min-w-0 flex-1 overflow-hidden", children: [
2858
+ /* @__PURE__ */ jsx2(
2859
+ BrowserViewport,
2860
+ {
2861
+ focusHandoffRef: viewportFocusHandoffRef,
2862
+ focusScope: JSON.stringify([
2863
+ selection?.sessionId,
2864
+ browser.selectedTarget?.id,
2865
+ browser.selectedTarget?.targetGeneration
2866
+ ]),
2867
+ frame: displayedFrame,
2868
+ connectionState: displayConnectionState,
2869
+ supportsLiveFrames,
2870
+ connectionError,
2871
+ controlUnavailable,
2872
+ observation: browser.observation,
2873
+ mutating: browser.mutating || savingProfile,
2874
+ activityLabel: savingProfile ? "Saving browser version\u2026" : void 0,
2875
+ clipboardEnabled: browser.session?.capabilities.clipboard === true,
2876
+ inputBatchAttachment: frames.attachment,
2877
+ onAction: async (action, frame) => {
2878
+ const focusedInput = frame && action.type === "pointer" && action.action === "click" && (action.button === void 0 || action.button === "left") && frames.attachment?.focusedInputObservations === true && frames.attachment.browserSessionId === frame.browserSessionId && frames.attachment.controllerGeneration === frame.controllerGeneration && frames.attachment.targetId === frame.targetId && Date.parse(frames.attachment.expiresAt) > Date.now();
2879
+ const receipt = frame ? await browser.actFromFrame(
2880
+ action,
2881
+ frame,
2882
+ void 0,
2883
+ focusedInput ? "input" : "none"
2884
+ ) : await browser.act(action);
2885
+ if (receipt.state !== "completed") {
2886
+ throw new Error(receipt.error?.message ?? "Browser input did not complete.");
2887
+ }
2888
+ return receipt;
2889
+ },
2890
+ onReadClipboard: browser.readClipboard,
2891
+ onObserveForInput: browser.observeForInput,
2892
+ onSelectFromObservation: browser.actFromObservation,
2893
+ onReconnect: selectedChromeGenerationLoss ? () => {
2894
+ if (replacementChromeDevice) {
2895
+ createBrowser({ kind: "attached", device: replacementChromeDevice });
2896
+ }
2897
+ } : () => {
2898
+ void browser.refresh();
2899
+ frames.reconnect();
2900
+ },
2901
+ reconnectLabel: selectedChromeGenerationLoss && replacementChromeDevice ? "Open a fresh Connected Chrome" : void 0,
2902
+ reconnectMessage: selectedChromeGenerationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : void 0,
2903
+ onError: (cause) => notifyError(cause, "Browser input failed.")
2904
+ },
2905
+ JSON.stringify([
2906
+ selection?.sessionId,
2907
+ browser.selectedTarget?.id,
2908
+ browser.selectedTarget?.targetGeneration,
2909
+ browser.selectedTarget?.documentGeneration,
2910
+ frames.attachment?.controllerGeneration
2911
+ ])
2912
+ ),
2913
+ diagnosticsView ? /* @__PURE__ */ jsx2(
2914
+ BrowserDiagnosticsDrawer,
2915
+ {
2916
+ session: browser.session,
2917
+ profile: selectedProfile,
2918
+ target: browser.selectedTarget,
2919
+ observation: browser.observation,
2920
+ baseRevisionOrdinal,
2921
+ state: diagnosticsView,
2922
+ downloads: downloads.downloads,
2923
+ downloadsEnabled: browser.session?.capabilities.downloads === true,
2924
+ downloadsLoading: downloads.loading,
2925
+ downloadsRefreshing: downloads.refreshing,
2926
+ downloadsError: downloads.error,
2927
+ savingDownloadIds: downloads.savingDownloadIds,
2928
+ onSaveDownload: saveDownload,
2929
+ onRefreshDownloads: () => void downloads.refresh(),
2930
+ onRefresh: refreshDiagnostics,
2931
+ onClose: closeDiagnostics
2932
+ }
2933
+ ) : null
2934
+ ] }),
2935
+ /* @__PURE__ */ jsx2(
2282
2936
  BrowserStatusBar,
2283
2937
  {
2284
2938
  session: browser.session,
2285
2939
  profile: selectedProfile,
2286
2940
  target: browser.selectedTarget,
2287
2941
  observation: browser.observation,
2942
+ inputFailure: browser.inputFailure,
2288
2943
  connectionState: displayConnectionState,
2289
2944
  refreshing: registry.refreshing,
2290
2945
  diagnosticsOpen: diagnosticsView !== null,
@@ -2295,28 +2950,7 @@ function BrowserViewer({
2295
2950
  onDiagnostics: diagnosticsView ? closeDiagnostics : loadDiagnostics
2296
2951
  }
2297
2952
  )
2298
- ] }),
2299
- diagnosticsView ? /* @__PURE__ */ jsx(
2300
- BrowserDiagnosticsDrawer,
2301
- {
2302
- session: browser.session,
2303
- profile: selectedProfile,
2304
- target: browser.selectedTarget,
2305
- observation: browser.observation,
2306
- baseRevisionOrdinal,
2307
- state: diagnosticsView,
2308
- downloads: downloads.downloads,
2309
- downloadsEnabled: browser.session?.capabilities.downloads === true,
2310
- downloadsLoading: downloads.loading,
2311
- downloadsRefreshing: downloads.refreshing,
2312
- downloadsError: downloads.error,
2313
- savingDownloadIds: downloads.savingDownloadIds,
2314
- onSaveDownload: saveDownload,
2315
- onRefreshDownloads: () => void downloads.refresh(),
2316
- onRefresh: refreshDiagnostics,
2317
- onClose: closeDiagnostics
2318
- }
2319
- ) : null
2953
+ ] })
2320
2954
  ]
2321
2955
  }
2322
2956
  );
@@ -2325,23 +2959,23 @@ function BrowserLifecyclePanel(props) {
2325
2959
  const failed = props.attempt?.error ?? null;
2326
2960
  const label = failed ? "Browser could not reopen" : props.session.lifecycle === "suspending" ? "Saving browser state\u2026" : props.session.lifecycle === "restoring" || props.attempt?.running ? "Opening browser\u2026" : props.session.lifecycle === "starting" ? "Starting browser\u2026" : "Browser is sleeping";
2327
2961
  const detail = failed ? failed.message : props.session.lifecycle === "suspended" ? "Its private state is saved. Opening it creates a fresh, isolated browser process." : "The browser will reconnect here when it is ready.";
2328
- 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: [
2329
- /* @__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: failed ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4.5 text-og-status-error" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
2330
- /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: label }),
2331
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: detail }),
2332
- failed || props.session.lifecycle === "suspended" && !props.attempt?.running ? /* @__PURE__ */ jsx(
2962
+ return /* @__PURE__ */ jsx2("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm", children: [
2963
+ /* @__PURE__ */ jsx2("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: failed ? /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4.5 text-og-danger" }) : /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-4.5 animate-spin" }) }),
2964
+ /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: label }),
2965
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: detail }),
2966
+ failed || props.session.lifecycle === "suspended" && !props.attempt?.running ? /* @__PURE__ */ jsx2(
2333
2967
  "button",
2334
2968
  {
2335
2969
  type: "button",
2336
2970
  onClick: props.onResume,
2337
- className: "mt-4 inline-flex h-8 items-center rounded-og-sm border border-og-border bg-og-surface-1 px-3 text-og-control font-medium text-og-fg transition hover:bg-og-surface-2",
2971
+ className: "mt-4 inline-flex h-8 items-center rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover",
2338
2972
  children: "Open browser"
2339
2973
  }
2340
2974
  ) : null
2341
2975
  ] }) });
2342
2976
  }
2343
2977
  function BrowserToolbar(props) {
2344
- const detailsRef = useRef8(null);
2978
+ const detailsRef = useViewerMenuDismiss();
2345
2979
  const selected = props.sessions.find((session) => session.id === props.selectedSessionId);
2346
2980
  const current = props.sessions.filter((session) => props.relevantSessionIds.has(session.id));
2347
2981
  const others = props.sessions.filter((session) => !props.relevantSessionIds.has(session.id));
@@ -2349,48 +2983,50 @@ function BrowserToolbar(props) {
2349
2983
  props.onSelect(id);
2350
2984
  detailsRef.current?.removeAttribute("open");
2351
2985
  };
2352
- 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: [
2353
- /* @__PURE__ */ jsxs("details", { ref: detailsRef, className: "relative min-w-0", children: [
2354
- /* @__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: [
2355
- /* @__PURE__ */ jsx(Globe2Icon, { className: "size-3.5 shrink-0 text-og-muted" }),
2356
- /* @__PURE__ */ jsx("span", { className: "truncate font-medium", children: selected?.name ?? "Browser" }),
2357
- (props.interventionCounts.get(selected?.id ?? "") ?? 0) > 0 ? /* @__PURE__ */ jsx("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-waiting" }) : null,
2358
- /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-subtle" })
2986
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-surface-1 px-2", children: [
2987
+ /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: "min-w-0", children: [
2988
+ /* @__PURE__ */ jsxs2("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: [
2989
+ /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
2990
+ /* @__PURE__ */ jsx2("span", { className: "truncate font-medium", children: selected?.name ?? "Browser" }),
2991
+ (props.interventionCounts.get(selected?.id ?? "") ?? 0) > 0 ? /* @__PURE__ */ jsx2("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-waiting" }) : null,
2992
+ /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
2359
2993
  ] }),
2360
- /* @__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: [
2361
- /* @__PURE__ */ jsx(
2362
- BrowserSessionGroup,
2363
- {
2364
- label: "Current agent",
2365
- sessions: current,
2366
- identities: props.identities,
2367
- selectedId: props.selectedSessionId,
2368
- interventionCounts: props.interventionCounts,
2369
- onSelect: choose
2370
- }
2371
- ),
2372
- /* @__PURE__ */ jsx(
2373
- BrowserSessionGroup,
2374
- {
2375
- label: "Workspace browsers",
2376
- sessions: others,
2377
- identities: props.identities,
2378
- selectedId: props.selectedSessionId,
2379
- interventionCounts: props.interventionCounts,
2380
- onSelect: choose
2381
- }
2382
- ),
2383
- /* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1 border-t border-og-border pt-1", children: [
2384
- current.length > 0 ? /* @__PURE__ */ jsx(MenuButton, { onClick: props.onFollow, children: "Follow agent" }) : null,
2385
- /* @__PURE__ */ jsxs(MenuButton, { onClick: () => props.onCreate(), disabled: props.creating, children: [
2386
- /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
2994
+ /* @__PURE__ */ jsxs2("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: [
2995
+ /* @__PURE__ */ jsxs2("div", { className: "min-h-0 max-h-96 overflow-y-auto overscroll-contain", children: [
2996
+ /* @__PURE__ */ jsx2(
2997
+ BrowserSessionGroup,
2998
+ {
2999
+ label: "Current agent",
3000
+ sessions: current,
3001
+ identities: props.identities,
3002
+ selectedId: props.selectedSessionId,
3003
+ interventionCounts: props.interventionCounts,
3004
+ onSelect: choose
3005
+ }
3006
+ ),
3007
+ /* @__PURE__ */ jsx2(
3008
+ BrowserSessionGroup,
3009
+ {
3010
+ label: "Workspace browsers",
3011
+ sessions: others,
3012
+ identities: props.identities,
3013
+ selectedId: props.selectedSessionId,
3014
+ interventionCounts: props.interventionCounts,
3015
+ onSelect: choose
3016
+ }
3017
+ )
3018
+ ] }),
3019
+ /* @__PURE__ */ jsxs2("div", { className: "mt-1 flex shrink-0 gap-1 border-t border-og-border pt-1", children: [
3020
+ current.length > 0 ? /* @__PURE__ */ jsx2(MenuButton, { onClick: props.onFollow, children: "Follow agent" }) : null,
3021
+ /* @__PURE__ */ jsxs2(MenuButton, { onClick: () => props.onCreate(), disabled: props.creating, children: [
3022
+ /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
2387
3023
  " New browser"
2388
3024
  ] })
2389
3025
  ] })
2390
3026
  ] })
2391
3027
  ] }),
2392
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-subtle", children: placementLabel(selected) }),
2393
- /* @__PURE__ */ jsx(
3028
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate text-og-xs text-og-fg-subtle", children: placementLabel(selected) }),
3029
+ /* @__PURE__ */ jsx2(
2394
3030
  BrowserProfileMenu,
2395
3031
  {
2396
3032
  session: selected ?? null,
@@ -2404,17 +3040,17 @@ function BrowserToolbar(props) {
2404
3040
  onOpenVersion: props.onOpenProfileVersion
2405
3041
  }
2406
3042
  ),
2407
- /* @__PURE__ */ jsx(
3043
+ /* @__PURE__ */ jsx2(
2408
3044
  "button",
2409
3045
  {
2410
3046
  type: "button",
2411
3047
  onClick: props.onRefresh,
2412
- className: "grid size-7 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg",
3048
+ className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
2413
3049
  "aria-label": "Refresh browsers",
2414
- children: /* @__PURE__ */ jsx(RefreshCwIcon, { className: cn("size-3.5", props.refreshing && "animate-spin") })
3050
+ children: /* @__PURE__ */ jsx2(RefreshCwIcon, { className: cn("size-3.5", props.refreshing && "animate-spin") })
2415
3051
  }
2416
3052
  ),
2417
- /* @__PURE__ */ jsx(
3053
+ /* @__PURE__ */ jsx2(
2418
3054
  BrowserLaunchMenu,
2419
3055
  {
2420
3056
  attachedBridges: props.attachedBridges,
@@ -2430,43 +3066,57 @@ function BrowserToolbar(props) {
2430
3066
  ] });
2431
3067
  }
2432
3068
  function BrowserUnselectedPanel(props) {
2433
- 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: [
2434
- /* @__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: /* @__PURE__ */ jsx(Globe2Icon, { className: "size-4.5" }) }),
2435
- /* @__PURE__ */ jsx("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "No browser for this agent" }),
2436
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-og-control leading-5 text-og-muted", children: props.peerCount === 1 ? "One workspace browser is available from the browser menu." : `${props.peerCount} workspace browsers are available from the browser menu.` }),
2437
- /* @__PURE__ */ jsxs(
3069
+ return /* @__PURE__ */ jsx2("div", { className: "grid min-h-0 flex-1 place-items-center bg-og-bg p-6 text-center", children: /* @__PURE__ */ jsxs2("div", { className: "max-w-sm", children: [
3070
+ /* @__PURE__ */ jsx2("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: /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-4.5" }) }),
3071
+ props.unavailableBrowser ? /* @__PURE__ */ jsx2(BrowserUnavailableNotice, { session: props.unavailableBrowser }) : /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "No browser for this agent" }),
3072
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-control leading-5 text-og-fg-muted", children: props.peerCount === 1 ? "One workspace browser is available from the browser menu." : `${props.peerCount} workspace browsers are available from the browser menu.` }),
3073
+ /* @__PURE__ */ jsxs2(
2438
3074
  "button",
2439
3075
  {
2440
3076
  type: "button",
2441
3077
  disabled: props.creating,
2442
3078
  onClick: props.onCreate,
2443
- className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm border border-og-border bg-og-surface-1 px-3 text-og-control font-medium text-og-fg transition hover:bg-og-surface-2 disabled:cursor-not-allowed disabled:opacity-50",
3079
+ className: "mt-4 inline-flex h-8 items-center gap-1.5 rounded-og-sm border border-og-primary-border bg-og-primary px-3 text-og-control font-medium text-og-primary-fg transition hover:bg-og-primary-hover disabled:cursor-not-allowed disabled:opacity-50",
2444
3080
  children: [
2445
- /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
3081
+ /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
2446
3082
  props.creating ? "Opening\u2026" : "New browser"
2447
3083
  ]
2448
3084
  }
2449
3085
  )
2450
3086
  ] }) });
2451
3087
  }
3088
+ function BrowserUnavailableNotice({ session }) {
3089
+ const reason = session.failureCode === "provider_deadline_rotation" ? "The computer running it reached its time limit." : session.failureCode === "source_placement_changed" ? "This chat moved to another computer." : session.failureCode === "idle_released" ? "It stopped after it went unused." : session.lifecycle === "failed" ? "It stopped unexpectedly." : "The connection to it was lost.";
3090
+ return /* @__PURE__ */ jsxs2("div", { role: "status", children: [
3091
+ /* @__PURE__ */ jsx2("p", { className: "mt-3 text-og-menu font-medium text-og-fg", children: "Browser unavailable" }),
3092
+ /* @__PURE__ */ jsxs2("p", { className: "mt-1 break-words text-og-control leading-5 text-og-fg-muted", children: [
3093
+ session.name,
3094
+ ". ",
3095
+ reason,
3096
+ " Open a new browser to continue."
3097
+ ] })
3098
+ ] });
3099
+ }
2452
3100
  function BrowserSessionGroup(props) {
2453
3101
  if (props.sessions.length === 0) return null;
2454
- return /* @__PURE__ */ jsxs("div", { className: "py-1", children: [
2455
- /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: props.label }),
3102
+ return /* @__PURE__ */ jsxs2("div", { className: "py-1", children: [
3103
+ /* @__PURE__ */ jsx2("p", { className: "px-2 pb-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: props.label }),
2456
3104
  props.sessions.map((session) => {
2457
3105
  const identity = props.identities.find((candidate) => candidate.id === session.identityId);
2458
3106
  const interventionCount = props.interventionCounts.get(session.id) ?? 0;
2459
- return /* @__PURE__ */ jsxs(
3107
+ return /* @__PURE__ */ jsxs2(
2460
3108
  "button",
2461
3109
  {
2462
3110
  type: "button",
3111
+ "aria-label": `${session.name}${identity ? ` \xB7 ${identity.name}` : ""} \xB7 ${placementLabel(session)}`,
3112
+ "aria-pressed": session.id === props.selectedId,
2463
3113
  onClick: () => props.onSelect(session.id),
2464
3114
  className: cn(
2465
3115
  "flex w-full items-center gap-2 rounded-og-sm px-2 py-1.5 text-left transition hover:bg-og-surface-2",
2466
3116
  session.id === props.selectedId && "bg-og-surface-2"
2467
3117
  ),
2468
3118
  children: [
2469
- /* @__PURE__ */ jsx(
3119
+ /* @__PURE__ */ jsx2(
2470
3120
  "span",
2471
3121
  {
2472
3122
  className: cn(
@@ -2475,14 +3125,14 @@ function BrowserSessionGroup(props) {
2475
3125
  )
2476
3126
  }
2477
3127
  ),
2478
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2479
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: session.name }),
2480
- /* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-subtle", children: [
3128
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3129
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: session.name }),
3130
+ /* @__PURE__ */ jsxs2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: [
2481
3131
  identity ? `${identity.name} \xB7 ` : "",
2482
3132
  placementLabel(session)
2483
3133
  ] })
2484
3134
  ] }),
2485
- interventionCount > 0 ? /* @__PURE__ */ jsx("span", { className: "rounded-full bg-og-status-waiting/12 px-1.5 py-0.5 text-[10px] font-medium text-og-status-waiting", children: interventionCount }) : null
3135
+ interventionCount > 0 ? /* @__PURE__ */ jsx2("span", { className: "rounded-full bg-og-status-waiting/12 px-1.5 py-0.5 text-[10px] font-medium text-og-status-waiting", children: interventionCount }) : null
2486
3136
  ]
2487
3137
  },
2488
3138
  session.id
@@ -2491,7 +3141,7 @@ function BrowserSessionGroup(props) {
2491
3141
  ] });
2492
3142
  }
2493
3143
  function BrowserLaunchMenu(props) {
2494
- const detailsRef = useRef8(null);
3144
+ const detailsRef = useViewerMenuDismiss();
2495
3145
  const choose = (choice) => {
2496
3146
  detailsRef.current?.removeAttribute("open");
2497
3147
  props.onCreate(choice);
@@ -2502,47 +3152,47 @@ function BrowserLaunchMenu(props) {
2502
3152
  const busy = props.creating || props.mutatingProfile;
2503
3153
  const activeIdentities = props.identities.filter((identity) => identity.status === "active");
2504
3154
  const archivedIdentities = props.identities.filter((identity) => identity.status === "archived");
2505
- return /* @__PURE__ */ jsxs("details", { ref: detailsRef, className: cn("relative", props.prominent && "mt-4 inline-block"), children: [
2506
- /* @__PURE__ */ jsxs(
3155
+ return /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: cn(props.prominent && "mt-4 inline-block"), children: [
3156
+ /* @__PURE__ */ jsxs2(
2507
3157
  "summary",
2508
3158
  {
2509
3159
  onClick: (event) => {
2510
3160
  if (busy) event.preventDefault();
2511
3161
  },
2512
3162
  className: cn(
2513
- "cursor-pointer list-none items-center justify-center gap-1.5 text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden",
3163
+ "cursor-pointer list-none items-center justify-center gap-1.5 text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden",
2514
3164
  props.prominent ? "inline-flex h-8 rounded-og-sm border border-og-border bg-og-surface-1 px-3 text-og-control font-medium text-og-fg" : "grid size-7 place-items-center rounded-og-sm",
2515
3165
  busy && "pointer-events-none opacity-50"
2516
3166
  ),
2517
3167
  "aria-label": "New browser",
2518
3168
  children: [
2519
- busy ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" }),
3169
+ busy ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" }),
2520
3170
  props.prominent ? "New browser" : null,
2521
- props.prominent ? /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3" }) : null
3171
+ props.prominent ? /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3" }) : null
2522
3172
  ]
2523
3173
  }
2524
3174
  ),
2525
- /* @__PURE__ */ jsxs(
3175
+ /* @__PURE__ */ jsxs2(
2526
3176
  "div",
2527
3177
  {
2528
3178
  className: cn(
2529
- "absolute z-40 w-72 overflow-hidden rounded-og-md border border-og-border bg-og-surface-1 p-1 text-left shadow-xl",
2530
- props.prominent ? "left-1/2 top-10 -translate-x-1/2" : "right-0 top-8"
3179
+ "absolute z-40 max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] overflow-y-auto rounded-og-md border border-og-border bg-og-surface-1 p-1 text-left shadow-xl",
3180
+ props.prominent ? "left-1/2 top-2 -translate-x-1/2" : "right-2 top-10"
2531
3181
  ),
2532
3182
  children: [
2533
- props.attachedDevices.length > 0 || props.attachedBridges.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "pb-1", children: [
2534
- /* @__PURE__ */ jsx("p", { className: "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: "Connected Chrome" }),
2535
- props.attachedDevices.map((device) => /* @__PURE__ */ jsxs(
3183
+ props.attachedDevices.length > 0 || props.attachedBridges.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "pb-1", children: [
3184
+ /* @__PURE__ */ jsx2("p", { className: "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: "Connected Chrome" }),
3185
+ props.attachedDevices.map((device) => /* @__PURE__ */ jsxs2(
2536
3186
  "button",
2537
3187
  {
2538
3188
  type: "button",
2539
3189
  onClick: () => choose({ kind: "attached", device }),
2540
3190
  className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
2541
3191
  children: [
2542
- /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0 text-og-muted" }),
2543
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2544
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: device.profileLabel ?? device.name }),
2545
- /* @__PURE__ */ jsxs("span", { className: "block truncate text-og-xs text-og-subtle", children: [
3192
+ /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3193
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3194
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: device.profileLabel ?? device.name }),
3195
+ /* @__PURE__ */ jsxs2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: [
2546
3196
  device.browserName,
2547
3197
  " \xB7 ",
2548
3198
  device.tabCount,
@@ -2551,12 +3201,12 @@ function BrowserLaunchMenu(props) {
2551
3201
  device.tabCount === 1 ? "tab" : "tabs"
2552
3202
  ] })
2553
3203
  ] }),
2554
- /* @__PURE__ */ jsx("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-running" })
3204
+ /* @__PURE__ */ jsx2("span", { className: "size-1.5 shrink-0 rounded-full bg-og-status-running" })
2555
3205
  ]
2556
3206
  },
2557
3207
  device.id
2558
3208
  )),
2559
- props.attachedBridges.length > 0 ? props.setupUrl ? /* @__PURE__ */ jsxs(
3209
+ props.attachedBridges.length > 0 ? props.setupUrl ? /* @__PURE__ */ jsxs2(
2560
3210
  "a",
2561
3211
  {
2562
3212
  href: props.setupUrl,
@@ -2565,70 +3215,70 @@ function BrowserLaunchMenu(props) {
2565
3215
  onClick: () => detailsRef.current?.removeAttribute("open"),
2566
3216
  className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
2567
3217
  children: [
2568
- /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5 shrink-0 text-og-muted" }),
2569
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2570
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: props.attachedDevices.length > 0 ? "Connect another Chrome profile" : "Connect this Chrome profile" }),
2571
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-xs text-og-subtle", children: "Machine connected \xB7 Chrome extension missing" })
3218
+ /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3219
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3220
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: props.attachedDevices.length > 0 ? "Connect another Chrome profile" : "Connect this Chrome profile" }),
3221
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-xs text-og-fg-subtle", children: "Machine connected \xB7 Chrome extension missing" })
2572
3222
  ] })
2573
3223
  ]
2574
3224
  }
2575
- ) : /* @__PURE__ */ jsxs("div", { className: "flex w-full items-center gap-2 px-2 py-2 text-left", children: [
2576
- /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5 shrink-0 text-og-muted" }),
2577
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2578
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: "Chrome connection ready" }),
2579
- /* @__PURE__ */ jsx("span", { className: "block text-og-xs leading-4 text-og-subtle", children: "Install the OpenGeni Browser extension in the profile you want to use." })
3225
+ ) : /* @__PURE__ */ jsxs2("div", { className: "flex w-full items-center gap-2 px-2 py-2 text-left", children: [
3226
+ /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5 shrink-0 text-og-fg-muted" }),
3227
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3228
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: "Chrome connection ready" }),
3229
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-xs leading-4 text-og-fg-subtle", children: "Install the Opengeni Browser extension in the profile you want to use." })
2580
3230
  ] })
2581
3231
  ] }) : null
2582
3232
  ] }) : null,
2583
- /* @__PURE__ */ jsx(
3233
+ /* @__PURE__ */ jsx2(
2584
3234
  "p",
2585
3235
  {
2586
3236
  className: cn(
2587
- "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle",
3237
+ "px-2 pb-1 pt-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle",
2588
3238
  (props.attachedDevices.length > 0 || props.attachedBridges.length > 0) && "border-t border-og-border"
2589
3239
  ),
2590
3240
  children: "Open isolated browser"
2591
3241
  }
2592
3242
  ),
2593
- /* @__PURE__ */ jsxs(
3243
+ /* @__PURE__ */ jsxs2(
2594
3244
  "button",
2595
3245
  {
2596
3246
  type: "button",
2597
3247
  onClick: () => choose({ kind: "clean" }),
2598
3248
  className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
2599
3249
  children: [
2600
- /* @__PURE__ */ jsx(Globe2Icon, { className: "size-3.5 text-og-muted" }),
2601
- /* @__PURE__ */ jsxs("span", { children: [
2602
- /* @__PURE__ */ jsx("span", { className: "block text-og-control text-og-fg", children: "Fresh browser" }),
2603
- /* @__PURE__ */ jsx("span", { className: "block text-og-xs text-og-subtle", children: "Visual browser \xB7 no saved state" })
3250
+ /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3.5 text-og-fg-muted" }),
3251
+ /* @__PURE__ */ jsxs2("span", { children: [
3252
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-control text-og-fg", children: "Fresh browser" }),
3253
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-xs text-og-fg-subtle", children: "Visual browser \xB7 no saved state" })
2604
3254
  ] })
2605
3255
  ]
2606
3256
  }
2607
3257
  ),
2608
- activeIdentities.length > 0 ? /* @__PURE__ */ jsx("div", { className: "mt-1 border-t border-og-border pt-1", children: activeIdentities.map((identity) => /* @__PURE__ */ jsxs(
3258
+ activeIdentities.length > 0 ? /* @__PURE__ */ jsx2("div", { className: "mt-1 border-t border-og-border pt-1", children: activeIdentities.map((identity) => /* @__PURE__ */ jsxs2(
2609
3259
  "button",
2610
3260
  {
2611
3261
  type: "button",
2612
3262
  onClick: () => choose({ kind: "profile", identityId: identity.id }),
2613
3263
  className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2",
2614
3264
  children: [
2615
- /* @__PURE__ */ jsx(UserRoundIcon, { className: "size-3.5 text-og-muted" }),
2616
- /* @__PURE__ */ jsxs("span", { className: "min-w-0", children: [
2617
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: identity.name }),
2618
- /* @__PURE__ */ jsx("span", { className: "block text-og-xs text-og-subtle", children: identity.defaultRevisionId ? `${identity.revisionCount} saved version${identity.revisionCount === 1 ? "" : "s"}` : "Ready for first sign-in" })
3265
+ /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3.5 text-og-fg-muted" }),
3266
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0", children: [
3267
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: identity.name }),
3268
+ /* @__PURE__ */ jsx2("span", { className: "block text-og-xs text-og-fg-subtle", children: identity.defaultRevisionId ? `${identity.revisionCount} saved version${identity.revisionCount === 1 ? "" : "s"}` : "Ready for first sign-in" })
2619
3269
  ] })
2620
3270
  ]
2621
3271
  },
2622
3272
  identity.id
2623
3273
  )) }) : null,
2624
- archivedIdentities.length > 0 ? /* @__PURE__ */ jsxs("details", { className: "mt-1 border-t border-og-border pt-1", children: [
2625
- /* @__PURE__ */ jsxs("summary", { className: "flex cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 py-1.5 text-og-xs text-og-subtle transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
2626
- /* @__PURE__ */ jsx(ArchiveIcon, { className: "size-3" }),
3274
+ archivedIdentities.length > 0 ? /* @__PURE__ */ jsxs2("details", { className: "mt-1 border-t border-og-border pt-1", children: [
3275
+ /* @__PURE__ */ jsxs2("summary", { className: "flex cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 py-1.5 text-og-xs text-og-fg-subtle transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
3276
+ /* @__PURE__ */ jsx2(ArchiveIcon, { className: "size-3" }),
2627
3277
  " Hidden profiles \xB7 ",
2628
3278
  archivedIdentities.length,
2629
- /* @__PURE__ */ jsx(ChevronDownIcon, { className: "ml-auto size-3" })
3279
+ /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "ml-auto size-3" })
2630
3280
  ] }),
2631
- archivedIdentities.map((identity) => /* @__PURE__ */ jsxs(
3281
+ archivedIdentities.map((identity) => /* @__PURE__ */ jsxs2(
2632
3282
  "button",
2633
3283
  {
2634
3284
  type: "button",
@@ -2636,8 +3286,8 @@ function BrowserLaunchMenu(props) {
2636
3286
  onClick: () => void restore(identity),
2637
3287
  className: "flex w-full items-center gap-2 rounded-og-sm px-2 py-2 text-left transition hover:bg-og-surface-2 disabled:opacity-50",
2638
3288
  children: [
2639
- /* @__PURE__ */ jsx(RotateCcwIcon, { className: "size-3.5 text-og-muted" }),
2640
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1 truncate text-og-control text-og-fg", children: [
3289
+ /* @__PURE__ */ jsx2(RotateCcwIcon, { className: "size-3.5 text-og-fg-muted" }),
3290
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1 truncate text-og-control text-og-fg", children: [
2641
3291
  "Restore ",
2642
3292
  identity.name
2643
3293
  ] })
@@ -2652,9 +3302,9 @@ function BrowserLaunchMenu(props) {
2652
3302
  ] });
2653
3303
  }
2654
3304
  function BrowserProfileMenu(props) {
2655
- const detailsRef = useRef8(null);
3305
+ const detailsRef = useViewerMenuDismiss();
2656
3306
  const nameInputId = useId();
2657
- const [name, setName] = useState8("");
3307
+ const [name, setName] = useState9("");
2658
3308
  const attached = props.session?.placement.kind === "attached_device";
2659
3309
  const canSave = (props.session?.capabilities.identityPublication ?? false) && props.identity?.status !== "archived";
2660
3310
  const revisions = props.identity && props.history?.identityId === props.identity.id ? [...props.history.revisions].sort((left, right) => right.ordinal - left.ordinal) : [];
@@ -2669,36 +3319,36 @@ function BrowserProfileMenu(props) {
2669
3319
  props.onOpenVersion(identityId, baseRevisionId);
2670
3320
  };
2671
3321
  const versionLabel = attached ? "live" : props.identity ? props.baseRevisionOrdinal ? `v${props.baseRevisionOrdinal}` : props.session?.baseRevisionId ? "saved" : "unsaved" : "temporary";
2672
- return /* @__PURE__ */ jsxs("details", { ref: detailsRef, className: "relative min-w-0", children: [
2673
- /* @__PURE__ */ jsxs("summary", { className: "flex h-7 max-w-40 cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 text-og-xs text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
2674
- props.saving ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }) : attached ? /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3" }) : /* @__PURE__ */ jsx(UserRoundIcon, { className: "size-3" }),
2675
- /* @__PURE__ */ jsx("span", { className: "truncate", children: attached ? props.session?.name : props.identity?.name ?? "Temporary" }),
2676
- attached || props.identity ? /* @__PURE__ */ jsxs("span", { className: "shrink-0 text-og-subtle", children: [
3322
+ return /* @__PURE__ */ jsxs2("details", { ref: detailsRef, className: "min-w-0", children: [
3323
+ /* @__PURE__ */ jsxs2("summary", { className: "flex h-7 max-w-40 cursor-pointer list-none items-center gap-1.5 rounded-og-sm px-2 text-og-xs text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg [&::-webkit-details-marker]:hidden", children: [
3324
+ props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : attached ? /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3" }) : /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3" }),
3325
+ /* @__PURE__ */ jsx2("span", { className: "truncate", children: attached ? props.session?.name : props.identity?.name ?? "Temporary" }),
3326
+ attached || props.identity ? /* @__PURE__ */ jsxs2("span", { className: "shrink-0 text-og-fg-subtle", children: [
2677
3327
  "\xB7 ",
2678
3328
  versionLabel
2679
3329
  ] }) : null,
2680
- /* @__PURE__ */ jsx(ChevronDownIcon, { className: "size-3 shrink-0 text-og-subtle" })
3330
+ /* @__PURE__ */ jsx2(ChevronDownIcon, { className: "size-3 shrink-0 text-og-fg-subtle" })
2681
3331
  ] }),
2682
- /* @__PURE__ */ jsxs("div", { className: "absolute right-0 top-8 z-40 w-72 rounded-og-md border border-og-border bg-og-surface-1 p-3 shadow-xl", children: [
2683
- /* @__PURE__ */ jsxs("div", { className: "flex items-start gap-2", children: [
2684
- /* @__PURE__ */ jsx("span", { className: "grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-surface-2 text-og-muted", children: attached ? /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx(UserRoundIcon, { className: "size-3.5" }) }),
2685
- /* @__PURE__ */ jsxs("div", { className: "min-w-0", children: [
2686
- /* @__PURE__ */ jsx("p", { className: "truncate text-og-control font-medium text-og-fg", children: attached ? props.session?.name : props.identity?.name ?? "Temporary browser" }),
2687
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-og-xs leading-4 text-og-subtle", children: attached ? "This session drives the existing Chrome profile and its current login state." : props.identity?.status === "archived" ? "Hidden from new browsers. This already-open browser is unchanged." : props.identity ? props.baseRevisionOrdinal ? `Started from version ${props.baseRevisionOrdinal}.` : "Not saved yet." : "This browser is not reusable yet." })
3332
+ /* @__PURE__ */ jsxs2("div", { className: "absolute right-2 top-10 z-40 max-h-[calc(100%-3rem)] w-72 max-w-[calc(100%-1rem)] overflow-y-auto rounded-og-md border border-og-border bg-og-surface-1 p-3 shadow-xl", children: [
3333
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-start gap-2", children: [
3334
+ /* @__PURE__ */ jsx2("span", { className: "grid size-7 shrink-0 place-items-center rounded-og-sm bg-og-surface-2 text-og-fg-muted", children: attached ? /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(UserRoundIcon, { className: "size-3.5" }) }),
3335
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0", children: [
3336
+ /* @__PURE__ */ jsx2("p", { className: "truncate text-og-control font-medium text-og-fg", children: attached ? props.session?.name : props.identity?.name ?? "Temporary browser" }),
3337
+ /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-og-xs leading-4 text-og-fg-subtle", children: attached ? "This session drives the existing Chrome profile and its current login state." : props.identity?.status === "archived" ? "Hidden from new browsers. This already-open browser is unchanged." : props.identity ? props.baseRevisionOrdinal ? `Started from version ${props.baseRevisionOrdinal}.` : "Not saved yet." : "This browser is not reusable yet." })
2688
3338
  ] })
2689
3339
  ] }),
2690
- props.identity && revisions.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-2", children: [
2691
- /* @__PURE__ */ jsx("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: "Saved versions" }),
2692
- /* @__PURE__ */ jsx("div", { className: "mt-1 max-h-40 overflow-y-auto", children: revisions.map((revision) => {
3340
+ props.identity && revisions.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-2", children: [
3341
+ /* @__PURE__ */ jsx2("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: "Saved versions" }),
3342
+ /* @__PURE__ */ jsx2("div", { className: "mt-1 max-h-40 overflow-y-auto", children: revisions.map((revision) => {
2693
3343
  const isDefault = revision.id === props.identity?.defaultRevisionId;
2694
3344
  const isCurrent = revision.ordinal === props.baseRevisionOrdinal;
2695
3345
  const materialization = revisionMaterializationSummary(revision);
2696
- return /* @__PURE__ */ jsxs(
3346
+ return /* @__PURE__ */ jsxs2(
2697
3347
  "div",
2698
3348
  {
2699
3349
  className: "flex w-full items-center gap-1 rounded-og-sm transition hover:bg-og-surface-2",
2700
3350
  children: [
2701
- /* @__PURE__ */ jsxs(
3351
+ /* @__PURE__ */ jsxs2(
2702
3352
  "button",
2703
3353
  {
2704
3354
  type: "button",
@@ -2707,19 +3357,19 @@ function BrowserProfileMenu(props) {
2707
3357
  onClick: () => openVersion(props.identity.id, revision.id),
2708
3358
  className: "flex min-w-0 flex-1 items-center gap-2 px-2 py-1.5 text-left disabled:opacity-50",
2709
3359
  children: [
2710
- /* @__PURE__ */ jsx("span", { className: "grid size-4 place-items-center text-og-muted", children: isCurrent ? /* @__PURE__ */ jsx(CheckIcon, { className: "size-3.5 text-og-accent" }) : null }),
2711
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2712
- /* @__PURE__ */ jsxs("span", { className: "block text-og-control text-og-fg", children: [
3360
+ /* @__PURE__ */ jsx2("span", { className: "grid size-4 place-items-center text-og-fg-muted", children: isCurrent ? /* @__PURE__ */ jsx2(CheckIcon, { className: "size-3.5 text-og-accent" }) : null }),
3361
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3362
+ /* @__PURE__ */ jsxs2("span", { className: "block text-og-control text-og-fg", children: [
2713
3363
  "Version ",
2714
3364
  revision.ordinal
2715
3365
  ] }),
2716
- /* @__PURE__ */ jsx("span", { className: "block truncate text-[10px] text-og-subtle", children: materialization.label })
3366
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-[10px] text-og-fg-subtle", children: materialization.label })
2717
3367
  ] }),
2718
- isCurrent ? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-og-subtle", children: "Current" }) : null
3368
+ isCurrent ? /* @__PURE__ */ jsx2("span", { className: "text-[10px] text-og-fg-subtle", children: "Current" }) : null
2719
3369
  ]
2720
3370
  }
2721
3371
  ),
2722
- /* @__PURE__ */ jsx(
3372
+ /* @__PURE__ */ jsx2(
2723
3373
  "button",
2724
3374
  {
2725
3375
  type: "button",
@@ -2727,7 +3377,7 @@ function BrowserProfileMenu(props) {
2727
3377
  "aria-label": isDefault ? `${props.identity.name} version ${revision.ordinal} is the default` : `Use ${props.identity.name} version ${revision.ordinal} by default`,
2728
3378
  title: isDefault ? "Default version" : "Use by default",
2729
3379
  onClick: () => void props.onUpdate(props.identity, { defaultRevisionId: revision.id }),
2730
- className: "mr-1 shrink-0 rounded-og-sm px-2 py-1 text-[10px] text-og-subtle transition hover:bg-og-surface-3 hover:text-og-fg disabled:cursor-default disabled:opacity-70",
3380
+ className: "mr-1 shrink-0 rounded-og-sm px-2 py-1 text-[10px] text-og-fg-subtle transition hover:bg-og-surface-3 hover:text-og-fg disabled:cursor-default disabled:opacity-70",
2731
3381
  children: isDefault ? "Default" : "Make default"
2732
3382
  }
2733
3383
  )
@@ -2736,24 +3386,24 @@ function BrowserProfileMenu(props) {
2736
3386
  revision.id
2737
3387
  );
2738
3388
  }) }),
2739
- /* @__PURE__ */ jsx("p", { className: "mt-1 px-2 text-[10px] leading-4 text-og-subtle", children: "Changing the default affects only browsers opened later. Saved browser data can be copied; a website may still expire or re-verify its own session." })
2740
- ] }) : props.identity && props.history?.identityId === props.identity.id && props.history.loading ? /* @__PURE__ */ jsxs("p", { className: "mt-3 flex items-center gap-1.5 border-t border-og-border px-2 pt-2 text-og-xs text-og-subtle", children: [
2741
- /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }),
3389
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 px-2 text-[10px] leading-4 text-og-fg-subtle", children: "Changing the default affects only browsers opened later. Saved browser data can be copied; a website may still expire or re-verify its own session." })
3390
+ ] }) : props.identity && props.history?.identityId === props.identity.id && props.history.loading ? /* @__PURE__ */ jsxs2("p", { className: "mt-3 flex items-center gap-1.5 border-t border-og-border px-2 pt-2 text-og-xs text-og-fg-subtle", children: [
3391
+ /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }),
2742
3392
  " Loading saved versions\u2026"
2743
3393
  ] }) : null,
2744
- props.identity && props.authConnections.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-2", children: [
2745
- /* @__PURE__ */ jsx("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-subtle", children: "Login health" }),
2746
- /* @__PURE__ */ jsx("div", { className: "mt-1 space-y-1", children: props.authConnections.map((connection) => {
3394
+ props.identity && props.authConnections.length > 0 ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-2", children: [
3395
+ /* @__PURE__ */ jsx2("p", { className: "px-1 text-[10px] font-medium uppercase tracking-[0.12em] text-og-fg-subtle", children: "Login health" }),
3396
+ /* @__PURE__ */ jsx2("div", { className: "mt-1 space-y-1", children: props.authConnections.map((connection) => {
2747
3397
  const health = siteAuthHealth(connection);
2748
- return /* @__PURE__ */ jsxs(
3398
+ return /* @__PURE__ */ jsxs2(
2749
3399
  "div",
2750
3400
  {
2751
3401
  className: "flex items-start gap-2 rounded-og-sm px-2 py-1.5",
2752
3402
  children: [
2753
- /* @__PURE__ */ jsx("span", { className: cn("mt-1 size-1.5 shrink-0 rounded-full", health.dotClass) }),
2754
- /* @__PURE__ */ jsxs("span", { className: "min-w-0 flex-1", children: [
2755
- /* @__PURE__ */ jsx("span", { className: "block truncate text-og-control text-og-fg", children: connection.name }),
2756
- /* @__PURE__ */ jsxs("span", { className: "block truncate text-[10px] text-og-subtle", children: [
3403
+ /* @__PURE__ */ jsx2("span", { className: cn("mt-1 size-1.5 shrink-0 rounded-full", health.dotClass) }),
3404
+ /* @__PURE__ */ jsxs2("span", { className: "min-w-0 flex-1", children: [
3405
+ /* @__PURE__ */ jsx2("span", { className: "block truncate text-og-control text-og-fg", children: connection.name }),
3406
+ /* @__PURE__ */ jsxs2("span", { className: "block truncate text-[10px] text-og-fg-subtle", children: [
2757
3407
  connection.accountLabel,
2758
3408
  " \xB7 ",
2759
3409
  health.label
@@ -2765,7 +3415,7 @@ function BrowserProfileMenu(props) {
2765
3415
  );
2766
3416
  }) })
2767
3417
  ] }) : null,
2768
- canSave ? props.identity ? /* @__PURE__ */ jsxs(
3418
+ canSave ? props.identity ? /* @__PURE__ */ jsxs2(
2769
3419
  "button",
2770
3420
  {
2771
3421
  type: "button",
@@ -2773,11 +3423,11 @@ function BrowserProfileMenu(props) {
2773
3423
  disabled: props.saving,
2774
3424
  className: "mt-3 flex h-8 w-full items-center justify-center gap-1.5 rounded-og-sm border border-og-border bg-og-surface-2 px-3 text-og-control font-medium text-og-fg transition hover:bg-og-surface-3 disabled:opacity-50",
2775
3425
  children: [
2776
- props.saving ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(SaveIcon, { className: "size-3.5" }),
3426
+ props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3.5" }),
2777
3427
  props.saving ? "Saving browser version\u2026" : "Save new version"
2778
3428
  ]
2779
3429
  }
2780
- ) : /* @__PURE__ */ jsxs(
3430
+ ) : /* @__PURE__ */ jsxs2(
2781
3431
  "form",
2782
3432
  {
2783
3433
  className: "mt-3",
@@ -2786,9 +3436,9 @@ function BrowserProfileMenu(props) {
2786
3436
  if (name.trim()) void save(name);
2787
3437
  },
2788
3438
  children: [
2789
- /* @__PURE__ */ jsx("label", { className: "text-og-xs font-medium text-og-muted", htmlFor: nameInputId, children: "Profile name" }),
2790
- /* @__PURE__ */ jsxs("div", { className: "mt-1 flex gap-1.5", children: [
2791
- /* @__PURE__ */ jsx(
3439
+ /* @__PURE__ */ jsx2("label", { className: "text-og-xs font-medium text-og-fg-muted", htmlFor: nameInputId, children: "Profile name" }),
3440
+ /* @__PURE__ */ jsxs2("div", { className: "mt-1 flex gap-1.5", children: [
3441
+ /* @__PURE__ */ jsx2(
2792
3442
  "input",
2793
3443
  {
2794
3444
  id: nameInputId,
@@ -2796,17 +3446,17 @@ function BrowserProfileMenu(props) {
2796
3446
  onInput: (event) => setName(event.currentTarget.value),
2797
3447
  maxLength: 200,
2798
3448
  placeholder: "Work",
2799
- className: "h-8 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-bg px-2 text-og-control text-og-fg placeholder:text-og-subtle focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent"
3449
+ className: "h-8 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-bg px-2 text-og-control text-og-fg placeholder:text-og-fg-subtle focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent"
2800
3450
  }
2801
3451
  ),
2802
- /* @__PURE__ */ jsxs(
3452
+ /* @__PURE__ */ jsxs2(
2803
3453
  "button",
2804
3454
  {
2805
3455
  type: "submit",
2806
3456
  disabled: !name.trim() || props.saving,
2807
- className: "inline-flex h-8 items-center gap-1 rounded-og-sm bg-og-accent px-2.5 text-og-control font-medium text-white transition hover:opacity-90 disabled:opacity-40",
3457
+ className: "inline-flex h-8 items-center gap-1 rounded-og-sm border border-og-primary-border bg-og-primary text-og-primary-fg px-2.5 text-og-control font-medium transition hover:bg-og-primary-hover disabled:opacity-50",
2808
3458
  children: [
2809
- props.saving ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx(SaveIcon, { className: "size-3.5" }),
3459
+ props.saving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3.5" }),
2810
3460
  "Save"
2811
3461
  ]
2812
3462
  }
@@ -2814,8 +3464,8 @@ function BrowserProfileMenu(props) {
2814
3464
  ] })
2815
3465
  ]
2816
3466
  }
2817
- ) : /* @__PURE__ */ jsx("p", { className: "mt-3 rounded-og-sm bg-og-surface-2 px-2.5 py-2 text-og-xs text-og-subtle", children: attached ? "Chrome keeps this profile's state directly; OpenGeni does not copy it automatically." : props.identity?.status === "archived" ? "Restore this profile before saving another version." : "This browser cannot save reusable profile state." }),
2818
- !attached && props.identity ? /* @__PURE__ */ jsxs(
3467
+ ) : /* @__PURE__ */ jsx2("p", { className: "mt-3 rounded-og-sm bg-og-surface-2 px-2.5 py-2 text-og-xs text-og-fg-subtle", children: attached ? "Chrome keeps this profile's state directly; Opengeni does not copy it automatically." : props.identity?.status === "archived" ? "Restore this profile before saving another version." : "This browser cannot save reusable profile state." }),
3468
+ !attached && props.identity ? /* @__PURE__ */ jsxs2(
2819
3469
  "button",
2820
3470
  {
2821
3471
  type: "button",
@@ -2823,14 +3473,14 @@ function BrowserProfileMenu(props) {
2823
3473
  onClick: () => void props.onUpdate(props.identity, {
2824
3474
  status: props.identity.status === "active" ? "archived" : "active"
2825
3475
  }),
2826
- className: "mt-2 flex h-8 w-full items-center justify-center gap-1.5 rounded-og-sm px-3 text-og-control text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
3476
+ className: "mt-2 flex h-8 w-full items-center justify-center gap-1.5 rounded-og-sm px-3 text-og-control text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-50",
2827
3477
  children: [
2828
- props.identity.status === "active" ? /* @__PURE__ */ jsx(ArchiveIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx(RotateCcwIcon, { className: "size-3.5" }),
3478
+ props.identity.status === "active" ? /* @__PURE__ */ jsx2(ArchiveIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(RotateCcwIcon, { className: "size-3.5" }),
2829
3479
  props.identity.status === "active" ? "Hide from new browsers" : "Restore profile"
2830
3480
  ]
2831
3481
  }
2832
3482
  ) : null,
2833
- canSave ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-[10px] leading-4 text-og-subtle", children: "Saving briefly restarts this browser. Other open browsers are unchanged." }) : null
3483
+ canSave ? /* @__PURE__ */ jsx2("p", { className: "mt-2 text-[10px] leading-4 text-og-fg-subtle", children: "Saving restarts this browser. Save unfinished work on its pages first; unsaved form entries may be lost. Other open browsers are unchanged." }) : null
2834
3484
  ] })
2835
3485
  ] });
2836
3486
  }
@@ -2867,29 +3517,29 @@ function siteAuthHealth(connection) {
2867
3517
  }
2868
3518
  }
2869
3519
  function MenuButton(props) {
2870
- return /* @__PURE__ */ jsx(
3520
+ return /* @__PURE__ */ jsx2(
2871
3521
  "button",
2872
3522
  {
2873
3523
  type: "button",
2874
3524
  onClick: props.onClick,
2875
3525
  disabled: props.disabled,
2876
- 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",
3526
+ 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",
2877
3527
  children: props.children
2878
3528
  }
2879
3529
  );
2880
3530
  }
2881
3531
  function BrowserTabs(props) {
2882
3532
  const pages = props.targets.filter((target) => target.kind === "page" || target.kind === "popup");
2883
- return /* @__PURE__ */ jsxs("div", { className: "flex h-9 shrink-0 items-end gap-px overflow-x-auto border-b border-og-border bg-og-surface-0 px-1 pt-1", children: [
2884
- pages.map((target) => /* @__PURE__ */ jsxs(
3533
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-9 shrink-0 items-end gap-px overflow-x-auto border-b border-og-border bg-og-surface-1 px-1 pt-1", children: [
3534
+ pages.map((target) => /* @__PURE__ */ jsxs2(
2885
3535
  "div",
2886
3536
  {
2887
3537
  className: cn(
2888
3538
  "group flex h-8 min-w-24 max-w-48 items-center rounded-t-og-sm border border-b-0 border-transparent px-2",
2889
- target.id === props.selectedTargetId ? "border-og-border bg-og-bg text-og-fg" : "text-og-muted hover:bg-og-surface-1"
3539
+ target.id === props.selectedTargetId ? "border-og-border bg-og-bg text-og-fg" : "text-og-fg-muted hover:bg-og-surface-1"
2890
3540
  ),
2891
3541
  children: [
2892
- /* @__PURE__ */ jsx(
3542
+ /* @__PURE__ */ jsx2(
2893
3543
  "button",
2894
3544
  {
2895
3545
  type: "button",
@@ -2898,7 +3548,7 @@ function BrowserTabs(props) {
2898
3548
  children: target.title || shortUrl(target.url)
2899
3549
  }
2900
3550
  ),
2901
- props.tabControl ? /* @__PURE__ */ jsx(
3551
+ props.tabControl ? /* @__PURE__ */ jsx2(
2902
3552
  "button",
2903
3553
  {
2904
3554
  type: "button",
@@ -2906,57 +3556,94 @@ function BrowserTabs(props) {
2906
3556
  disabled: props.mutating,
2907
3557
  className: "ml-1 grid size-4 shrink-0 place-items-center rounded opacity-0 transition hover:bg-og-surface-3 group-hover:opacity-100 focus:opacity-100 disabled:opacity-30",
2908
3558
  "aria-label": `Close ${target.title || "tab"}`,
2909
- children: /* @__PURE__ */ jsx(XIcon, { className: "size-3" })
3559
+ children: /* @__PURE__ */ jsx2(XIcon, { className: "size-3" })
2910
3560
  }
2911
3561
  ) : null
2912
3562
  ]
2913
3563
  },
2914
3564
  target.id
2915
3565
  )),
2916
- props.tabControl ? /* @__PURE__ */ jsx(
3566
+ props.tabControl ? /* @__PURE__ */ jsx2(
2917
3567
  "button",
2918
3568
  {
2919
3569
  type: "button",
2920
3570
  onClick: props.onOpen,
2921
3571
  disabled: props.mutating,
2922
- className: "mb-1 grid size-6 shrink-0 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
3572
+ className: "mb-1 grid size-6 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
2923
3573
  "aria-label": "New tab",
2924
- children: /* @__PURE__ */ jsx(PlusIcon, { className: "size-3.5" })
3574
+ children: /* @__PURE__ */ jsx2(PlusIcon, { className: "size-3.5" })
2925
3575
  }
2926
3576
  ) : null
2927
3577
  ] });
2928
3578
  }
2929
3579
  function BrowserAddressBar(props) {
2930
- const [draft, setDraft] = useState8(props.target?.url ?? "");
2931
- const focusedRef = useRef8(false);
2932
- useEffect8(() => {
3580
+ const [draft, setDraft] = useState9(props.target?.url ?? "");
3581
+ const focusedRef = useRef9(false);
3582
+ const historyPendingRef = useRef9(false);
3583
+ const [historyPending, setHistoryPending] = useState9(false);
3584
+ useEffect9(() => {
2933
3585
  if (!focusedRef.current) setDraft(props.target?.url ?? "");
2934
3586
  }, [props.target?.id, props.target?.url]);
2935
3587
  const submit = (event) => {
2936
3588
  event.preventDefault();
3589
+ if (historyPendingRef.current) return;
2937
3590
  const normalized = normalizeBrowserAddress(draft);
2938
3591
  if (normalized) props.onNavigate(normalized);
2939
3592
  };
2940
- return /* @__PURE__ */ jsxs(
3593
+ const navigateHistory = async (direction) => {
3594
+ if (!props.target || props.loading || historyPendingRef.current) return;
3595
+ historyPendingRef.current = true;
3596
+ setHistoryPending(true);
3597
+ try {
3598
+ await props.onHistory(direction);
3599
+ } finally {
3600
+ historyPendingRef.current = false;
3601
+ setHistoryPending(false);
3602
+ }
3603
+ };
3604
+ return /* @__PURE__ */ jsxs2(
2941
3605
  "form",
2942
3606
  {
2943
3607
  onSubmit: submit,
3608
+ "aria-busy": historyPending || void 0,
2944
3609
  className: "flex h-10 shrink-0 items-center gap-1.5 border-b border-og-border bg-og-bg px-2",
2945
3610
  children: [
2946
- /* @__PURE__ */ jsx(
3611
+ ["back", "forward"].map((direction) => /* @__PURE__ */ jsx2(
2947
3612
  "button",
2948
3613
  {
2949
3614
  type: "button",
2950
- onClick: props.onReload,
3615
+ onClick: () => void navigateHistory(direction),
2951
3616
  disabled: !props.target || props.loading,
2952
- 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-35",
3617
+ "aria-disabled": historyPending || void 0,
3618
+ className: cn(
3619
+ "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-35",
3620
+ historyPending && "opacity-50"
3621
+ ),
3622
+ "aria-label": direction === "back" ? "Back" : "Forward",
3623
+ title: direction === "back" ? "Back" : "Forward",
3624
+ children: direction === "back" ? /* @__PURE__ */ jsx2(ArrowLeftIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsx2(ArrowRightIcon, { className: "size-3.5" })
3625
+ },
3626
+ direction
3627
+ )),
3628
+ /* @__PURE__ */ jsx2(
3629
+ "button",
3630
+ {
3631
+ type: "button",
3632
+ onClick: props.onReload,
3633
+ disabled: !props.target || props.loading || historyPending,
3634
+ className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-35",
2953
3635
  "aria-label": "Reload",
2954
- children: /* @__PURE__ */ jsx(RefreshCwIcon, { className: cn("size-3.5", props.loading && "animate-spin") })
3636
+ children: /* @__PURE__ */ jsx2(
3637
+ RefreshCwIcon,
3638
+ {
3639
+ className: cn("size-3.5", (props.loading || historyPending) && "animate-spin")
3640
+ }
3641
+ )
2955
3642
  }
2956
3643
  ),
2957
- /* @__PURE__ */ jsxs("div", { className: "flex h-7 min-w-0 flex-1 items-center gap-2 rounded-og-sm border border-og-border bg-og-surface-0 px-2 focus-within:border-og-accent/60", children: [
2958
- /* @__PURE__ */ jsx(Globe2Icon, { className: "size-3 shrink-0 text-og-subtle" }),
2959
- /* @__PURE__ */ jsx(
3644
+ /* @__PURE__ */ jsxs2("div", { className: "flex h-7 min-w-0 flex-1 items-center gap-2 rounded-og-sm border border-og-border bg-og-surface-1 px-2 focus-within:border-og-accent/60", children: [
3645
+ /* @__PURE__ */ jsx2(Globe2Icon, { className: "size-3 shrink-0 text-og-fg-subtle" }),
3646
+ /* @__PURE__ */ jsx2(
2960
3647
  "input",
2961
3648
  {
2962
3649
  value: draft,
@@ -2970,11 +3657,12 @@ function BrowserAddressBar(props) {
2970
3657
  onKeyDown: (event) => {
2971
3658
  if (event.key !== "Enter") return;
2972
3659
  event.preventDefault();
3660
+ if (historyPendingRef.current) return;
2973
3661
  const normalized = normalizeBrowserAddress(draft);
2974
3662
  if (normalized) props.onNavigate(normalized);
2975
3663
  },
2976
3664
  disabled: !props.target,
2977
- className: "min-w-0 flex-1 bg-transparent text-og-control text-og-fg placeholder:text-og-subtle focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50",
3665
+ className: "min-w-0 flex-1 bg-transparent text-og-control text-og-fg placeholder:text-og-fg-subtle focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50",
2978
3666
  "aria-label": "Address",
2979
3667
  spellCheck: false,
2980
3668
  placeholder: "Search or enter address"
@@ -2986,24 +3674,48 @@ function BrowserAddressBar(props) {
2986
3674
  );
2987
3675
  }
2988
3676
  function BrowserViewport(props) {
2989
- const canvasRef = useRef8(null);
2990
- const inputRef = useRef8(null);
2991
- const composingRef = useRef8(false);
2992
- const pointerStartRef = useRef8(null);
2993
- const lastClickRef = useRef8(null);
2994
- const wheelRef = useRef8(null);
2995
- const pendingTextRef = useRef8(null);
2996
- const actionRef = useRef8(props.onAction);
2997
- const readClipboardRef = useRef8(props.onReadClipboard);
2998
- const errorRef = useRef8(props.onError);
2999
- const actionTailRef = useRef8(Promise.resolve());
3000
- const actionQueueEpochRef = useRef8(0);
3001
- const queuedFrameRef = useRef8(null);
3002
- const decodingFrameRef = useRef8(false);
3003
- const mountedRef = useRef8(true);
3677
+ const canvasRef = useRef9(null);
3678
+ const inputRef = useRef9(null);
3679
+ const [selectPopup, setSelectPopup] = useState9(null);
3680
+ const inputSequenceRef = useRef9(0);
3681
+ const dismissSelectPopup = useCallback8(() => {
3682
+ inputSequenceRef.current += 1;
3683
+ setSelectPopup({ observation: null, anchor: null });
3684
+ }, []);
3685
+ const composingRef = useRef9(false);
3686
+ const pointerStartRef = useRef9(null);
3687
+ const lastClickRef = useRef9(null);
3688
+ const wheelRef = useRef9(null);
3689
+ const pendingTextRef = useRef9(null);
3690
+ const actionRef = useRef9(props.onAction);
3691
+ const readClipboardRef = useRef9(props.onReadClipboard);
3692
+ const observeInputRef = useRef9(props.onObserveForInput);
3693
+ const errorRef = useRef9(props.onError);
3694
+ const actionTailRef = useRef9(Promise.resolve());
3695
+ const actionQueueEpochRef = useRef9(0);
3696
+ const queuedTypingRef = useRef9(null);
3697
+ const queuedFrameRef = useRef9(null);
3698
+ const currentFrameRef = useRef9(props.frame);
3699
+ const paintedFrameRef = useRef9(null);
3700
+ const [paintedFrame, setPaintedFrame] = useState9(null);
3701
+ const decodingFrameRef = useRef9(false);
3702
+ const mountedRef = useRef9(true);
3004
3703
  actionRef.current = props.onAction;
3005
3704
  readClipboardRef.current = props.onReadClipboard;
3705
+ observeInputRef.current = props.onObserveForInput;
3006
3706
  errorRef.current = props.onError;
3707
+ const clearBufferedInput = useCallback8(() => {
3708
+ dismissSelectPopup();
3709
+ if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
3710
+ if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
3711
+ wheelRef.current = null;
3712
+ pendingTextRef.current = null;
3713
+ queuedTypingRef.current = null;
3714
+ pointerStartRef.current = null;
3715
+ lastClickRef.current = null;
3716
+ composingRef.current = false;
3717
+ if (inputRef.current) inputRef.current.value = "";
3718
+ }, [dismissSelectPopup]);
3007
3719
  const paintQueuedFrames = useCallback8(() => {
3008
3720
  if (decodingFrameRef.current) return;
3009
3721
  decodingFrameRef.current = true;
@@ -3015,15 +3727,17 @@ function BrowserViewport(props) {
3015
3727
  if (!frame) break;
3016
3728
  let objectUrl = null;
3017
3729
  try {
3018
- const blob = new Blob([frame.data.slice().buffer], {
3730
+ const blob = new Blob([frame.data], {
3019
3731
  type: frame.mediaType
3020
3732
  });
3021
3733
  if (typeof createImageBitmap === "function") {
3022
3734
  const bitmap = await createImageBitmap(blob);
3023
3735
  try {
3024
3736
  const canvas2 = canvasRef.current;
3025
- if (mountedRef.current && canvas2) {
3737
+ if (mountedRef.current && canvas2 && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current)) {
3026
3738
  paintCanvas(canvas2, bitmap, frame.width, frame.height);
3739
+ paintedFrameRef.current = frame;
3740
+ setPaintedFrame(frame);
3027
3741
  }
3028
3742
  } finally {
3029
3743
  bitmap.close();
@@ -3033,11 +3747,14 @@ function BrowserViewport(props) {
3033
3747
  objectUrl = URL.createObjectURL(blob);
3034
3748
  const image = await loadImage(objectUrl);
3035
3749
  const canvas = canvasRef.current;
3036
- if (mountedRef.current && canvas) {
3750
+ if (mountedRef.current && canvas && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current)) {
3037
3751
  paintCanvas(canvas, image, frame.width, frame.height);
3752
+ paintedFrameRef.current = frame;
3753
+ setPaintedFrame(frame);
3038
3754
  }
3039
3755
  } catch (cause) {
3040
- if (mountedRef.current) errorRef.current(cause);
3756
+ if (mountedRef.current && currentFrameRef.current && sameBrowserDocument(frame, currentFrameRef.current))
3757
+ errorRef.current(cause);
3041
3758
  } finally {
3042
3759
  if (objectUrl) URL.revokeObjectURL(objectUrl);
3043
3760
  }
@@ -3048,38 +3765,83 @@ function BrowserViewport(props) {
3048
3765
  }
3049
3766
  })();
3050
3767
  }, []);
3051
- useEffect8(() => {
3768
+ useLayoutEffect2(() => {
3769
+ const keyboardInput = inputRef.current;
3770
+ const focusHandoff = props.focusHandoffRef;
3052
3771
  mountedRef.current = true;
3772
+ if (focusHandoff.current === props.focusScope) {
3773
+ keyboardInput?.focus({ preventScroll: true });
3774
+ }
3775
+ focusHandoff.current = null;
3053
3776
  return () => {
3777
+ if (document.activeElement === keyboardInput) {
3778
+ focusHandoff.current = props.focusScope;
3779
+ }
3054
3780
  mountedRef.current = false;
3781
+ actionQueueEpochRef.current += 1;
3055
3782
  queuedFrameRef.current = null;
3783
+ clearBufferedInput();
3056
3784
  };
3057
- }, []);
3058
- useEffect8(() => {
3059
- if (!props.frame) return;
3785
+ }, [clearBufferedInput, props.focusHandoffRef, props.focusScope]);
3786
+ useLayoutEffect2(() => {
3787
+ if (currentFrameRef.current && !props.frame) {
3788
+ actionQueueEpochRef.current += 1;
3789
+ clearBufferedInput();
3790
+ }
3791
+ currentFrameRef.current = props.frame;
3792
+ if (!props.frame) {
3793
+ queuedFrameRef.current = null;
3794
+ paintedFrameRef.current = null;
3795
+ setPaintedFrame(null);
3796
+ return;
3797
+ }
3060
3798
  queuedFrameRef.current = props.frame;
3061
3799
  paintQueuedFrames();
3062
- }, [paintQueuedFrames, props.frame]);
3063
- useEffect8(
3064
- () => () => {
3065
- if (wheelRef.current?.timer) clearTimeout(wheelRef.current.timer);
3066
- if (pendingTextRef.current?.timer) clearTimeout(pendingTextRef.current.timer);
3067
- },
3068
- []
3069
- );
3800
+ }, [clearBufferedInput, paintQueuedFrames, props.frame]);
3070
3801
  const enqueue = useCallback8(
3071
- (action, frame, after) => {
3802
+ (action, frame, after, selectAnchor) => {
3803
+ const inputSequence = ++inputSequenceRef.current;
3804
+ setSelectPopup({ observation: null, anchor: null });
3805
+ frame = frame ? {
3806
+ browserSessionId: frame.browserSessionId,
3807
+ controllerGeneration: frame.controllerGeneration,
3808
+ targetId: frame.targetId,
3809
+ targetGeneration: frame.targetGeneration,
3810
+ documentGeneration: frame.documentGeneration,
3811
+ frameId: frame.frameId
3812
+ } : null;
3072
3813
  const epoch = actionQueueEpochRef.current;
3814
+ const attachment = props.inputBatchAttachment;
3815
+ const canBatchTyping = action.type === "type" && !after && frame && attachment?.fencedInputBatches === true && attachment.browserSessionId === frame.browserSessionId && attachment.controllerGeneration === frame.controllerGeneration && attachment.targetId === frame.targetId && Date.parse(attachment.expiresAt) > Date.now();
3816
+ const previous = queuedTypingRef.current;
3817
+ if (canBatchTyping && frame && previous && previous.epoch === epoch && sameFrameFence(previous.frame, frame) && previous.actions.length < 16) {
3818
+ previous.actions.push(action);
3819
+ return;
3820
+ }
3821
+ const typing = canBatchTyping && frame ? { actions: [action], frame, epoch } : null;
3822
+ queuedTypingRef.current = typing;
3073
3823
  actionTailRef.current = actionTailRef.current.catch(() => void 0).then(async () => {
3074
- if (epoch !== actionQueueEpochRef.current) return;
3075
- const receipt = await actionRef.current(action, frame);
3824
+ if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3825
+ if (queuedTypingRef.current === typing) queuedTypingRef.current = null;
3826
+ const dispatchedAction = typing && typing.actions.length > 1 ? { type: "batch", actions: typing.actions, fenceEachAction: true } : action;
3827
+ const receipt = await actionRef.current(dispatchedAction, frame);
3828
+ if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3829
+ const keyboardSelect = dispatchedAction.type === "press" && dispatchedAction.key === "Alt+ArrowDown";
3830
+ if ((selectAnchor || keyboardSelect) && inputSequence === inputSequenceRef.current) {
3831
+ const observation = keyboardSelect ? await observeInputRef.current() : receipt.observation;
3832
+ if (mountedRef.current && epoch === actionQueueEpochRef.current && inputSequence === inputSequenceRef.current && observation) {
3833
+ setSelectPopup({ observation, anchor: selectAnchor ?? null });
3834
+ }
3835
+ }
3076
3836
  await after?.(receipt);
3077
3837
  }).catch((cause) => {
3838
+ if (!mountedRef.current || epoch !== actionQueueEpochRef.current) return;
3078
3839
  actionQueueEpochRef.current += 1;
3840
+ clearBufferedInput();
3079
3841
  errorRef.current(cause);
3080
3842
  });
3081
3843
  },
3082
- []
3844
+ [clearBufferedInput, props.inputBatchAttachment]
3083
3845
  );
3084
3846
  const point = useCallback8(
3085
3847
  (frame, clientX, clientY) => browserPoint(canvasRef.current, frame, clientX, clientY),
@@ -3095,15 +3857,35 @@ function BrowserViewport(props) {
3095
3857
  const flushPendingClick = useCallback8(() => {
3096
3858
  lastClickRef.current = null;
3097
3859
  }, []);
3860
+ const flushPendingWheel = useCallback8(() => {
3861
+ const batch = wheelRef.current;
3862
+ if (!batch) return;
3863
+ if (batch.timer) clearTimeout(batch.timer);
3864
+ wheelRef.current = null;
3865
+ enqueue(
3866
+ {
3867
+ type: "pointer",
3868
+ action: "scroll",
3869
+ x: batch.x,
3870
+ y: batch.y,
3871
+ deltaX: batch.deltaX,
3872
+ deltaY: batch.deltaY
3873
+ },
3874
+ batch.frame
3875
+ );
3876
+ }, [enqueue]);
3098
3877
  const pointerDown = (event) => {
3099
- if (!props.frame || event.button !== 0) return;
3878
+ const frame = paintedFrameRef.current;
3879
+ if (!frame || event.button !== 0) return;
3100
3880
  event.preventDefault();
3881
+ dismissSelectPopup();
3882
+ flushPendingWheel();
3101
3883
  flushPendingText();
3102
3884
  pointerStartRef.current = {
3103
3885
  x: event.clientX,
3104
3886
  y: event.clientY,
3105
3887
  pointerId: event.pointerId,
3106
- frame: props.frame
3888
+ frame
3107
3889
  };
3108
3890
  event.currentTarget.setPointerCapture?.(event.pointerId);
3109
3891
  inputRef.current?.focus({ preventScroll: true });
@@ -3138,52 +3920,54 @@ function BrowserViewport(props) {
3138
3920
  return;
3139
3921
  }
3140
3922
  lastClickRef.current = { at: now, x: to.x, y: to.y, frame: start.frame };
3141
- enqueue({ type: "pointer", action: "click", x: to.x, y: to.y }, start.frame);
3923
+ const viewportBounds = canvasRef.current?.parentElement?.getBoundingClientRect();
3924
+ enqueue(
3925
+ { type: "pointer", action: "click", x: to.x, y: to.y },
3926
+ start.frame,
3927
+ void 0,
3928
+ viewportBounds ? { x: event.clientX - viewportBounds.left, y: event.clientY - viewportBounds.top + 8 } : void 0
3929
+ );
3142
3930
  };
3143
3931
  const contextMenu = (event) => {
3144
3932
  event.preventDefault();
3145
- const frame = props.frame;
3933
+ const frame = paintedFrameRef.current;
3146
3934
  if (!frame) return;
3935
+ flushPendingWheel();
3147
3936
  flushPendingText();
3148
3937
  flushPendingClick();
3149
3938
  const at = point(frame, event.clientX, event.clientY);
3150
3939
  if (at) enqueue({ type: "pointer", action: "click", x: at.x, y: at.y, button: "right" }, frame);
3151
3940
  };
3152
3941
  const wheel = (event) => {
3153
- const frame = props.frame;
3942
+ dismissSelectPopup();
3943
+ const frame = paintedFrameRef.current;
3154
3944
  if (!frame) return;
3155
3945
  const at = point(frame, event.clientX, event.clientY);
3156
3946
  if (!at) return;
3157
3947
  flushPendingText();
3158
3948
  flushPendingClick();
3159
3949
  event.preventDefault();
3160
- const pending = wheelRef.current;
3161
- if (pending?.timer) clearTimeout(pending.timer);
3950
+ let pending = wheelRef.current;
3951
+ if (pending && (!sameBrowserDocument(pending.frame, frame) || Math.hypot(pending.x - at.x, pending.y - at.y) > 6)) {
3952
+ flushPendingWheel();
3953
+ pending = null;
3954
+ }
3162
3955
  wheelRef.current = {
3163
3956
  x: at.x,
3164
3957
  y: at.y,
3165
- deltaX: (pending && sameFrameFence(pending.frame, frame) ? pending.deltaX : 0) + event.deltaX,
3166
- deltaY: (pending && sameFrameFence(pending.frame, frame) ? pending.deltaY : 0) + event.deltaY,
3958
+ deltaX: (pending?.deltaX ?? 0) + event.deltaX,
3959
+ deltaY: (pending?.deltaY ?? 0) + event.deltaY,
3167
3960
  frame,
3168
- timer: setTimeout(() => {
3169
- const batch = wheelRef.current;
3170
- wheelRef.current = null;
3171
- if (batch)
3172
- enqueue(
3173
- {
3174
- type: "pointer",
3175
- action: "scroll",
3176
- x: batch.x,
3177
- y: batch.y,
3178
- deltaX: batch.deltaX,
3179
- deltaY: batch.deltaY
3180
- },
3181
- batch.frame
3182
- );
3183
- }, 45)
3961
+ // Retain the first event's deadline so a continuous gesture keeps moving.
3962
+ timer: pending?.timer ?? setTimeout(flushPendingWheel, 45)
3184
3963
  };
3185
3964
  };
3186
3965
  const keyDown = (event) => {
3966
+ if (composingRef.current || event.nativeEvent.isComposing || event.nativeEvent.keyCode === 229) {
3967
+ return;
3968
+ }
3969
+ dismissSelectPopup();
3970
+ flushPendingWheel();
3187
3971
  const command = event.metaKey || event.ctrlKey;
3188
3972
  if (props.clipboardEnabled && command && !event.altKey && ["c", "v"].includes(event.key.toLowerCase())) {
3189
3973
  flushPendingText();
@@ -3194,24 +3978,28 @@ function BrowserViewport(props) {
3194
3978
  event.preventDefault();
3195
3979
  flushPendingText();
3196
3980
  flushPendingClick();
3197
- enqueue({ type: "press", key }, props.frame);
3981
+ enqueue({ type: "press", key }, paintedFrameRef.current);
3198
3982
  };
3983
+ const finishCopy = useCallback8(async () => {
3984
+ const clipboard = await readClipboardRef.current();
3985
+ if (!mountedRef.current) return;
3986
+ if (clipboard.text.length === 0) return;
3987
+ if (!await copyTextToClipboard(clipboard.text)) {
3988
+ throw new Error("Browser text could not be copied to the local clipboard");
3989
+ }
3990
+ }, []);
3199
3991
  const copy = (event) => {
3200
3992
  if (!props.clipboardEnabled) return;
3201
3993
  event.preventDefault();
3994
+ flushPendingWheel();
3202
3995
  flushPendingText();
3203
3996
  flushPendingClick();
3204
- enqueue({ type: "clipboard", operation: "copy" }, props.frame, async () => {
3205
- const clipboard = await readClipboardRef.current();
3206
- if (clipboard.text.length === 0) return;
3207
- if (!await copyTextToClipboard(clipboard.text)) {
3208
- throw new Error("Browser text could not be copied to the local clipboard");
3209
- }
3210
- });
3997
+ enqueue({ type: "clipboard", operation: "copy" }, paintedFrameRef.current, finishCopy);
3211
3998
  };
3212
3999
  const paste = (event) => {
3213
4000
  if (!props.clipboardEnabled) return;
3214
4001
  event.preventDefault();
4002
+ flushPendingWheel();
3215
4003
  flushPendingText();
3216
4004
  flushPendingClick();
3217
4005
  enqueue(
@@ -3220,15 +4008,17 @@ function BrowserViewport(props) {
3220
4008
  operation: "paste",
3221
4009
  text: event.clipboardData.getData("text/plain")
3222
4010
  },
3223
- props.frame
4011
+ paintedFrameRef.current
3224
4012
  );
3225
4013
  };
3226
4014
  const input = (value, nativeComposing = false) => {
4015
+ dismissSelectPopup();
3227
4016
  if (composingRef.current || nativeComposing) return;
3228
4017
  if (!value) return;
4018
+ flushPendingWheel();
3229
4019
  flushPendingClick();
3230
4020
  const pending = pendingTextRef.current;
3231
- if (pending && sameOptionalBrowserFrame(pending.frame, props.frame)) {
4021
+ if (pending && sameOptionalBrowserFrame(pending.frame, paintedFrameRef.current)) {
3232
4022
  clearTimeout(pending.timer);
3233
4023
  pending.text += value;
3234
4024
  pending.timer = setTimeout(flushPendingText, 16);
@@ -3236,7 +4026,7 @@ function BrowserViewport(props) {
3236
4026
  flushPendingText();
3237
4027
  pendingTextRef.current = {
3238
4028
  text: value,
3239
- frame: props.frame,
4029
+ frame: paintedFrameRef.current,
3240
4030
  timer: setTimeout(flushPendingText, 16)
3241
4031
  };
3242
4032
  }
@@ -3249,9 +4039,9 @@ function BrowserViewport(props) {
3249
4039
  composingRef.current = false;
3250
4040
  input(event.currentTarget.value || event.data);
3251
4041
  };
3252
- const showCanvas = props.frame !== null;
3253
- return /* @__PURE__ */ jsxs("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
3254
- /* @__PURE__ */ jsx(
4042
+ const showCanvas = !props.controlUnavailable && props.frame !== null && paintedFrame !== null && sameBrowserDocument(props.frame, paintedFrame);
4043
+ return /* @__PURE__ */ jsxs2("div", { className: "relative min-h-0 flex-1 overflow-hidden bg-black", children: [
4044
+ /* @__PURE__ */ jsx2(
3255
4045
  "canvas",
3256
4046
  {
3257
4047
  ref: canvasRef,
@@ -3269,10 +4059,11 @@ function BrowserViewport(props) {
3269
4059
  "aria-label": "Interactive browser page"
3270
4060
  }
3271
4061
  ),
3272
- /* @__PURE__ */ jsx(
4062
+ /* @__PURE__ */ jsx2(
3273
4063
  "textarea",
3274
4064
  {
3275
4065
  ref: inputRef,
4066
+ disabled: props.controlUnavailable,
3276
4067
  defaultValue: "",
3277
4068
  onInput: (event) => input(event.currentTarget.value, event.nativeEvent.isComposing),
3278
4069
  onCompositionStart: compositionStart,
@@ -3282,26 +4073,44 @@ function BrowserViewport(props) {
3282
4073
  onPaste: paste,
3283
4074
  className: "pointer-events-none absolute left-1/2 top-1/2 size-px resize-none overflow-hidden opacity-0",
3284
4075
  "aria-label": "Browser keyboard input",
4076
+ "aria-keyshortcuts": "Alt+ArrowDown",
3285
4077
  autoCapitalize: "off",
3286
4078
  autoCorrect: "off",
3287
4079
  spellCheck: false
3288
4080
  }
3289
4081
  ),
3290
- !showCanvas ? /* @__PURE__ */ jsx(
4082
+ !showCanvas ? /* @__PURE__ */ jsx2(
3291
4083
  SemanticBrowserFallback,
3292
4084
  {
3293
4085
  observation: props.observation,
3294
4086
  connectionState: props.connectionState,
3295
4087
  supportsLiveFrames: props.supportsLiveFrames,
3296
4088
  error: props.connectionError,
4089
+ controlUnavailable: props.controlUnavailable,
3297
4090
  onAction: (action) => enqueue(action, null),
3298
4091
  onReconnect: props.onReconnect,
3299
4092
  reconnectLabel: props.reconnectLabel,
3300
4093
  reconnectMessage: props.reconnectMessage
3301
4094
  }
3302
4095
  ) : null,
3303
- props.mutating ? /* @__PURE__ */ jsxs("div", { className: "pointer-events-none absolute right-3 top-3 flex items-center gap-1.5 rounded-full border border-white/10 bg-black/65 px-2.5 py-1 text-[11px] text-white/80 backdrop-blur", children: [
3304
- /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }),
4096
+ showCanvas ? /* @__PURE__ */ jsx2(
4097
+ BrowserSelectControl,
4098
+ {
4099
+ manualFallback: props.inputBatchAttachment?.focusedInputObservations !== true,
4100
+ activation: selectPopup,
4101
+ onDismiss: () => inputRef.current?.focus({ preventScroll: true }),
4102
+ observe: async () => {
4103
+ flushPendingText();
4104
+ flushPendingWheel();
4105
+ await actionTailRef.current;
4106
+ if (!mountedRef.current) throw new Error("The browser page changed.");
4107
+ return await props.onObserveForInput();
4108
+ },
4109
+ act: props.onSelectFromObservation
4110
+ }
4111
+ ) : null,
4112
+ props.mutating ? /* @__PURE__ */ jsxs2("div", { className: "pointer-events-none absolute right-3 top-3 flex items-center gap-1.5 rounded-full border border-white/10 bg-black/65 px-2.5 py-1 text-[11px] text-white/80 backdrop-blur", children: [
4113
+ /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }),
3305
4114
  " ",
3306
4115
  props.activityLabel ?? "Acting"
3307
4116
  ] }) : null
@@ -3309,20 +4118,21 @@ function BrowserViewport(props) {
3309
4118
  }
3310
4119
  function SemanticBrowserFallback(props) {
3311
4120
  const controlFailure = interactionControlFailureFromError(props.error);
3312
- const generationLoss = Boolean(props.reconnectMessage) || isAttachedChromeGenerationLossError(props.error);
4121
+ const { controlUnavailable } = props;
4122
+ const generationLoss = Boolean(props.reconnectMessage);
3313
4123
  const nodes = semanticNodes(
3314
4124
  props.observation?.semantic?.kind === "snapshot" ? props.observation.semantic.roots : []
3315
4125
  );
3316
4126
  const interactive = nodes.filter((node) => node.actions.includes("click")).slice(0, 8);
3317
- 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: [
3318
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3319
- props.error ? /* @__PURE__ */ jsx(CircleAlertIcon, { className: "size-4 text-og-status-error" }) : !props.supportsLiveFrames ? /* @__PURE__ */ jsx(ZapIcon, { className: "size-4 text-og-muted" }) : /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-4 animate-spin text-og-muted" }),
3320
- /* @__PURE__ */ jsx("p", { className: "text-og-menu font-medium text-og-fg", children: generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : props.error ? "Live view disconnected" : props.supportsLiveFrames ? browserConnectionLabel(props.connectionState) : "Semantic browser" })
4127
+ return /* @__PURE__ */ jsx2("div", { className: "absolute inset-0 grid place-items-center bg-og-bg p-6", children: /* @__PURE__ */ jsxs2("div", { className: "w-full max-w-md rounded-og-lg border border-og-border bg-og-surface-1 p-4 shadow-lg", children: [
4128
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
4129
+ props.error ? /* @__PURE__ */ jsx2(CircleAlertIcon, { className: "size-4 text-og-danger" }) : props.connectionState === "semantic" ? /* @__PURE__ */ jsx2(ZapIcon, { className: "size-4 text-og-fg-muted" }) : /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-4 animate-spin text-og-fg-muted" }),
4130
+ /* @__PURE__ */ jsx2("p", { className: "text-og-menu font-medium text-og-fg", children: generationLoss ? "Chrome reconnected\u2014open a fresh browser/desktop." : controlUnavailable ? "Browser controls unavailable" : props.error ? props.supportsLiveFrames ? "Live view disconnected" : "Browser unavailable" : props.connectionState === "semantic" ? "Semantic browser" : browserConnectionLabel(props.connectionState) })
3321
4131
  ] }),
3322
- props.error || props.reconnectMessage ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-og-control leading-5 text-og-muted", children: props.reconnectMessage ?? controlFailure?.message ?? props.error?.message }) : null,
3323
- interactive.length > 0 ? /* @__PURE__ */ jsxs("div", { className: "mt-3 border-t border-og-border pt-3", children: [
3324
- /* @__PURE__ */ jsx("p", { className: "mb-2 text-og-xs text-og-subtle", children: props.supportsLiveFrames ? "Page controls remain available" : "Available page controls" }),
3325
- /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: interactive.map((node) => /* @__PURE__ */ jsx(
4132
+ props.error || props.reconnectMessage ? /* @__PURE__ */ jsx2("p", { className: "mt-2 text-og-control leading-5 text-og-fg-muted", children: props.reconnectMessage ?? controlFailure?.message ?? props.error?.message }) : null,
4133
+ interactive.length > 0 && !controlUnavailable ? /* @__PURE__ */ jsxs2("div", { className: "mt-3 border-t border-og-border pt-3", children: [
4134
+ /* @__PURE__ */ jsx2("p", { className: "mb-2 text-og-xs text-og-fg-subtle", children: props.supportsLiveFrames ? "Page controls remain available" : "Available page controls" }),
4135
+ /* @__PURE__ */ jsx2("div", { className: "flex flex-wrap gap-1.5", children: interactive.map((node) => /* @__PURE__ */ jsx2(
3326
4136
  "button",
3327
4137
  {
3328
4138
  type: "button",
@@ -3336,12 +4146,12 @@ function SemanticBrowserFallback(props) {
3336
4146
  node.ref
3337
4147
  )) })
3338
4148
  ] }) : null,
3339
- props.error && props.supportsLiveFrames && (!generationLoss || props.reconnectLabel) ? /* @__PURE__ */ jsx(
4149
+ props.error && (!generationLoss || props.reconnectLabel) ? /* @__PURE__ */ jsx2(
3340
4150
  "button",
3341
4151
  {
3342
4152
  type: "button",
3343
4153
  onClick: props.onReconnect,
3344
- className: "mt-3 text-og-control font-medium text-og-accent hover:underline",
4154
+ className: "mt-3 text-og-control font-medium og-link",
3345
4155
  children: props.reconnectLabel ?? (controlFailure?.retryable === false ? "Try again" : "Reconnect")
3346
4156
  }
3347
4157
  ) : null
@@ -3350,8 +4160,8 @@ function SemanticBrowserFallback(props) {
3350
4160
  function BrowserStatusBar(props) {
3351
4161
  const errors = (props.observation?.diagnostics.consoleErrorCount ?? 0) + (props.observation?.diagnostics.pageErrorCount ?? 0);
3352
4162
  const failed = props.observation?.diagnostics.failedRequestCount ?? 0;
3353
- return /* @__PURE__ */ jsxs("div", { className: "flex h-7 shrink-0 items-center gap-2 border-t border-og-border bg-og-surface-0 px-2 text-og-xs text-og-subtle", children: [
3354
- /* @__PURE__ */ jsx(
4163
+ return /* @__PURE__ */ jsxs2("div", { className: "flex h-7 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: [
4164
+ /* @__PURE__ */ jsx2(
3355
4165
  "span",
3356
4166
  {
3357
4167
  className: cn(
@@ -3360,11 +4170,12 @@ function BrowserStatusBar(props) {
3360
4170
  )
3361
4171
  }
3362
4172
  ),
3363
- /* @__PURE__ */ jsx("span", { children: props.connectionState === "live" ? "Live" : props.connectionState === "semantic" ? "Semantic" : browserConnectionLabel(props.connectionState) }),
3364
- /* @__PURE__ */ jsx("span", { children: props.profile?.name ?? "Temporary browser" }),
3365
- /* @__PURE__ */ jsx("span", { className: "min-w-0 flex-1 truncate", children: props.target?.title }),
3366
- props.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }) : null,
3367
- props.onOpenComputer ? /* @__PURE__ */ jsxs(
4173
+ /* @__PURE__ */ jsx2("span", { children: props.connectionState === "live" ? "Live" : props.connectionState === "semantic" ? "Semantic" : browserConnectionLabel(props.connectionState) }),
4174
+ props.inputFailure ? /* @__PURE__ */ jsx2("span", { className: "text-og-status-failed", children: props.inputFailure.state === "failed" ? "Input failed" : "Input result unknown" }) : null,
4175
+ /* @__PURE__ */ jsx2("span", { children: props.profile?.name ?? "Temporary browser" }),
4176
+ /* @__PURE__ */ jsx2("span", { className: "min-w-0 flex-1 truncate", children: props.target?.title }),
4177
+ props.refreshing ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : null,
4178
+ props.onOpenComputer ? /* @__PURE__ */ jsxs2(
3368
4179
  "button",
3369
4180
  {
3370
4181
  type: "button",
@@ -3372,12 +4183,12 @@ function BrowserStatusBar(props) {
3372
4183
  className: "flex items-center gap-1 rounded px-1.5 py-0.5 transition hover:bg-og-surface-2 hover:text-og-fg",
3373
4184
  "aria-label": "Open this browser window in Desktop",
3374
4185
  children: [
3375
- /* @__PURE__ */ jsx(MonitorIcon, { className: "size-3" }),
4186
+ /* @__PURE__ */ jsx2(MonitorIcon, { className: "size-3" }),
3376
4187
  "Desktop"
3377
4188
  ]
3378
4189
  }
3379
4190
  ) : null,
3380
- /* @__PURE__ */ jsxs(
4191
+ /* @__PURE__ */ jsxs2(
3381
4192
  "button",
3382
4193
  {
3383
4194
  type: "button",
@@ -3389,7 +4200,7 @@ function BrowserStatusBar(props) {
3389
4200
  props.diagnosticsOpen && "bg-og-surface-2 text-og-fg"
3390
4201
  ),
3391
4202
  children: [
3392
- /* @__PURE__ */ jsx(BugIcon, { className: "size-3" }),
4203
+ /* @__PURE__ */ jsx2(BugIcon, { className: "size-3" }),
3393
4204
  errors + failed > 0 ? errors + failed : "Debug"
3394
4205
  ]
3395
4206
  }
@@ -3400,101 +4211,102 @@ function BrowserDiagnosticsDrawer(props) {
3400
4211
  const session = props.session;
3401
4212
  const diagnostics = props.observation?.diagnostics;
3402
4213
  const semanticMode = props.observation?.semantic ? "Semantic page structure available" : props.observation?.screenshot ? "Visual fallback in use" : "Waiting for page observation";
3403
- return /* @__PURE__ */ jsxs(
4214
+ return /* @__PURE__ */ jsxs2(
3404
4215
  "aside",
3405
4216
  {
3406
4217
  id: "browser-diagnostics-drawer",
3407
4218
  role: "region",
3408
4219
  "aria-label": "Browser diagnostics",
3409
- className: "absolute bottom-7 right-0 top-10 z-40 flex w-full max-w-md flex-col border-l border-og-border bg-og-surface-0 shadow-2xl",
4220
+ className: "absolute inset-y-0 right-0 z-40 flex min-h-0 w-full max-w-md flex-col border-l border-og-border bg-og-surface-1 shadow-2xl",
3410
4221
  children: [
3411
- /* @__PURE__ */ jsxs("div", { className: "flex h-11 shrink-0 items-center gap-2 border-b border-og-border px-3", children: [
3412
- /* @__PURE__ */ jsx(BugIcon, { className: "size-4 text-og-muted" }),
3413
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3414
- /* @__PURE__ */ jsx("p", { className: "text-og-control font-semibold text-og-fg", children: "Browser diagnostics" }),
3415
- /* @__PURE__ */ jsx("p", { className: "truncate text-og-xs text-og-subtle", children: props.target?.title || shortUrl(props.target?.url ?? "") || "Current tab" })
4222
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-h-11 shrink-0 items-center gap-2 border-b border-og-border px-3 py-2", children: [
4223
+ /* @__PURE__ */ jsx2(BugIcon, { className: "size-4 shrink-0 text-og-fg-muted" }),
4224
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
4225
+ /* @__PURE__ */ jsx2("p", { className: "text-og-control font-semibold text-og-fg", children: "Browser diagnostics" }),
4226
+ /* @__PURE__ */ jsx2("p", { className: "truncate text-og-xs text-og-fg-subtle", children: props.target?.title || shortUrl(props.target?.url ?? "") || "Current tab" })
3416
4227
  ] }),
3417
- /* @__PURE__ */ jsx(
4228
+ /* @__PURE__ */ jsx2(
3418
4229
  "button",
3419
4230
  {
3420
4231
  type: "button",
3421
4232
  onClick: props.onRefresh,
3422
4233
  disabled: props.state.loading,
3423
- 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",
4234
+ className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg disabled:opacity-40",
3424
4235
  "aria-label": "Refresh browser diagnostics",
3425
- children: /* @__PURE__ */ jsx(RefreshCwIcon, { className: cn("size-3.5", props.state.loading && "animate-spin") })
4236
+ children: /* @__PURE__ */ jsx2(RefreshCwIcon, { className: cn("size-3.5", props.state.loading && "animate-spin") })
3426
4237
  }
3427
4238
  ),
3428
- /* @__PURE__ */ jsx(
4239
+ /* @__PURE__ */ jsx2(
3429
4240
  "button",
3430
4241
  {
3431
4242
  type: "button",
3432
4243
  onClick: props.onClose,
3433
- className: "grid size-7 place-items-center rounded-og-sm text-og-muted transition hover:bg-og-surface-2 hover:text-og-fg",
4244
+ className: "grid size-7 shrink-0 place-items-center rounded-og-sm text-og-fg-muted transition hover:bg-og-surface-2 hover:text-og-fg",
3434
4245
  "aria-label": "Close browser diagnostics",
3435
- children: /* @__PURE__ */ jsx(XIcon, { className: "size-3.5" })
4246
+ children: /* @__PURE__ */ jsx2(XIcon, { className: "size-3.5" })
3436
4247
  }
3437
4248
  )
3438
4249
  ] }),
3439
- /* @__PURE__ */ jsxs("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
3440
- /* @__PURE__ */ jsxs("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-runtime-title", children: [
3441
- /* @__PURE__ */ jsx(
4250
+ /* @__PURE__ */ jsxs2("div", { className: "min-h-0 flex-1 overflow-y-auto", children: [
4251
+ /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-runtime-title", children: [
4252
+ /* @__PURE__ */ jsx2(
3442
4253
  "h3",
3443
4254
  {
3444
4255
  id: "browser-runtime-title",
3445
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-subtle",
4256
+ className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
3446
4257
  children: "Runtime"
3447
4258
  }
3448
4259
  ),
3449
- /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-[7rem_minmax(0,1fr)] gap-x-3 gap-y-1.5 text-og-xs", children: [
3450
- /* @__PURE__ */ jsx(DiagnosticFact, { label: "Engine", value: browserEngineLabel(session) }),
3451
- /* @__PURE__ */ jsx(DiagnosticFact, { label: "Driver", value: session?.driverId ?? "Unavailable" }),
3452
- /* @__PURE__ */ jsx(DiagnosticFact, { label: "Placement", value: placementLabel(session ?? void 0) }),
3453
- /* @__PURE__ */ jsx(
4260
+ /* @__PURE__ */ jsxs2("dl", { className: "mt-2 grid grid-cols-[7rem_minmax(0,1fr)] gap-x-3 gap-y-1.5 text-og-xs", children: [
4261
+ /* @__PURE__ */ jsx2(DiagnosticFact, { label: "Engine", value: browserEngineLabel(session) }),
4262
+ /* @__PURE__ */ jsx2(DiagnosticFact, { label: "Driver", value: session?.driverId ?? "Unavailable" }),
4263
+ /* @__PURE__ */ jsx2(DiagnosticFact, { label: "Placement", value: placementLabel(session ?? void 0) }),
4264
+ /* @__PURE__ */ jsx2(
3454
4265
  DiagnosticFact,
3455
4266
  {
3456
4267
  label: "Profile",
3457
4268
  value: props.profile ? `${props.profile.name}${props.baseRevisionOrdinal ? ` \xB7 v${props.baseRevisionOrdinal}` : ""}` : "Temporary"
3458
4269
  }
3459
4270
  ),
3460
- /* @__PURE__ */ jsx(
4271
+ /* @__PURE__ */ jsx2(
3461
4272
  DiagnosticFact,
3462
4273
  {
3463
4274
  label: "Controller",
3464
4275
  value: shortGeneration(session?.controller?.controllerGeneration)
3465
4276
  }
3466
4277
  ),
3467
- /* @__PURE__ */ jsx(
4278
+ /* @__PURE__ */ jsx2(
3468
4279
  DiagnosticFact,
3469
4280
  {
3470
4281
  label: "Network",
3471
4282
  value: session?.networkRouteId ? "Custom route" : "Placement default"
3472
4283
  }
3473
4284
  ),
3474
- /* @__PURE__ */ jsx(DiagnosticFact, { label: "Observation", value: semanticMode })
4285
+ /* @__PURE__ */ jsx2(DiagnosticFact, { label: "Observation", value: semanticMode })
3475
4286
  ] })
3476
4287
  ] }),
3477
- /* @__PURE__ */ jsxs("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-page-title", children: [
3478
- /* @__PURE__ */ jsx(
4288
+ /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-page-title", children: [
4289
+ /* @__PURE__ */ jsx2(
3479
4290
  "h3",
3480
4291
  {
3481
4292
  id: "browser-page-title",
3482
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-subtle",
3483
- children: "Current page"
4293
+ className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
4294
+ children: "Tab diagnostics"
3484
4295
  }
3485
4296
  ),
3486
- /* @__PURE__ */ jsxs("dl", { className: "mt-2 grid grid-cols-2 gap-2", children: [
3487
- /* @__PURE__ */ jsx(DiagnosticCount, { label: "Console errors", value: diagnostics?.consoleErrorCount ?? 0 }),
3488
- /* @__PURE__ */ jsx(DiagnosticCount, { label: "Page errors", value: diagnostics?.pageErrorCount ?? 0 }),
3489
- /* @__PURE__ */ jsx(DiagnosticCount, { label: "Failed requests", value: diagnostics?.failedRequestCount ?? 0 }),
3490
- /* @__PURE__ */ jsx(DiagnosticCount, { label: "Downloads", value: diagnostics?.downloadCount ?? 0 })
4297
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 text-og-xs text-og-fg-subtle", children: "Includes earlier pages in this tab." }),
4298
+ /* @__PURE__ */ jsxs2("dl", { className: "mt-2 grid grid-cols-2 gap-2", children: [
4299
+ /* @__PURE__ */ jsx2(DiagnosticCount, { label: "Console errors", value: diagnostics?.consoleErrorCount }),
4300
+ /* @__PURE__ */ jsx2(DiagnosticCount, { label: "Page errors", value: diagnostics?.pageErrorCount }),
4301
+ /* @__PURE__ */ jsx2(DiagnosticCount, { label: "Failed requests", value: diagnostics?.failedRequestCount }),
4302
+ /* @__PURE__ */ jsx2(DiagnosticCount, { label: "Downloads", value: diagnostics?.downloadCount })
3491
4303
  ] }),
3492
- session?.failureCode ? /* @__PURE__ */ jsxs("p", { className: "mt-2 rounded-og-sm border border-og-status-error/30 bg-og-status-error/5 px-2.5 py-2 text-og-xs text-og-status-error", children: [
4304
+ session?.failureCode ? /* @__PURE__ */ jsxs2("p", { className: "mt-2 rounded-og-sm border border-og-status-error/30 bg-og-status-error/5 px-2.5 py-2 text-og-xs text-og-danger", children: [
3493
4305
  "Browser state: ",
3494
4306
  session.failureCode
3495
4307
  ] }) : null
3496
4308
  ] }),
3497
- props.downloadsEnabled ? /* @__PURE__ */ jsx(
4309
+ props.downloadsEnabled ? /* @__PURE__ */ jsx2(
3498
4310
  BrowserDownloadsPanel,
3499
4311
  {
3500
4312
  downloads: props.downloads,
@@ -3506,39 +4318,39 @@ function BrowserDiagnosticsDrawer(props) {
3506
4318
  onRefresh: props.onRefreshDownloads
3507
4319
  }
3508
4320
  ) : null,
3509
- /* @__PURE__ */ jsxs("section", { className: "p-3", "aria-labelledby": "browser-events-title", children: [
3510
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-2", children: [
3511
- /* @__PURE__ */ jsx(
4321
+ /* @__PURE__ */ jsxs2("section", { className: "p-3", "aria-labelledby": "browser-events-title", children: [
4322
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center justify-between gap-2", children: [
4323
+ /* @__PURE__ */ jsx2(
3512
4324
  "h3",
3513
4325
  {
3514
4326
  id: "browser-events-title",
3515
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-subtle",
4327
+ className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
3516
4328
  children: "Recent events"
3517
4329
  }
3518
4330
  ),
3519
- props.state.batch?.truncated ? /* @__PURE__ */ jsx("span", { className: "text-[10px] text-og-subtle", children: "Newest 100" }) : null
4331
+ props.state.batch?.truncated ? /* @__PURE__ */ jsx2("span", { className: "text-[10px] text-og-fg-subtle", children: "Newest 100" }) : null
3520
4332
  ] }),
3521
- props.state.loading && !props.state.batch ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-6 text-og-control text-og-muted", children: [
3522
- /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
4333
+ props.state.loading && !props.state.batch ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-6 text-og-control text-og-fg-muted", children: [
4334
+ /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
3523
4335
  " Loading diagnostics\u2026"
3524
- ] }) : props.state.error ? /* @__PURE__ */ jsxs("div", { className: "py-5", children: [
3525
- /* @__PURE__ */ jsx("p", { className: "text-og-control text-og-status-error", children: props.state.error.message }),
3526
- /* @__PURE__ */ jsx(
4336
+ ] }) : props.state.error ? /* @__PURE__ */ jsxs2("div", { className: "py-5", children: [
4337
+ /* @__PURE__ */ jsx2("p", { className: "text-og-control text-og-danger", children: props.state.error.message }),
4338
+ /* @__PURE__ */ jsx2(
3527
4339
  "button",
3528
4340
  {
3529
4341
  type: "button",
3530
4342
  onClick: props.onRefresh,
3531
- className: "mt-2 text-og-control font-medium text-og-accent hover:underline",
4343
+ className: "mt-2 text-og-control font-medium og-link",
3532
4344
  children: "Try again"
3533
4345
  }
3534
4346
  )
3535
- ] }) : props.state.batch?.entries.length ? /* @__PURE__ */ jsx("ol", { className: "mt-2 space-y-2", children: props.state.batch.entries.map((entry) => /* @__PURE__ */ jsxs(
4347
+ ] }) : props.state.batch?.entries.length ? /* @__PURE__ */ jsx2("ol", { className: "mt-2 space-y-2", children: props.state.batch.entries.map((entry) => /* @__PURE__ */ jsxs2(
3536
4348
  "li",
3537
4349
  {
3538
4350
  className: "rounded-og-sm border border-og-border bg-og-bg p-2.5",
3539
4351
  children: [
3540
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 text-[10px] text-og-subtle", children: [
3541
- /* @__PURE__ */ jsx(
4352
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 text-[10px] text-og-fg-subtle", children: [
4353
+ /* @__PURE__ */ jsx2(
3542
4354
  "span",
3543
4355
  {
3544
4356
  className: cn(
@@ -3548,14 +4360,14 @@ function BrowserDiagnosticsDrawer(props) {
3548
4360
  children: diagnosticKindLabel(entry.kind)
3549
4361
  }
3550
4362
  ),
3551
- /* @__PURE__ */ jsx("span", { className: "ml-auto", children: formatDiagnosticTime(entry.occurredAt) })
4363
+ /* @__PURE__ */ jsx2("span", { className: "ml-auto", children: formatDiagnosticTime(entry.occurredAt) })
3552
4364
  ] }),
3553
- /* @__PURE__ */ jsx("p", { className: "mt-1 whitespace-pre-wrap break-words text-og-xs leading-5 text-og-fg", children: entry.message }),
3554
- entry.url || entry.filename || entry.method || entry.status ? /* @__PURE__ */ jsx("p", { className: "mt-1 truncate font-og-mono text-[10px] text-og-subtle", children: [entry.method, entry.status, entry.filename, entry.url].filter((value) => value !== null).join(" \xB7 ") }) : null
4365
+ /* @__PURE__ */ jsx2("p", { className: "mt-1 whitespace-pre-wrap break-words text-og-xs leading-5 text-og-fg", children: entry.message }),
4366
+ entry.url || entry.filename || entry.method || entry.status ? /* @__PURE__ */ jsx2("p", { className: "mt-1 truncate font-og-mono text-[10px] text-og-fg-subtle", children: [entry.method, entry.status, entry.filename, entry.url].filter((value) => value !== null).join(" \xB7 ") }) : null
3555
4367
  ]
3556
4368
  },
3557
4369
  entry.sequence
3558
- )) }) : /* @__PURE__ */ jsx("p", { className: "py-6 text-og-control text-og-muted", children: "No diagnostics for this tab." })
4370
+ )) }) : /* @__PURE__ */ jsx2("p", { className: "py-6 text-og-control text-og-fg-muted", children: "No diagnostics for this tab." })
3559
4371
  ] })
3560
4372
  ] })
3561
4373
  ]
@@ -3563,10 +4375,10 @@ function BrowserDiagnosticsDrawer(props) {
3563
4375
  );
3564
4376
  }
3565
4377
  function BrowserDownloadsPanel(props) {
3566
- const [paths, setPaths] = useState8({});
3567
- const [overwrite, setOverwrite] = useState8({});
3568
- const [savedPaths, setSavedPaths] = useState8({});
3569
- const [errors, setErrors] = useState8({});
4378
+ const [paths, setPaths] = useState9({});
4379
+ const [overwrite, setOverwrite] = useState9({});
4380
+ const [savedPaths, setSavedPaths] = useState9({});
4381
+ const [errors, setErrors] = useState9({});
3570
4382
  const saving = useMemo2(() => new Set(props.savingDownloadIds), [props.savingDownloadIds]);
3571
4383
  const save = (download) => {
3572
4384
  const destinationPath = (paths[download.id] ?? defaultDownloadDestination(download)).trim();
@@ -3581,46 +4393,46 @@ function BrowserDownloadsPanel(props) {
3581
4393
  }));
3582
4394
  });
3583
4395
  };
3584
- return /* @__PURE__ */ jsxs("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-downloads-title", children: [
3585
- /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
3586
- /* @__PURE__ */ jsx(DownloadIcon, { className: "size-3.5 text-og-muted" }),
3587
- /* @__PURE__ */ jsx(
4396
+ return /* @__PURE__ */ jsxs2("section", { className: "border-b border-og-border p-3", "aria-labelledby": "browser-downloads-title", children: [
4397
+ /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
4398
+ /* @__PURE__ */ jsx2(DownloadIcon, { className: "size-3.5 text-og-fg-muted" }),
4399
+ /* @__PURE__ */ jsx2(
3588
4400
  "h3",
3589
4401
  {
3590
4402
  id: "browser-downloads-title",
3591
- className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-subtle",
4403
+ className: "text-[10px] font-semibold uppercase tracking-[0.14em] text-og-fg-subtle",
3592
4404
  children: "Downloads"
3593
4405
  }
3594
4406
  ),
3595
- props.refreshing ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "ml-auto size-3 animate-spin text-og-muted" }) : null
4407
+ props.refreshing ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "ml-auto size-3 animate-spin text-og-fg-muted" }) : null
3596
4408
  ] }),
3597
- props.loading ? /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-5 text-og-control text-og-muted", children: [
3598
- /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
4409
+ props.loading ? /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2 py-5 text-og-control text-og-fg-muted", children: [
4410
+ /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3.5 animate-spin" }),
3599
4411
  " Loading downloads\u2026"
3600
- ] }) : props.error ? /* @__PURE__ */ jsxs("div", { className: "py-4", children: [
3601
- /* @__PURE__ */ jsx("p", { className: "text-og-control text-og-status-error", children: props.error.message }),
3602
- /* @__PURE__ */ jsx(
4412
+ ] }) : props.error ? /* @__PURE__ */ jsxs2("div", { className: "py-4", children: [
4413
+ /* @__PURE__ */ jsx2("p", { className: "text-og-control text-og-danger", children: props.error.message }),
4414
+ /* @__PURE__ */ jsx2(
3603
4415
  "button",
3604
4416
  {
3605
4417
  type: "button",
3606
4418
  onClick: props.onRefresh,
3607
- className: "mt-2 text-og-control font-medium text-og-accent hover:underline",
4419
+ className: "mt-2 text-og-control font-medium og-link",
3608
4420
  children: "Try again"
3609
4421
  }
3610
4422
  )
3611
- ] }) : props.downloads.length === 0 ? /* @__PURE__ */ jsx("p", { className: "py-4 text-og-control text-og-muted", children: "No files downloaded yet." }) : /* @__PURE__ */ jsx("ol", { className: "mt-2 space-y-2", children: props.downloads.map((download) => {
4423
+ ] }) : props.downloads.length === 0 ? /* @__PURE__ */ jsx2("p", { className: "py-4 text-og-control text-og-fg-muted", children: "No files downloaded yet." }) : /* @__PURE__ */ jsx2("ol", { className: "mt-2 space-y-2", children: props.downloads.map((download) => {
3612
4424
  const destinationPath = paths[download.id] ?? defaultDownloadDestination(download);
3613
4425
  const isSaving = saving.has(download.id);
3614
4426
  const isSaved = savedPaths[download.id] === destinationPath;
3615
- return /* @__PURE__ */ jsxs(
4427
+ return /* @__PURE__ */ jsxs2(
3616
4428
  "li",
3617
4429
  {
3618
4430
  className: "rounded-og-sm border border-og-border bg-og-bg p-2.5",
3619
4431
  children: [
3620
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 items-start gap-2", children: [
3621
- /* @__PURE__ */ jsx("span", { className: "mt-0.5 grid size-6 shrink-0 place-items-center rounded bg-og-surface-2 text-og-muted", children: isSaved ? /* @__PURE__ */ jsx(FileCheck2Icon, { className: "size-3.5 text-og-status-success" }) : /* @__PURE__ */ jsx(DownloadIcon, { className: "size-3.5" }) }),
3622
- /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
3623
- /* @__PURE__ */ jsx(
4432
+ /* @__PURE__ */ jsxs2("div", { className: "flex min-w-0 items-start gap-2", children: [
4433
+ /* @__PURE__ */ jsx2("span", { className: "mt-0.5 grid size-6 shrink-0 place-items-center rounded bg-og-surface-2 text-og-fg-muted", children: isSaved ? /* @__PURE__ */ jsx2(FileCheck2Icon, { className: "size-3.5 text-og-status-idle" }) : /* @__PURE__ */ jsx2(DownloadIcon, { className: "size-3.5" }) }),
4434
+ /* @__PURE__ */ jsxs2("div", { className: "min-w-0 flex-1", children: [
4435
+ /* @__PURE__ */ jsx2(
3624
4436
  "p",
3625
4437
  {
3626
4438
  className: "truncate text-og-xs font-medium text-og-fg",
@@ -3628,12 +4440,12 @@ function BrowserDownloadsPanel(props) {
3628
4440
  children: download.filename
3629
4441
  }
3630
4442
  ),
3631
- /* @__PURE__ */ jsx("p", { className: "mt-0.5 text-[10px] text-og-subtle", children: downloadStatusLabel(download) })
4443
+ /* @__PURE__ */ jsx2("p", { className: "mt-0.5 text-[10px] text-og-fg-subtle", children: downloadStatusLabel(download) })
3632
4444
  ] })
3633
4445
  ] }),
3634
- download.status === "completed" ? /* @__PURE__ */ jsxs("div", { className: "mt-2", children: [
3635
- /* @__PURE__ */ jsxs("div", { className: "flex gap-1.5", children: [
3636
- /* @__PURE__ */ jsx(
4446
+ download.status === "completed" ? /* @__PURE__ */ jsxs2("div", { className: "mt-2", children: [
4447
+ /* @__PURE__ */ jsxs2("div", { className: "flex gap-1.5", children: [
4448
+ /* @__PURE__ */ jsx2(
3637
4449
  "input",
3638
4450
  {
3639
4451
  type: "text",
@@ -3648,10 +4460,10 @@ function BrowserDownloadsPanel(props) {
3648
4460
  },
3649
4461
  disabled: isSaving,
3650
4462
  "aria-label": `Workspace path for ${download.filename}`,
3651
- className: "h-7 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-surface-0 px-2 font-og-mono text-[10px] text-og-fg transition focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50"
4463
+ className: "h-7 min-w-0 flex-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-og-mono text-[10px] text-og-fg transition focus:border-og-accent focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:opacity-50"
3652
4464
  }
3653
4465
  ),
3654
- /* @__PURE__ */ jsxs(
4466
+ /* @__PURE__ */ jsxs2(
3655
4467
  "button",
3656
4468
  {
3657
4469
  type: "button",
@@ -3659,14 +4471,14 @@ function BrowserDownloadsPanel(props) {
3659
4471
  disabled: isSaving || !destinationPath.trim() || isSaved && !overwrite[download.id],
3660
4472
  className: "inline-flex h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 text-[10px] font-medium text-og-fg transition hover:bg-og-surface-2 disabled:opacity-40",
3661
4473
  children: [
3662
- isSaving ? /* @__PURE__ */ jsx(LoaderCircleIcon, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx(SaveIcon, { className: "size-3" }),
4474
+ isSaving ? /* @__PURE__ */ jsx2(LoaderCircleIcon, { className: "size-3 animate-spin" }) : /* @__PURE__ */ jsx2(SaveIcon, { className: "size-3" }),
3663
4475
  isSaving ? "Saving" : isSaved ? "Saved" : overwrite[download.id] ? "Replace" : "Save"
3664
4476
  ]
3665
4477
  }
3666
4478
  )
3667
4479
  ] }),
3668
- /* @__PURE__ */ jsxs("label", { className: "mt-1.5 flex w-fit items-center gap-1.5 text-[10px] text-og-subtle", children: [
3669
- /* @__PURE__ */ jsx(
4480
+ /* @__PURE__ */ jsxs2("label", { className: "mt-1.5 flex w-fit items-center gap-1.5 text-[10px] text-og-fg-subtle", children: [
4481
+ /* @__PURE__ */ jsx2(
3670
4482
  "input",
3671
4483
  {
3672
4484
  type: "checkbox",
@@ -3684,7 +4496,7 @@ function BrowserDownloadsPanel(props) {
3684
4496
  ),
3685
4497
  "Replace an existing workspace file"
3686
4498
  ] }),
3687
- errors[download.id] ? /* @__PURE__ */ jsx("p", { className: "mt-1.5 break-words text-[10px] text-og-status-error", children: errors[download.id] }) : null
4499
+ errors[download.id] ? /* @__PURE__ */ jsx2("p", { className: "mt-1.5 break-words text-[10px] text-og-danger", children: errors[download.id] }) : null
3688
4500
  ] }) : null
3689
4501
  ]
3690
4502
  },
@@ -3712,22 +4524,22 @@ function defaultDownloadDestination(download) {
3712
4524
  return portable;
3713
4525
  }
3714
4526
  function DiagnosticFact(props) {
3715
- return /* @__PURE__ */ jsxs(Fragment, { children: [
3716
- /* @__PURE__ */ jsx("dt", { className: "text-og-subtle", children: props.label }),
3717
- /* @__PURE__ */ jsx("dd", { className: "min-w-0 truncate text-og-fg", title: props.value, children: props.value })
4527
+ return /* @__PURE__ */ jsxs2(Fragment2, { children: [
4528
+ /* @__PURE__ */ jsx2("dt", { className: "text-og-fg-subtle", children: props.label }),
4529
+ /* @__PURE__ */ jsx2("dd", { className: "min-w-0 truncate text-og-fg", title: props.value, children: props.value })
3718
4530
  ] });
3719
4531
  }
3720
4532
  function DiagnosticCount(props) {
3721
- return /* @__PURE__ */ jsxs("div", { className: "rounded-og-sm border border-og-border bg-og-bg px-2.5 py-2", children: [
3722
- /* @__PURE__ */ jsx("dt", { className: "text-[10px] text-og-subtle", children: props.label }),
3723
- /* @__PURE__ */ jsx(
4533
+ return /* @__PURE__ */ jsxs2("div", { className: "rounded-og-sm border border-og-border bg-og-bg px-2.5 py-2", children: [
4534
+ /* @__PURE__ */ jsx2("dt", { className: "text-[10px] text-og-fg-subtle", children: props.label }),
4535
+ /* @__PURE__ */ jsx2(
3724
4536
  "dd",
3725
4537
  {
3726
4538
  className: cn(
3727
4539
  "mt-0.5 text-og-sm font-semibold",
3728
- props.value ? "text-og-fg" : "text-og-muted"
4540
+ props.value ? "text-og-fg" : "text-og-fg-muted"
3729
4541
  ),
3730
- children: props.value
4542
+ children: props.value ?? "Unavailable"
3731
4543
  }
3732
4544
  )
3733
4545
  ] });
@@ -3754,23 +4566,23 @@ function diagnosticKindLabel(kind) {
3754
4566
  }
3755
4567
  }
3756
4568
  function diagnosticTone(level) {
3757
- if (level === "error") return "text-og-status-error";
4569
+ if (level === "error") return "text-og-danger";
3758
4570
  if (level === "warning") return "text-og-status-waiting";
3759
- return "text-og-muted";
4571
+ return "text-og-fg-muted";
3760
4572
  }
3761
4573
  function formatDiagnosticTime(value) {
3762
4574
  const date = new Date(value);
3763
4575
  return Number.isNaN(date.getTime()) ? "" : date.toLocaleTimeString([], { hour: "2-digit", minute: "2-digit", second: "2-digit" });
3764
4576
  }
3765
4577
  function BrowserNotice(props) {
3766
- return /* @__PURE__ */ jsx(
4578
+ return /* @__PURE__ */ jsx2(
3767
4579
  "div",
3768
4580
  {
3769
4581
  className: cn(
3770
- "grid h-full place-items-center bg-og-bg text-og-control text-og-muted",
4582
+ "grid h-full place-items-center bg-og-bg text-og-control text-og-fg-muted",
3771
4583
  props.className
3772
4584
  ),
3773
- children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
4585
+ children: /* @__PURE__ */ jsxs2("div", { className: "flex items-center gap-2", children: [
3774
4586
  props.icon,
3775
4587
  props.text
3776
4588
  ] })
@@ -3819,7 +4631,10 @@ function frameMatchesSelectedTarget(frame, session, target) {
3819
4631
  );
3820
4632
  }
3821
4633
  function sameFrameFence(left, right) {
3822
- return left.browserSessionId === right.browserSessionId && left.controllerGeneration === right.controllerGeneration && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration && left.documentGeneration === right.documentGeneration && left.frameId === right.frameId;
4634
+ return sameBrowserDocument(left, right) && left.frameId === right.frameId;
4635
+ }
4636
+ function sameBrowserDocument(left, right) {
4637
+ return left.browserSessionId === right.browserSessionId && left.controllerGeneration === right.controllerGeneration && left.targetId === right.targetId && left.targetGeneration === right.targetGeneration && left.documentGeneration === right.documentGeneration;
3823
4638
  }
3824
4639
  function sameOptionalBrowserFrame(left, right) {
3825
4640
  return left === null || right === null ? left === right : sameFrameFence(left, right);
@@ -3895,13 +4710,14 @@ function shortUrl(value) {
3895
4710
  }
3896
4711
 
3897
4712
  export {
3898
- useBrowserFrameStream,
4713
+ useSiteAuthConnections,
3899
4714
  useAttachedBrowsers,
4715
+ useBrowserSessions,
3900
4716
  useBrowserIdentities,
3901
- useBrowserDownloads,
3902
4717
  useBrowserSession,
3903
- useBrowserSessions,
3904
- useSiteAuthConnections,
4718
+ useBrowserDownloads,
4719
+ useBrowserFrameStream,
4720
+ staleBrowserEndRetryDelayMs,
3905
4721
  BrowserViewer
3906
4722
  };
3907
- //# sourceMappingURL=chunk-ZPBTI3TE.js.map
4723
+ //# sourceMappingURL=chunk-KZC6QRNR.js.map