@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,442 @@
1
+ import type {
2
+ ComputerAction,
3
+ ComputerActionReceipt,
4
+ ComputerFrame,
5
+ ComputerObservation,
6
+ ComputerSession,
7
+ ComputerTarget,
8
+ } from "@opengeni/sdk/interaction";
9
+ import { useCallback, useEffect, useRef, useState } from "react";
10
+ import {
11
+ type EmbeddedComputerInteractionClientOverride,
12
+ useEmbeddedComputerInteraction,
13
+ } from "../session-context";
14
+ import { usePageLiveActivity } from "./internal";
15
+
16
+ export type UseComputerSessionOptions = EmbeddedComputerInteractionClientOverride & {
17
+ computerSessionId: string | null;
18
+ enabled?: boolean | undefined;
19
+ pollIntervalMs?: number | undefined;
20
+ };
21
+
22
+ export type UseComputerSessionResult = {
23
+ session: ComputerSession | null;
24
+ targets: ComputerTarget[];
25
+ selectedTarget: ComputerTarget | null;
26
+ observation: ComputerObservation | null;
27
+ loading: boolean;
28
+ mutating: boolean;
29
+ error: Error | null;
30
+ refresh: () => Promise<void>;
31
+ /** Changes only this viewer's target cursor; it never takes ownership or
32
+ * foregrounds an application. */
33
+ selectTarget: (targetId: string) => Promise<ComputerTarget>;
34
+ act: (action: ComputerAction, operationId?: string) => Promise<ComputerActionReceipt>;
35
+ /** Dispatch pointer input against the exact displayed frame. */
36
+ actFromFrame: (
37
+ action: Extract<ComputerAction, { type: "pointer" }>,
38
+ frame: ComputerFrame,
39
+ operationId?: string,
40
+ ) => Promise<ComputerActionReceipt>;
41
+ };
42
+
43
+ /** Selected ComputerSession state. Semantic and pixel input share the same
44
+ * generation-fenced controller operation stream used by agents. */
45
+ export function useComputerSession(options: UseComputerSessionOptions): UseComputerSessionResult {
46
+ const { client, workspaceId } = useEmbeddedComputerInteraction(options);
47
+ const computerSessionId = options.computerSessionId;
48
+ const enabled = (options.enabled ?? true) && computerSessionId !== null;
49
+ const pageLive = usePageLiveActivity();
50
+ const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2_000);
51
+ const [state, setState] = useState<ComputerControlState>(() =>
52
+ emptyState(computerSessionId, enabled),
53
+ );
54
+ const visible =
55
+ state.computerSessionId === computerSessionId ? state : emptyState(computerSessionId, enabled);
56
+ const selectedTargetIdRef = useRef<string | null>(visible.selectedTargetId);
57
+ selectedTargetIdRef.current = visible.selectedTargetId;
58
+ const targetsRef = useRef<{
59
+ computerSessionId: string | null;
60
+ targets: ComputerTarget[];
61
+ }>({
62
+ computerSessionId,
63
+ targets: visible.targets,
64
+ });
65
+ const observationRef = useRef<{
66
+ computerSessionId: string | null;
67
+ observation: ComputerObservation | null;
68
+ }>({ computerSessionId, observation: visible.observation });
69
+ if (targetsRef.current.computerSessionId !== computerSessionId) {
70
+ targetsRef.current = { computerSessionId, targets: visible.targets };
71
+ observationRef.current = {
72
+ computerSessionId,
73
+ observation: visible.observation,
74
+ };
75
+ } else {
76
+ targetsRef.current.targets = visible.targets;
77
+ observationRef.current.observation = visible.observation;
78
+ }
79
+ const requestRef = useRef<{ id: number; controller: AbortController | null }>({
80
+ id: 0,
81
+ controller: null,
82
+ });
83
+ const mutationRef = useRef<{
84
+ computerSessionId: string | null;
85
+ count: number;
86
+ }>({
87
+ computerSessionId,
88
+ count: 0,
89
+ });
90
+ const mountedRef = useRef(true);
91
+
92
+ const invalidateRefresh = useCallback(() => {
93
+ const id = requestRef.current.id + 1;
94
+ requestRef.current.controller?.abort();
95
+ requestRef.current = { id, controller: null };
96
+ }, []);
97
+
98
+ const refresh = useCallback(async (): Promise<void> => {
99
+ if (!enabled || !computerSessionId) return;
100
+ const id = requestRef.current.id + 1;
101
+ requestRef.current.controller?.abort();
102
+ const controller = new AbortController();
103
+ requestRef.current = { id, controller };
104
+ try {
105
+ const [session, targetResponse] = await Promise.all([
106
+ client.getComputerSession(workspaceId, computerSessionId, {
107
+ signal: controller.signal,
108
+ }),
109
+ client.listComputerTargets(workspaceId, computerSessionId, {
110
+ signal: controller.signal,
111
+ }),
112
+ ]);
113
+ if (!mountedRef.current || requestRef.current.id !== id) return;
114
+ const targets = sortComputerTargets(targetResponse.targets);
115
+ const selected = chooseTarget(targets, selectedTargetIdRef.current);
116
+ const observation = selected
117
+ ? await client.observeComputerTarget(workspaceId, computerSessionId, selected.id, {
118
+ signal: controller.signal,
119
+ })
120
+ : null;
121
+ if (!mountedRef.current || requestRef.current.id !== id) return;
122
+ selectedTargetIdRef.current = selected?.id ?? null;
123
+ targetsRef.current = { computerSessionId, targets };
124
+ observationRef.current = { computerSessionId, observation };
125
+ setState((current) =>
126
+ current.computerSessionId === computerSessionId
127
+ ? {
128
+ ...current,
129
+ session,
130
+ targets,
131
+ selectedTargetId: selected?.id ?? null,
132
+ observation,
133
+ loading: false,
134
+ error: null,
135
+ }
136
+ : current,
137
+ );
138
+ } catch (cause) {
139
+ if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
140
+ setState((current) =>
141
+ current.computerSessionId === computerSessionId
142
+ ? {
143
+ ...current,
144
+ loading: false,
145
+ error: cause instanceof Error ? cause : new Error(String(cause)),
146
+ }
147
+ : current,
148
+ );
149
+ } finally {
150
+ if (requestRef.current.id === id) requestRef.current = { id, controller: null };
151
+ }
152
+ }, [client, computerSessionId, enabled, workspaceId]);
153
+
154
+ useEffect(() => {
155
+ mountedRef.current = true;
156
+ if (!enabled) {
157
+ invalidateRefresh();
158
+ setState(emptyState(computerSessionId, false));
159
+ return;
160
+ }
161
+ setState((current) =>
162
+ current.computerSessionId === computerSessionId
163
+ ? { ...current, loading: true }
164
+ : emptyState(computerSessionId, true),
165
+ );
166
+ void refresh();
167
+ return () => {
168
+ mountedRef.current = false;
169
+ invalidateRefresh();
170
+ };
171
+ }, [computerSessionId, enabled, invalidateRefresh, refresh]);
172
+
173
+ useEffect(() => {
174
+ if (!enabled || !pageLive || visible.mutating) return;
175
+ const timer = setInterval(() => {
176
+ if (!requestRef.current.controller) void refresh();
177
+ }, pollIntervalMs);
178
+ return () => clearInterval(timer);
179
+ }, [enabled, pageLive, pollIntervalMs, refresh, visible.mutating]);
180
+
181
+ useEffect(() => {
182
+ if (!enabled || !computerSessionId || !pageLive) return;
183
+ let disposed = false;
184
+ const timer = setInterval(() => {
185
+ void client.heartbeatComputerSession(workspaceId, computerSessionId).catch(() => {
186
+ if (!disposed) void refresh();
187
+ });
188
+ }, 30_000);
189
+ return () => {
190
+ disposed = true;
191
+ clearInterval(timer);
192
+ };
193
+ }, [client, computerSessionId, enabled, pageLive, refresh, workspaceId]);
194
+
195
+ const runMutation = useCallback(
196
+ async <T>(scopeComputerSessionId: string, operation: () => Promise<T>): Promise<T> => {
197
+ invalidateRefresh();
198
+ if (mutationRef.current.computerSessionId !== scopeComputerSessionId) {
199
+ mutationRef.current = {
200
+ computerSessionId: scopeComputerSessionId,
201
+ count: 0,
202
+ };
203
+ }
204
+ mutationRef.current.count += 1;
205
+ setState((current) =>
206
+ current.computerSessionId === scopeComputerSessionId
207
+ ? { ...current, mutating: true, error: null }
208
+ : current,
209
+ );
210
+ try {
211
+ return await operation();
212
+ } catch (cause) {
213
+ const error = cause instanceof Error ? cause : new Error(String(cause));
214
+ setState((current) =>
215
+ current.computerSessionId === scopeComputerSessionId ? { ...current, error } : current,
216
+ );
217
+ throw error;
218
+ } finally {
219
+ if (mutationRef.current.computerSessionId === scopeComputerSessionId) {
220
+ mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
221
+ const mutating = mutationRef.current.count > 0;
222
+ setState((current) =>
223
+ current.computerSessionId === scopeComputerSessionId
224
+ ? { ...current, mutating }
225
+ : current,
226
+ );
227
+ }
228
+ }
229
+ },
230
+ [invalidateRefresh],
231
+ );
232
+
233
+ const selectTarget = useCallback(
234
+ async (targetId: string): Promise<ComputerTarget> => {
235
+ if (!computerSessionId) throw new Error("No ComputerSession is selected.");
236
+ const target = targetsRef.current.targets.find((candidate) => candidate.id === targetId);
237
+ if (!target) throw new Error("The selected computer target is no longer available.");
238
+ invalidateRefresh();
239
+ selectedTargetIdRef.current = target.id;
240
+ observationRef.current = { computerSessionId, observation: null };
241
+ setState((current) =>
242
+ current.computerSessionId === computerSessionId
243
+ ? {
244
+ ...current,
245
+ selectedTargetId: target.id,
246
+ observation: null,
247
+ loading: true,
248
+ error: null,
249
+ }
250
+ : current,
251
+ );
252
+ try {
253
+ const observation = await client.observeComputerTarget(
254
+ workspaceId,
255
+ computerSessionId,
256
+ target.id,
257
+ );
258
+ if (selectedTargetIdRef.current !== target.id) return target;
259
+ observationRef.current = { computerSessionId, observation };
260
+ setState((current) =>
261
+ current.computerSessionId === computerSessionId && current.selectedTargetId === target.id
262
+ ? { ...current, observation, loading: false, error: null }
263
+ : current,
264
+ );
265
+ return observation.target;
266
+ } catch (cause) {
267
+ const error = cause instanceof Error ? cause : new Error(String(cause));
268
+ setState((current) =>
269
+ current.computerSessionId === computerSessionId && current.selectedTargetId === target.id
270
+ ? { ...current, loading: false, error }
271
+ : current,
272
+ );
273
+ throw error;
274
+ }
275
+ },
276
+ [client, computerSessionId, invalidateRefresh, workspaceId],
277
+ );
278
+
279
+ const dispatchAction = useCallback(
280
+ async (
281
+ action: ComputerAction,
282
+ operationId: string,
283
+ frame: ComputerFrame | null,
284
+ ): Promise<ComputerActionReceipt> => {
285
+ if (!computerSessionId) throw new Error("No ComputerSession is selected.");
286
+ if (frame && frame.computerSessionId !== computerSessionId) {
287
+ throw new Error("The displayed computer frame belongs to another ComputerSession.");
288
+ }
289
+ const currentObservation =
290
+ observationRef.current.computerSessionId === computerSessionId
291
+ ? observationRef.current.observation
292
+ : null;
293
+ const focusTarget =
294
+ action.type === "focus"
295
+ ? (targetsRef.current.targets.find((candidate) => candidate.id === action.targetId) ??
296
+ null)
297
+ : null;
298
+ const frameTarget = frame
299
+ ? (targetsRef.current.targets.find((candidate) => candidate.id === frame.targetId) ??
300
+ (currentObservation?.target.id === frame.targetId ? currentObservation.target : null))
301
+ : null;
302
+ const target = frameTarget ?? focusTarget ?? currentObservation?.target ?? null;
303
+ if (!target) throw new Error("The computer target is not ready for input.");
304
+ if (frame && frame.targetId !== selectedTargetIdRef.current) {
305
+ throw new Error("The displayed computer frame is no longer selected.");
306
+ }
307
+ if (action.type === "pointer") {
308
+ const expectedFrameId = frame?.frameId ?? currentObservation?.frameId ?? null;
309
+ if (!expectedFrameId || action.frameId !== expectedFrameId) {
310
+ throw new Error("Pointer input must reference the exact displayed computer frame.");
311
+ }
312
+ }
313
+ if (action.type === "semantic" && !currentObservation) {
314
+ throw new Error("The computer accessibility tree is not ready for input.");
315
+ }
316
+
317
+ return await runMutation(computerSessionId, async () => {
318
+ const receipt = await client.actInComputer(workspaceId, computerSessionId, {
319
+ operationId,
320
+ targetId: target.id,
321
+ expectedTargetGeneration: frame?.targetGeneration ?? target.targetGeneration,
322
+ expectedObservationId:
323
+ action.type === "pointer" ? null : (currentObservation?.observationId ?? null),
324
+ expectedFrameId: action.type === "pointer" ? action.frameId : null,
325
+ action,
326
+ });
327
+ if (receipt.observation) {
328
+ const observation = receipt.observation;
329
+ observationRef.current = { computerSessionId, observation };
330
+ selectedTargetIdRef.current = observation.target.id;
331
+ setState((current) =>
332
+ current.computerSessionId === computerSessionId
333
+ ? {
334
+ ...current,
335
+ observation,
336
+ selectedTargetId: observation.target.id,
337
+ targets: replaceTarget(current.targets, observation.target),
338
+ }
339
+ : current,
340
+ );
341
+ } else {
342
+ void refresh();
343
+ }
344
+ return receipt;
345
+ });
346
+ },
347
+ [client, computerSessionId, refresh, runMutation, workspaceId],
348
+ );
349
+
350
+ const act = useCallback(
351
+ async (
352
+ action: ComputerAction,
353
+ operationId: string = crypto.randomUUID(),
354
+ ): Promise<ComputerActionReceipt> => await dispatchAction(action, operationId, null),
355
+ [dispatchAction],
356
+ );
357
+
358
+ const actFromFrame = useCallback(
359
+ async (
360
+ action: Extract<ComputerAction, { type: "pointer" }>,
361
+ frame: ComputerFrame,
362
+ operationId: string = crypto.randomUUID(),
363
+ ): Promise<ComputerActionReceipt> => await dispatchAction(action, operationId, frame),
364
+ [dispatchAction],
365
+ );
366
+
367
+ return {
368
+ session: visible.session,
369
+ targets: visible.targets,
370
+ selectedTarget:
371
+ visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null,
372
+ observation: visible.observation,
373
+ loading: visible.loading,
374
+ mutating: visible.mutating,
375
+ error: visible.error,
376
+ refresh,
377
+ selectTarget,
378
+ act,
379
+ actFromFrame,
380
+ };
381
+ }
382
+
383
+ type ComputerControlState = {
384
+ computerSessionId: string | null;
385
+ session: ComputerSession | null;
386
+ targets: ComputerTarget[];
387
+ selectedTargetId: string | null;
388
+ observation: ComputerObservation | null;
389
+ loading: boolean;
390
+ mutating: boolean;
391
+ error: Error | null;
392
+ };
393
+
394
+ function emptyState(computerSessionId: string | null, loading: boolean): ComputerControlState {
395
+ return {
396
+ computerSessionId,
397
+ session: null,
398
+ targets: [],
399
+ selectedTargetId: null,
400
+ observation: null,
401
+ loading,
402
+ mutating: false,
403
+ error: null,
404
+ };
405
+ }
406
+
407
+ function chooseTarget(
408
+ targets: readonly ComputerTarget[],
409
+ preferredId: string | null,
410
+ ): ComputerTarget | null {
411
+ return (
412
+ targets.find((target) => target.id === preferredId) ??
413
+ targets.find((target) => target.focused && target.kind === "window") ??
414
+ targets.find((target) => target.kind === "window") ??
415
+ targets.find((target) => target.focused && target.kind === "app") ??
416
+ targets.find((target) => target.kind === "app") ??
417
+ targets.find((target) => target.kind === "screen") ??
418
+ targets[0] ??
419
+ null
420
+ );
421
+ }
422
+
423
+ function sortComputerTargets(targets: readonly ComputerTarget[]): ComputerTarget[] {
424
+ const rank = { window: 0, app: 1, screen: 2 } as const;
425
+ return [...targets].sort(
426
+ (left, right) =>
427
+ Number(right.focused) - Number(left.focused) ||
428
+ rank[left.kind] - rank[right.kind] ||
429
+ left.title.localeCompare(right.title) ||
430
+ left.id.localeCompare(right.id),
431
+ );
432
+ }
433
+
434
+ function replaceTarget(
435
+ targets: readonly ComputerTarget[],
436
+ target: ComputerTarget,
437
+ ): ComputerTarget[] {
438
+ return sortComputerTargets([
439
+ ...targets.filter((candidate) => candidate.id !== target.id),
440
+ target,
441
+ ]);
442
+ }
@@ -0,0 +1,245 @@
1
+ import type {
2
+ ComputerSession,
3
+ ComputerSessionMutationResponse,
4
+ CreateComputerSessionRequest,
5
+ } from "@opengeni/sdk/interaction";
6
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
7
+ import type { EmbeddedComputerInteractionClientLike } from "../client";
8
+ import {
9
+ type EmbeddedComputerInteractionClientOverride,
10
+ useEmbeddedComputerInteraction,
11
+ } from "../session-context";
12
+ import { usePageLiveActivity } from "./internal";
13
+
14
+ export type UseComputerSessionsOptions = EmbeddedComputerInteractionClientOverride & {
15
+ /** Current agent/session relevance. Workspace peers remain in `sessions`. */
16
+ sessionId?: string | undefined;
17
+ enabled?: boolean | undefined;
18
+ /** Temporary invalidation path until the interaction event stream lands. */
19
+ pollIntervalMs?: number | undefined;
20
+ };
21
+
22
+ export type UseComputerSessionsResult = {
23
+ revision: number;
24
+ sessions: ComputerSession[];
25
+ relevantSessions: ComputerSession[];
26
+ loading: boolean;
27
+ refreshing: boolean;
28
+ error: Error | null;
29
+ refresh: () => Promise<void>;
30
+ create: (
31
+ request: Omit<CreateComputerSessionRequest, "operationId"> & {
32
+ operationId?: string;
33
+ },
34
+ ) => Promise<ComputerSessionMutationResponse>;
35
+ end: (
36
+ computerSessionId: string,
37
+ operationId?: string,
38
+ ) => Promise<ComputerSessionMutationResponse>;
39
+ };
40
+
41
+ /** Workspace-wide ComputerSession registry. Associations rank relevance but
42
+ * never hide peer resources or create an ownership boundary. */
43
+ export function useComputerSessions(
44
+ options: UseComputerSessionsOptions = {},
45
+ ): UseComputerSessionsResult {
46
+ const { client, workspaceId, registerSessionReconciler } =
47
+ useEmbeddedComputerInteraction(options);
48
+ const enabled = options.enabled ?? true;
49
+ const pageLive = usePageLiveActivity();
50
+ const pollIntervalMs = Math.max(1_000, options.pollIntervalMs ?? 3_000);
51
+ const [state, setState] = useState<ComputerRegistryState>(() => emptyState(workspaceId, enabled));
52
+ const requestRef = useRef<{ id: number; controller: AbortController | null }>({
53
+ id: 0,
54
+ controller: null,
55
+ });
56
+ const mountedRef = useRef(true);
57
+ const visible = state.workspaceId === workspaceId ? state : emptyState(workspaceId, enabled);
58
+
59
+ const cancelLoad = useCallback(() => {
60
+ const id = requestRef.current.id + 1;
61
+ requestRef.current.controller?.abort();
62
+ requestRef.current = { id, controller: null };
63
+ }, []);
64
+
65
+ const load = useCallback(
66
+ async (foreground: boolean): Promise<void> => {
67
+ if (!enabled) return;
68
+ const id = requestRef.current.id + 1;
69
+ requestRef.current.controller?.abort();
70
+ const controller = new AbortController();
71
+ requestRef.current = { id, controller };
72
+ setState((current) => ({
73
+ ...(current.workspaceId === workspaceId ? current : emptyState(workspaceId, true)),
74
+ loading: foreground,
75
+ refreshing: !foreground,
76
+ error: foreground || current.workspaceId !== workspaceId ? null : current.error,
77
+ }));
78
+ try {
79
+ const response = await client.listComputerSessions(workspaceId, {
80
+ signal: controller.signal,
81
+ });
82
+ if (!mountedRef.current || requestRef.current.id !== id) return;
83
+ setState({
84
+ workspaceId,
85
+ revision: response.revision,
86
+ sessions: sortComputerSessions(response.sessions),
87
+ loading: false,
88
+ refreshing: false,
89
+ error: null,
90
+ });
91
+ } catch (cause) {
92
+ if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
93
+ return;
94
+ setState((current) => ({
95
+ ...(current.workspaceId === workspaceId ? current : emptyState(workspaceId, true)),
96
+ loading: false,
97
+ refreshing: false,
98
+ error: cause instanceof Error ? cause : new Error(String(cause)),
99
+ }));
100
+ } finally {
101
+ if (requestRef.current.id === id) requestRef.current = { id, controller: null };
102
+ }
103
+ },
104
+ [client, enabled, workspaceId],
105
+ );
106
+
107
+ const refresh = useCallback(async () => await load(false), [load]);
108
+
109
+ useEffect(() => {
110
+ mountedRef.current = true;
111
+ if (!enabled) {
112
+ cancelLoad();
113
+ setState(emptyState(workspaceId, false));
114
+ return;
115
+ }
116
+ void load(true);
117
+ return () => {
118
+ mountedRef.current = false;
119
+ cancelLoad();
120
+ };
121
+ }, [cancelLoad, enabled, load, workspaceId]);
122
+
123
+ useEffect(() => {
124
+ if (!enabled || !pageLive) return;
125
+ const timer = setInterval(() => {
126
+ if (!requestRef.current.controller) void load(false);
127
+ }, pollIntervalMs);
128
+ return () => clearInterval(timer);
129
+ }, [enabled, load, pageLive, pollIntervalMs]);
130
+
131
+ useEffect(() => {
132
+ if (!enabled || !options.sessionId) return;
133
+ return registerSessionReconciler(
134
+ options.sessionId,
135
+ `computer-sessions:${workspaceId}`,
136
+ refresh,
137
+ );
138
+ }, [enabled, options.sessionId, refresh, registerSessionReconciler, workspaceId]);
139
+
140
+ const create = useCallback(
141
+ async (
142
+ request: Omit<CreateComputerSessionRequest, "operationId"> & {
143
+ operationId?: string;
144
+ },
145
+ ): Promise<ComputerSessionMutationResponse> => {
146
+ const response = await client.createComputerSession(workspaceId, {
147
+ ...request,
148
+ operationId: request.operationId ?? crypto.randomUUID(),
149
+ });
150
+ setState((current) => mergeSession(current, workspaceId, response.session));
151
+ return response;
152
+ },
153
+ [client, workspaceId],
154
+ );
155
+
156
+ const end = useCallback(
157
+ async (
158
+ computerSessionId: string,
159
+ operationId: string = crypto.randomUUID(),
160
+ ): Promise<ComputerSessionMutationResponse> => {
161
+ const response = await client.endComputerSession(workspaceId, computerSessionId, {
162
+ operationId,
163
+ });
164
+ setState((current) => mergeSession(current, workspaceId, response.session));
165
+ return response;
166
+ },
167
+ [client, workspaceId],
168
+ );
169
+
170
+ const relevantSessions = useMemo(
171
+ () =>
172
+ options.sessionId
173
+ ? visible.sessions.filter((session) =>
174
+ session.associations.some((association) => association.sessionId === options.sessionId),
175
+ )
176
+ : [],
177
+ [options.sessionId, visible.sessions],
178
+ );
179
+
180
+ return {
181
+ revision: visible.revision,
182
+ sessions: visible.sessions,
183
+ relevantSessions,
184
+ loading: visible.loading,
185
+ refreshing: visible.refreshing,
186
+ error: visible.error,
187
+ refresh,
188
+ create,
189
+ end,
190
+ };
191
+ }
192
+
193
+ type ComputerRegistryState = {
194
+ workspaceId: string;
195
+ revision: number;
196
+ sessions: ComputerSession[];
197
+ loading: boolean;
198
+ refreshing: boolean;
199
+ error: Error | null;
200
+ };
201
+
202
+ function emptyState(workspaceId: string, loading: boolean): ComputerRegistryState {
203
+ return {
204
+ workspaceId,
205
+ revision: 0,
206
+ sessions: [],
207
+ loading,
208
+ refreshing: false,
209
+ error: null,
210
+ };
211
+ }
212
+
213
+ function sortComputerSessions(sessions: readonly ComputerSession[]): ComputerSession[] {
214
+ return [...sessions].sort((left, right) => {
215
+ const live = Number(isLiveComputer(right)) - Number(isLiveComputer(left));
216
+ return (
217
+ live ||
218
+ timestamp(right.lastUsedAt) - timestamp(left.lastUsedAt) ||
219
+ left.id.localeCompare(right.id)
220
+ );
221
+ });
222
+ }
223
+
224
+ function isLiveComputer(session: ComputerSession): boolean {
225
+ return !["ending", "ended", "failed", "lost"].includes(session.lifecycle);
226
+ }
227
+
228
+ function timestamp(value: string): number {
229
+ const parsed = Date.parse(value);
230
+ return Number.isFinite(parsed) ? parsed : 0;
231
+ }
232
+
233
+ function mergeSession(
234
+ current: ComputerRegistryState,
235
+ workspaceId: string,
236
+ session: ComputerSession,
237
+ ): ComputerRegistryState {
238
+ if (current.workspaceId !== workspaceId) return current;
239
+ const sessions = current.sessions.filter((candidate) => candidate.id !== session.id);
240
+ sessions.push(session);
241
+ return { ...current, sessions: sortComputerSessions(sessions), error: null };
242
+ }
243
+
244
+ /** Exported for narrow proxy implementations and tests. */
245
+ export type ComputerSessionsClient = EmbeddedComputerInteractionClientLike;