@opengeni/react 0.46.1 → 0.52.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (341) hide show
  1. package/README.md +158 -14
  2. package/dist/artifacts-document.d.ts +3 -0
  3. package/dist/artifacts-document.js +17 -0
  4. package/dist/artifacts-document.js.map +1 -0
  5. package/dist/artifacts-presentation.d.ts +3 -0
  6. package/dist/artifacts-presentation.js +17 -0
  7. package/dist/artifacts-presentation.js.map +1 -0
  8. package/dist/artifacts-spreadsheet.d.ts +3 -0
  9. package/dist/artifacts-spreadsheet.js +17 -0
  10. package/dist/artifacts-spreadsheet.js.map +1 -0
  11. package/dist/artifacts.d.ts +2 -0
  12. package/dist/artifacts.js +170 -0
  13. package/dist/artifacts.js.map +1 -0
  14. package/dist/chunk-6BBWWOOU.js +82 -0
  15. package/dist/chunk-6BBWWOOU.js.map +1 -0
  16. package/dist/{chunk-HFO4ERGQ.js → chunk-724BW4TW.js} +586 -35
  17. package/dist/chunk-724BW4TW.js.map +1 -0
  18. package/dist/chunk-7I2KB3OK.js +3895 -0
  19. package/dist/chunk-7I2KB3OK.js.map +1 -0
  20. package/dist/chunk-7WCLAEFV.js +2828 -0
  21. package/dist/chunk-7WCLAEFV.js.map +1 -0
  22. package/dist/{chunk-U3CSCMWA.js → chunk-FK6VG4LL.js} +9 -13
  23. package/dist/chunk-FK6VG4LL.js.map +1 -0
  24. package/dist/{chunk-SRFUT2ZU.js → chunk-FWS3KC3Z.js} +120 -64
  25. package/dist/chunk-FWS3KC3Z.js.map +1 -0
  26. package/dist/{chunk-EKZH6IDG.js → chunk-HDBG7XGO.js} +62 -1
  27. package/dist/chunk-HDBG7XGO.js.map +1 -0
  28. package/dist/chunk-HUIKIDM5.js +2148 -0
  29. package/dist/chunk-HUIKIDM5.js.map +1 -0
  30. package/dist/{chunk-LGVMUIRV.js → chunk-JAGDUCZQ.js} +1053 -390
  31. package/dist/chunk-JAGDUCZQ.js.map +1 -0
  32. package/dist/{chunk-LWR4MXSS.js → chunk-JR3QFY6B.js} +420 -35
  33. package/dist/chunk-JR3QFY6B.js.map +1 -0
  34. package/dist/chunk-JVYQRRC4.js +53 -0
  35. package/dist/chunk-JVYQRRC4.js.map +1 -0
  36. package/dist/chunk-KCGXPP5V.js +157 -0
  37. package/dist/chunk-KCGXPP5V.js.map +1 -0
  38. package/dist/{chunk-EB67J347.js → chunk-KZ73RL76.js} +111 -31
  39. package/dist/chunk-KZ73RL76.js.map +1 -0
  40. package/dist/chunk-PEI6RANI.js +3234 -0
  41. package/dist/chunk-PEI6RANI.js.map +1 -0
  42. package/dist/{chunk-3PB7MIT6.js → chunk-ROIRTM37.js} +220 -72
  43. package/dist/chunk-ROIRTM37.js.map +1 -0
  44. package/dist/chunk-VZTQ73XT.js +86 -0
  45. package/dist/chunk-VZTQ73XT.js.map +1 -0
  46. package/dist/{chunk-VCBFUSUZ.js → chunk-WSK7G5LE.js} +72 -4
  47. package/dist/chunk-WSK7G5LE.js.map +1 -0
  48. package/dist/{chunk-FGZUCXZF.js → chunk-WYA65Y3F.js} +16 -16
  49. package/dist/chunk-WYA65Y3F.js.map +1 -0
  50. package/dist/{chunk-6EDV6YFE.js → chunk-X4S56Y2H.js} +147 -35
  51. package/dist/chunk-X4S56Y2H.js.map +1 -0
  52. package/dist/client.d.ts +7 -1
  53. package/dist/clipboard.d.ts +1 -0
  54. package/dist/clipboard.js +7 -0
  55. package/dist/clipboard.js.map +1 -0
  56. package/dist/commands/index.d.ts +3 -3
  57. package/dist/commands/registry.d.ts +1 -1
  58. package/dist/commands/types.d.ts +1 -1
  59. package/dist/components/approval-surface.d.ts +2 -2
  60. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  61. package/dist/components/artifacts/document-editor.d.ts +172 -0
  62. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  63. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  64. package/dist/components/artifacts/editable-document.d.ts +24 -0
  65. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  66. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  67. package/dist/components/artifacts/index.d.ts +8 -0
  68. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  69. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  70. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  71. package/dist/components/browser-viewer.d.ts +29 -0
  72. package/dist/components/chat-composer.d.ts +13 -8
  73. package/dist/components/code-editor.d.ts +25 -3
  74. package/dist/components/command-palette.d.ts +2 -2
  75. package/dist/components/composer-transcription-control.d.ts +4 -2
  76. package/dist/components/composer.d.ts +29 -15
  77. package/dist/components/computer-viewer.d.ts +23 -0
  78. package/dist/components/copy-button.d.ts +2 -2
  79. package/dist/components/desktop-viewer.d.ts +25 -21
  80. package/dist/components/diff-view.d.ts +1 -1
  81. package/dist/components/enrollment-consent.d.ts +1 -1
  82. package/dist/components/enrollment-device-flow.d.ts +1 -1
  83. package/dist/components/file-browser.d.ts +2 -2
  84. package/dist/components/fleet-tile.d.ts +1 -1
  85. package/dist/components/generated-video-player.d.ts +10 -0
  86. package/dist/components/human-input-form.d.ts +1 -1
  87. package/dist/components/human-input-surface.d.ts +2 -2
  88. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  89. package/dist/components/machine-card.d.ts +2 -2
  90. package/dist/components/machine-dock-bar.d.ts +3 -3
  91. package/dist/components/machine-health-pill.d.ts +2 -2
  92. package/dist/components/machine-input-display.d.ts +1 -1
  93. package/dist/components/machine-metrics.d.ts +2 -2
  94. package/dist/components/machine-status-pill.d.ts +4 -4
  95. package/dist/components/machines/health.d.ts +1 -1
  96. package/dist/components/machines/machine-detail.d.ts +5 -3
  97. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  98. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  99. package/dist/components/machines/series.d.ts +2 -2
  100. package/dist/components/machines-dashboard.d.ts +5 -5
  101. package/dist/components/markdown.d.ts +1 -1
  102. package/dist/components/message-timeline.d.ts +22 -5
  103. package/dist/components/model-picker.d.ts +2 -2
  104. package/dist/components/model-policy-picker.d.ts +7 -7
  105. package/dist/components/pierre-diff.d.ts +1 -1
  106. package/dist/components/pierre-file.d.ts +1 -1
  107. package/dist/components/queue-draft-policy.d.ts +1 -1
  108. package/dist/components/queue-surface-implementation.d.ts +3 -3
  109. package/dist/components/queue-surface-state.d.ts +4 -4
  110. package/dist/components/queue-surface.d.ts +5 -5
  111. package/dist/components/sandbox-files.d.ts +5 -5
  112. package/dist/components/sandbox-terminal.d.ts +50 -4
  113. package/dist/components/sandbox-workspace.d.ts +30 -10
  114. package/dist/components/session-chrome.d.ts +4 -4
  115. package/dist/components/session-status.d.ts +2 -2
  116. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  117. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  118. package/dist/components/timeline-annotations.d.ts +13 -0
  119. package/dist/components/tooltip.d.ts +4 -4
  120. package/dist/components/user-message-body.d.ts +2 -2
  121. package/dist/components/workbench-changes.d.ts +2 -2
  122. package/dist/components/workspace-dock.d.ts +3 -1
  123. package/dist/composer.d.ts +11 -11
  124. package/dist/composer.js +7 -7
  125. package/dist/hooks/internal.d.ts +7 -1
  126. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  127. package/dist/hooks/use-available-models.d.ts +2 -2
  128. package/dist/hooks/use-billing-usage.d.ts +1 -1
  129. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  130. package/dist/hooks/use-browser-identities.d.ts +26 -0
  131. package/dist/hooks/use-browser-session.d.ts +28 -0
  132. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  133. package/dist/hooks/use-codex-accounts.d.ts +3 -3
  134. package/dist/hooks/use-composer.d.ts +36 -9
  135. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  136. package/dist/hooks/use-computer-session.d.ts +28 -0
  137. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  138. package/dist/hooks/use-desktop-stream.d.ts +1 -1
  139. package/dist/hooks/use-environments.d.ts +11 -5
  140. package/dist/hooks/use-file-attachments.d.ts +1 -1
  141. package/dist/hooks/use-goal.d.ts +2 -2
  142. package/dist/hooks/use-human-input.d.ts +2 -2
  143. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  144. package/dist/hooks/use-machine-chip.d.ts +2 -2
  145. package/dist/hooks/use-machines.d.ts +10 -3
  146. package/dist/hooks/use-packs.d.ts +7 -2
  147. package/dist/hooks/use-rigs.d.ts +1 -1
  148. package/dist/hooks/use-sandbox-files.d.ts +25 -4
  149. package/dist/hooks/use-sandbox-git.d.ts +13 -5
  150. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  151. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  152. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  153. package/dist/hooks/use-session-control.d.ts +1 -1
  154. package/dist/hooks/use-session-events.d.ts +2 -2
  155. package/dist/hooks/use-session-lineage.d.ts +1 -1
  156. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  157. package/dist/hooks/use-session.d.ts +2 -2
  158. package/dist/hooks/use-slash-commands.d.ts +1 -1
  159. package/dist/hooks/use-terminal-stream.d.ts +8 -2
  160. package/dist/hooks/use-transcription.d.ts +2 -2
  161. package/dist/hooks/use-turn-queue.d.ts +2 -2
  162. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  163. package/dist/hooks/use-voice-input.d.ts +6 -4
  164. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  165. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  166. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  167. package/dist/hooks/use-workspaces.d.ts +1 -1
  168. package/dist/index.d.ts +158 -154
  169. package/dist/index.js +1718 -908
  170. package/dist/index.js.map +1 -1
  171. package/dist/interaction.d.ts +23 -0
  172. package/dist/interaction.js +36 -0
  173. package/dist/interaction.js.map +1 -0
  174. package/dist/interactive-workbench-viewers-KHNOHWQE.js +13 -0
  175. package/dist/interactive-workbench-viewers-KHNOHWQE.js.map +1 -0
  176. package/dist/lib/composer-responsive-context.d.ts +9 -0
  177. package/dist/lib/relay-wire.d.ts +4 -0
  178. package/dist/lib/sandbox-liveness.d.ts +9 -0
  179. package/dist/lib/terminal-capability.d.ts +5 -0
  180. package/dist/lib/use-portal-token-style.d.ts +14 -3
  181. package/dist/lib/xterm-theme.d.ts +1 -1
  182. package/dist/machines.d.ts +30 -30
  183. package/dist/machines.js +3 -3
  184. package/dist/provider.d.ts +4 -4
  185. package/dist/{queue-surface-implementation-J4XJFKRW.js → queue-surface-implementation-2KVJTYXH.js} +117 -64
  186. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  187. package/dist/realtime/dropdown-menu.d.ts +8 -8
  188. package/dist/realtime/realtime-control.d.ts +15 -6
  189. package/dist/realtime.d.ts +4 -4
  190. package/dist/realtime.js +30 -33
  191. package/dist/realtime.js.map +1 -1
  192. package/dist/session-context.d.ts +19 -1
  193. package/dist/session-ui.d.ts +14 -14
  194. package/dist/session-ui.js +6 -5
  195. package/dist/session.d.ts +28 -28
  196. package/dist/session.js +5 -5
  197. package/dist/timeline/activity-rail.d.ts +6 -3
  198. package/dist/timeline/disclosure-context.d.ts +1 -1
  199. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  200. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  201. package/dist/timeline/index.d.ts +19 -19
  202. package/dist/timeline/parsers.d.ts +6 -3
  203. package/dist/timeline/projection.d.ts +3 -3
  204. package/dist/timeline/registry.d.ts +10 -1
  205. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  206. package/dist/timeline/shared.d.ts +21 -10
  207. package/dist/timeline/tool-diff.d.ts +2 -2
  208. package/dist/timeline/tool-renderers.d.ts +1 -1
  209. package/dist/timeline/turn-summary.d.ts +3 -3
  210. package/dist/timeline/types.d.ts +42 -2
  211. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  212. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  213. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  214. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  215. package/dist/voice-recording-store.d.ts +8 -2
  216. package/package.json +52 -7
  217. package/src/artifacts-document.ts +31 -0
  218. package/src/artifacts-presentation.ts +36 -0
  219. package/src/artifacts-spreadsheet.ts +21 -0
  220. package/src/artifacts.ts +86 -0
  221. package/src/client.ts +90 -1
  222. package/src/clipboard.ts +2 -0
  223. package/src/components/artifacts/artifact-surface.tsx +93 -0
  224. package/src/components/artifacts/document-editor.tsx +2255 -0
  225. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  226. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  227. package/src/components/artifacts/editable-document.tsx +657 -0
  228. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  229. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  230. package/src/components/artifacts/index.ts +99 -0
  231. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  232. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  233. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  234. package/src/components/browser-viewer.tsx +1592 -0
  235. package/src/components/chat-composer.tsx +25 -2
  236. package/src/components/code-editor.tsx +90 -31
  237. package/src/components/command-palette.tsx +1 -1
  238. package/src/components/composer-transcription-control.tsx +51 -22
  239. package/src/components/composer.tsx +49 -23
  240. package/src/components/computer-viewer.tsx +1139 -0
  241. package/src/components/copy-button.tsx +1 -1
  242. package/src/components/desktop-viewer.tsx +103 -75
  243. package/src/components/file-browser.tsx +39 -17
  244. package/src/components/generated-video-player.tsx +89 -0
  245. package/src/components/human-input-form.tsx +2 -2
  246. package/src/components/interactive-workbench-viewers.ts +2 -0
  247. package/src/components/machine-card.tsx +1 -1
  248. package/src/components/machine-input-display.ts +3 -0
  249. package/src/components/machines/machine-detail.tsx +39 -15
  250. package/src/components/machines-dashboard.tsx +7 -6
  251. package/src/components/message-timeline.tsx +327 -37
  252. package/src/components/model-picker.tsx +2 -2
  253. package/src/components/model-policy-picker.tsx +16 -15
  254. package/src/components/queue-surface-implementation.tsx +135 -75
  255. package/src/components/queue-surface-state.tsx +2 -2
  256. package/src/components/sandbox-files.tsx +142 -62
  257. package/src/components/sandbox-terminal.tsx +119 -35
  258. package/src/components/sandbox-workspace.tsx +394 -228
  259. package/src/components/session-chrome.tsx +67 -27
  260. package/src/components/timeline-annotation-selection.tsx +221 -0
  261. package/src/components/timeline-annotations-dialog.tsx +213 -0
  262. package/src/components/timeline-annotations.tsx +84 -0
  263. package/src/components/tooltip.tsx +13 -16
  264. package/src/components/user-message-body.tsx +1 -1
  265. package/src/components/workbench-changes.tsx +192 -35
  266. package/src/components/workspace-dock.tsx +13 -5
  267. package/src/composer.ts +3 -0
  268. package/src/hooks/internal.ts +132 -38
  269. package/src/hooks/use-attached-browsers.ts +152 -0
  270. package/src/hooks/use-browser-frame-stream.ts +316 -0
  271. package/src/hooks/use-browser-identities.ts +225 -0
  272. package/src/hooks/use-browser-session.ts +476 -0
  273. package/src/hooks/use-browser-sessions.ts +281 -0
  274. package/src/hooks/use-codex-accounts.ts +4 -4
  275. package/src/hooks/use-composer.ts +769 -24
  276. package/src/hooks/use-computer-frame-stream.ts +311 -0
  277. package/src/hooks/use-computer-session.ts +442 -0
  278. package/src/hooks/use-computer-sessions.ts +245 -0
  279. package/src/hooks/use-environments.ts +24 -5
  280. package/src/hooks/use-file-attachments.ts +70 -32
  281. package/src/hooks/use-goal.ts +17 -5
  282. package/src/hooks/use-machine-chip.ts +5 -3
  283. package/src/hooks/use-machines.ts +79 -3
  284. package/src/hooks/use-packs.ts +51 -0
  285. package/src/hooks/use-sandbox-files.ts +382 -88
  286. package/src/hooks/use-sandbox-git.ts +208 -46
  287. package/src/hooks/use-sandbox-terminal.ts +3 -2
  288. package/src/hooks/use-session-capabilities.ts +94 -70
  289. package/src/hooks/use-session-events.ts +18 -4
  290. package/src/hooks/use-terminal-stream.ts +102 -23
  291. package/src/hooks/use-transcription.ts +4 -14
  292. package/src/hooks/use-turn-queue.ts +17 -5
  293. package/src/hooks/use-video-artifact-playback.ts +28 -0
  294. package/src/hooks/use-voice-input.ts +163 -19
  295. package/src/hooks/use-windowed-sections.ts +9 -7
  296. package/src/hooks/use-workspace-edit.ts +2 -1
  297. package/src/index.ts +21 -6
  298. package/src/interaction.ts +71 -0
  299. package/src/lib/composer-responsive-context.ts +15 -0
  300. package/src/lib/relay-wire.ts +4 -0
  301. package/src/lib/sandbox-liveness.ts +11 -0
  302. package/src/lib/terminal-capability.ts +10 -0
  303. package/src/lib/use-portal-token-style.ts +60 -15
  304. package/src/provider.tsx +52 -7
  305. package/src/realtime/dropdown-menu.tsx +15 -19
  306. package/src/realtime/realtime-control.tsx +16 -5
  307. package/src/session-context.ts +90 -0
  308. package/src/timeline/activity-rail.tsx +31 -6
  309. package/src/timeline/index.ts +4 -1
  310. package/src/timeline/parsers.ts +46 -20
  311. package/src/timeline/projection.ts +233 -13
  312. package/src/timeline/registry.ts +19 -0
  313. package/src/timeline/screenshot-lightbox.tsx +30 -9
  314. package/src/timeline/shared.tsx +59 -11
  315. package/src/timeline/tool-renderers.tsx +421 -24
  316. package/src/timeline/turn-summary.tsx +2 -2
  317. package/src/timeline/types.ts +58 -2
  318. package/src/voice-recording-store.ts +15 -0
  319. package/styles/compiled.css +5298 -0
  320. package/styles/compiled.d.ts +2 -0
  321. package/styles/effective-tokens.css +27 -0
  322. package/styles/index.css +18 -0
  323. package/styles/responsive.css +208 -0
  324. package/styles/responsive.d.ts +2 -0
  325. package/styles/tokens.css +50 -17
  326. package/dist/chunk-3PB7MIT6.js.map +0 -1
  327. package/dist/chunk-6EDV6YFE.js.map +0 -1
  328. package/dist/chunk-EB67J347.js.map +0 -1
  329. package/dist/chunk-EKZH6IDG.js.map +0 -1
  330. package/dist/chunk-FGZUCXZF.js.map +0 -1
  331. package/dist/chunk-HFO4ERGQ.js.map +0 -1
  332. package/dist/chunk-JB5725BI.js +0 -48
  333. package/dist/chunk-JB5725BI.js.map +0 -1
  334. package/dist/chunk-LGVMUIRV.js.map +0 -1
  335. package/dist/chunk-LWR4MXSS.js.map +0 -1
  336. package/dist/chunk-SRFUT2ZU.js.map +0 -1
  337. package/dist/chunk-U3CSCMWA.js.map +0 -1
  338. package/dist/chunk-VCBFUSUZ.js.map +0 -1
  339. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  340. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  341. package/dist/queue-surface-implementation-J4XJFKRW.js.map +0 -1
@@ -0,0 +1,476 @@
1
+ import type {
2
+ BrowserAction,
3
+ BrowserActionBatch,
4
+ BrowserActionReceipt,
5
+ BrowserDiagnosticBatch,
6
+ BrowserDiagnosticsOptions,
7
+ BrowserFrame,
8
+ BrowserObservation,
9
+ BrowserSession,
10
+ BrowserTarget,
11
+ } from "@opengeni/sdk/interaction";
12
+ import { useCallback, useEffect, useRef, useState } from "react";
13
+ import {
14
+ type EmbeddedBrowserInteractionClientOverride,
15
+ useEmbeddedBrowserInteraction,
16
+ } from "../session-context";
17
+ import { usePageLiveActivity } from "./internal";
18
+
19
+ export type UseBrowserSessionOptions = EmbeddedBrowserInteractionClientOverride & {
20
+ browserSessionId: string | null;
21
+ enabled?: boolean | undefined;
22
+ pollIntervalMs?: number | undefined;
23
+ };
24
+
25
+ export type UseBrowserSessionResult = {
26
+ session: BrowserSession | null;
27
+ targets: BrowserTarget[];
28
+ selectedTarget: BrowserTarget | null;
29
+ observation: BrowserObservation | null;
30
+ loading: boolean;
31
+ mutating: boolean;
32
+ error: Error | null;
33
+ refresh: () => Promise<void>;
34
+ selectTarget: (targetId: string) => Promise<BrowserTarget>;
35
+ openTarget: (url?: string) => Promise<BrowserTarget>;
36
+ closeTarget: (targetId: string) => Promise<void>;
37
+ act: (
38
+ action: BrowserAction | BrowserActionBatch,
39
+ operationId?: string,
40
+ ) => Promise<BrowserActionReceipt>;
41
+ /** Dispatch input against the exact image a human saw. Stale images fail at
42
+ * the controller fence instead of targeting a newer page. */
43
+ actFromFrame: (
44
+ action: BrowserAction | BrowserActionBatch,
45
+ frame: BrowserFrame,
46
+ operationId?: string,
47
+ ) => Promise<BrowserActionReceipt>;
48
+ diagnostics: (options?: BrowserDiagnosticsOptions) => Promise<BrowserDiagnosticBatch>;
49
+ };
50
+
51
+ /** Selected BrowserSession control state. All human actions use the same
52
+ * generation-fenced controller operation stream as agent actions. */
53
+ export function useBrowserSession(options: UseBrowserSessionOptions): UseBrowserSessionResult {
54
+ const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
55
+ const browserSessionId = options.browserSessionId;
56
+ const enabled = (options.enabled ?? true) && browserSessionId !== null;
57
+ const pageLive = usePageLiveActivity();
58
+ const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2_000);
59
+ const [state, setState] = useState<{
60
+ browserSessionId: string | null;
61
+ session: BrowserSession | null;
62
+ targets: BrowserTarget[];
63
+ selectedTargetId: string | null;
64
+ observation: BrowserObservation | null;
65
+ loading: boolean;
66
+ mutating: boolean;
67
+ error: Error | null;
68
+ }>(() => emptyState(browserSessionId, enabled));
69
+ const visible =
70
+ state.browserSessionId === browserSessionId ? state : emptyState(browserSessionId, enabled);
71
+ const selectedTargetIdRef = useRef<string | null>(visible.selectedTargetId);
72
+ selectedTargetIdRef.current = visible.selectedTargetId;
73
+ const observationRef = useRef<{
74
+ browserSessionId: string | null;
75
+ observation: BrowserObservation | null;
76
+ }>({ browserSessionId, observation: visible.observation });
77
+ if (observationRef.current.browserSessionId !== browserSessionId) {
78
+ observationRef.current = {
79
+ browserSessionId,
80
+ observation: visible.observation,
81
+ };
82
+ } else {
83
+ observationRef.current.observation = visible.observation;
84
+ }
85
+ const requestRef = useRef<{ id: number; controller: AbortController | null }>({
86
+ id: 0,
87
+ controller: null,
88
+ });
89
+ const mutationRef = useRef<{
90
+ browserSessionId: string | null;
91
+ count: number;
92
+ }>({
93
+ browserSessionId,
94
+ count: 0,
95
+ });
96
+ const mountedRef = useRef(true);
97
+
98
+ const invalidateRefresh = useCallback(() => {
99
+ const id = requestRef.current.id + 1;
100
+ requestRef.current.controller?.abort();
101
+ requestRef.current = { id, controller: null };
102
+ }, []);
103
+
104
+ const refresh = useCallback(async (): Promise<void> => {
105
+ if (!enabled || !browserSessionId) return;
106
+ const id = requestRef.current.id + 1;
107
+ requestRef.current.controller?.abort();
108
+ const controller = new AbortController();
109
+ requestRef.current = { id, controller };
110
+ try {
111
+ const [session, targetResponse] = await Promise.all([
112
+ client.getBrowserSession(workspaceId, browserSessionId, {
113
+ signal: controller.signal,
114
+ }),
115
+ client.listBrowserTargets(workspaceId, browserSessionId, {
116
+ signal: controller.signal,
117
+ }),
118
+ ]);
119
+ if (!mountedRef.current || requestRef.current.id !== id) return;
120
+ const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
121
+ const observation = selected
122
+ ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id, {
123
+ signal: controller.signal,
124
+ })
125
+ : null;
126
+ if (!mountedRef.current || requestRef.current.id !== id) return;
127
+ selectedTargetIdRef.current = selected?.id ?? null;
128
+ observationRef.current = { browserSessionId, observation };
129
+ setState((current) =>
130
+ current.browserSessionId === browserSessionId
131
+ ? {
132
+ ...current,
133
+ session,
134
+ targets: targetResponse.targets,
135
+ selectedTargetId: selected?.id ?? null,
136
+ observation,
137
+ loading: false,
138
+ error: null,
139
+ }
140
+ : current,
141
+ );
142
+ } catch (cause) {
143
+ if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
144
+ setState((current) =>
145
+ current.browserSessionId === browserSessionId
146
+ ? {
147
+ ...current,
148
+ loading: false,
149
+ error: cause instanceof Error ? cause : new Error(String(cause)),
150
+ }
151
+ : current,
152
+ );
153
+ } finally {
154
+ if (requestRef.current.id === id) {
155
+ requestRef.current = { id, controller: null };
156
+ }
157
+ }
158
+ }, [browserSessionId, client, enabled, workspaceId]);
159
+
160
+ useEffect(() => {
161
+ mountedRef.current = true;
162
+ if (!enabled) {
163
+ invalidateRefresh();
164
+ setState(emptyState(browserSessionId, false));
165
+ return;
166
+ }
167
+ setState((current) =>
168
+ current.browserSessionId === browserSessionId
169
+ ? { ...current, loading: true }
170
+ : emptyState(browserSessionId, true),
171
+ );
172
+ void refresh();
173
+ return () => {
174
+ mountedRef.current = false;
175
+ invalidateRefresh();
176
+ };
177
+ }, [browserSessionId, enabled, invalidateRefresh, refresh]);
178
+
179
+ useEffect(() => {
180
+ if (!enabled || !pageLive || visible.mutating) return;
181
+ const timer = setInterval(() => {
182
+ if (!requestRef.current.controller) void refresh();
183
+ }, pollIntervalMs);
184
+ return () => clearInterval(timer);
185
+ }, [enabled, pageLive, pollIntervalMs, refresh, visible.mutating]);
186
+
187
+ useEffect(() => {
188
+ if (!enabled || !browserSessionId || !pageLive) return;
189
+ let disposed = false;
190
+ const heartbeat = () => {
191
+ void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
192
+ if (!disposed) void refresh();
193
+ });
194
+ };
195
+ const timer = setInterval(heartbeat, 30_000);
196
+ return () => {
197
+ disposed = true;
198
+ clearInterval(timer);
199
+ };
200
+ }, [browserSessionId, client, enabled, pageLive, refresh, workspaceId]);
201
+
202
+ const runMutation = useCallback(
203
+ async <T>(scopeBrowserSessionId: string, operation: () => Promise<T>): Promise<T> => {
204
+ invalidateRefresh();
205
+ if (mutationRef.current.browserSessionId !== scopeBrowserSessionId) {
206
+ mutationRef.current = {
207
+ browserSessionId: scopeBrowserSessionId,
208
+ count: 0,
209
+ };
210
+ }
211
+ mutationRef.current.count += 1;
212
+ setState((current) =>
213
+ current.browserSessionId === scopeBrowserSessionId
214
+ ? { ...current, mutating: true, error: null }
215
+ : current,
216
+ );
217
+ try {
218
+ return await operation();
219
+ } catch (cause) {
220
+ const error = cause instanceof Error ? cause : new Error(String(cause));
221
+ setState((current) =>
222
+ current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current,
223
+ );
224
+ throw error;
225
+ } finally {
226
+ if (mutationRef.current.browserSessionId === scopeBrowserSessionId) {
227
+ mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
228
+ const mutating = mutationRef.current.count > 0;
229
+ setState((current) =>
230
+ current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current,
231
+ );
232
+ }
233
+ }
234
+ },
235
+ [invalidateRefresh],
236
+ );
237
+
238
+ const selectTarget = useCallback(
239
+ async (targetId: string): Promise<BrowserTarget> => {
240
+ if (!browserSessionId) throw new Error("No BrowserSession is selected.");
241
+ return await runMutation(browserSessionId, async () => {
242
+ const target = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
243
+ const observation = await client.observeBrowserTarget(
244
+ workspaceId,
245
+ browserSessionId,
246
+ target.id,
247
+ );
248
+ selectedTargetIdRef.current = target.id;
249
+ observationRef.current = { browserSessionId, observation };
250
+ setState((current) =>
251
+ current.browserSessionId === browserSessionId
252
+ ? {
253
+ ...current,
254
+ targets: current.targets.map((candidate) => ({
255
+ ...candidate,
256
+ selected: candidate.id === target.id,
257
+ })),
258
+ selectedTargetId: target.id,
259
+ observation,
260
+ }
261
+ : current,
262
+ );
263
+ return target;
264
+ });
265
+ },
266
+ [browserSessionId, client, runMutation, workspaceId],
267
+ );
268
+
269
+ const openTarget = useCallback(
270
+ async (url?: string): Promise<BrowserTarget> => {
271
+ if (!browserSessionId) throw new Error("No BrowserSession is selected.");
272
+ return await runMutation(browserSessionId, async () => {
273
+ const target = await client.openBrowserTarget(
274
+ workspaceId,
275
+ browserSessionId,
276
+ url === undefined ? {} : { url },
277
+ );
278
+ const observation = await client.observeBrowserTarget(
279
+ workspaceId,
280
+ browserSessionId,
281
+ target.id,
282
+ );
283
+ selectedTargetIdRef.current = target.id;
284
+ observationRef.current = { browserSessionId, observation };
285
+ setState((current) =>
286
+ current.browserSessionId === browserSessionId
287
+ ? {
288
+ ...current,
289
+ targets: [
290
+ ...current.targets.map((candidate) => ({
291
+ ...candidate,
292
+ selected: false,
293
+ })),
294
+ target,
295
+ ],
296
+ selectedTargetId: target.id,
297
+ observation,
298
+ }
299
+ : current,
300
+ );
301
+ return target;
302
+ });
303
+ },
304
+ [browserSessionId, client, runMutation, workspaceId],
305
+ );
306
+
307
+ const closeTarget = useCallback(
308
+ async (targetId: string): Promise<void> => {
309
+ if (!browserSessionId) throw new Error("No BrowserSession is selected.");
310
+ await runMutation(browserSessionId, async () => {
311
+ const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
312
+ const selected = chooseTarget(response.targets, null);
313
+ const observation = selected
314
+ ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id)
315
+ : null;
316
+ selectedTargetIdRef.current = selected?.id ?? null;
317
+ observationRef.current = { browserSessionId, observation };
318
+ setState((current) =>
319
+ current.browserSessionId === browserSessionId
320
+ ? {
321
+ ...current,
322
+ targets: response.targets,
323
+ selectedTargetId: selected?.id ?? null,
324
+ observation,
325
+ }
326
+ : current,
327
+ );
328
+ });
329
+ },
330
+ [browserSessionId, client, runMutation, workspaceId],
331
+ );
332
+
333
+ const dispatchAction = useCallback(
334
+ async (
335
+ action: BrowserAction | BrowserActionBatch,
336
+ operationId: string,
337
+ frame: BrowserFrame | null,
338
+ ): Promise<BrowserActionReceipt> => {
339
+ if (!browserSessionId) throw new Error("No BrowserSession is selected.");
340
+ if (frame && frame.browserSessionId !== browserSessionId) {
341
+ throw new Error("The displayed browser frame belongs to another BrowserSession.");
342
+ }
343
+ const currentObservation =
344
+ observationRef.current.browserSessionId === browserSessionId
345
+ ? observationRef.current.observation
346
+ : null;
347
+ const fence = frame
348
+ ? {
349
+ targetId: frame.targetId,
350
+ expectedTargetGeneration: frame.targetGeneration,
351
+ expectedDocumentGeneration: frame.documentGeneration,
352
+ expectedFrameId: frame.frameId,
353
+ }
354
+ : currentObservation
355
+ ? {
356
+ targetId: currentObservation.target.id,
357
+ expectedTargetGeneration: currentObservation.target.targetGeneration,
358
+ expectedDocumentGeneration: currentObservation.target.documentGeneration,
359
+ expectedFrameId: currentObservation.frameId,
360
+ }
361
+ : null;
362
+ if (!fence) {
363
+ throw new Error("The browser page is not ready for input.");
364
+ }
365
+ return await runMutation(browserSessionId, async () => {
366
+ const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
367
+ operationId,
368
+ ...fence,
369
+ action,
370
+ });
371
+ if (receipt.observation) {
372
+ observationRef.current = {
373
+ browserSessionId,
374
+ observation: receipt.observation,
375
+ };
376
+ setState((current) =>
377
+ current.browserSessionId === browserSessionId
378
+ ? {
379
+ ...current,
380
+ observation: receipt.observation,
381
+ targets: replaceTarget(current.targets, receipt.observation!.target),
382
+ }
383
+ : current,
384
+ );
385
+ } else {
386
+ void refresh();
387
+ }
388
+ return receipt;
389
+ });
390
+ },
391
+ [browserSessionId, client, refresh, runMutation, workspaceId],
392
+ );
393
+
394
+ const act = useCallback(
395
+ async (
396
+ action: BrowserAction | BrowserActionBatch,
397
+ operationId: string = crypto.randomUUID(),
398
+ ): Promise<BrowserActionReceipt> => await dispatchAction(action, operationId, null),
399
+ [dispatchAction],
400
+ );
401
+
402
+ const actFromFrame = useCallback(
403
+ async (
404
+ action: BrowserAction | BrowserActionBatch,
405
+ frame: BrowserFrame,
406
+ operationId: string = crypto.randomUUID(),
407
+ ): Promise<BrowserActionReceipt> => await dispatchAction(action, operationId, frame),
408
+ [dispatchAction],
409
+ );
410
+
411
+ const diagnostics = useCallback(
412
+ async (diagnosticOptions: BrowserDiagnosticsOptions = {}): Promise<BrowserDiagnosticBatch> => {
413
+ const targetId = selectedTargetIdRef.current;
414
+ if (!browserSessionId || !targetId) {
415
+ throw new Error("No browser tab is selected.");
416
+ }
417
+ return await client.listBrowserDiagnostics(
418
+ workspaceId,
419
+ browserSessionId,
420
+ targetId,
421
+ diagnosticOptions,
422
+ );
423
+ },
424
+ [browserSessionId, client, workspaceId],
425
+ );
426
+
427
+ return {
428
+ session: visible.session,
429
+ targets: visible.targets,
430
+ selectedTarget:
431
+ visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null,
432
+ observation: visible.observation,
433
+ loading: visible.loading,
434
+ mutating: visible.mutating,
435
+ error: visible.error,
436
+ refresh,
437
+ selectTarget,
438
+ openTarget,
439
+ closeTarget,
440
+ act,
441
+ actFromFrame,
442
+ diagnostics,
443
+ };
444
+ }
445
+
446
+ function emptyState(browserSessionId: string | null, loading: boolean) {
447
+ return {
448
+ browserSessionId,
449
+ session: null as BrowserSession | null,
450
+ targets: [] as BrowserTarget[],
451
+ selectedTargetId: null as string | null,
452
+ observation: null as BrowserObservation | null,
453
+ loading,
454
+ mutating: false,
455
+ error: null as Error | null,
456
+ };
457
+ }
458
+
459
+ function chooseTarget(
460
+ targets: readonly BrowserTarget[],
461
+ preferredId: string | null,
462
+ ): BrowserTarget | null {
463
+ return (
464
+ targets.find((target) => target.id === preferredId) ??
465
+ targets.find((target) => target.selected && target.kind === "page") ??
466
+ targets.find((target) => target.kind === "page") ??
467
+ targets[0] ??
468
+ null
469
+ );
470
+ }
471
+
472
+ function replaceTarget(targets: readonly BrowserTarget[], target: BrowserTarget): BrowserTarget[] {
473
+ const next = targets.filter((candidate) => candidate.id !== target.id);
474
+ next.push(target);
475
+ return next.sort((left, right) => left.createdAt.localeCompare(right.createdAt));
476
+ }