@opengeni/react 1.4.0 → 1.4.4

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 (218) hide show
  1. package/README.md +8 -0
  2. package/dist/accounts.js +2 -0
  3. package/dist/accounts.js.map +1 -1
  4. package/dist/artifacts-document.js +5 -4
  5. package/dist/artifacts-presentation.js +5 -4
  6. package/dist/artifacts-spreadsheet.js +4 -3
  7. package/dist/artifacts.js +14 -13
  8. package/dist/artifacts.js.map +1 -1
  9. package/dist/browser-viewer-5EEAS3IU.js +17 -0
  10. package/dist/chat-interactive-block-ZFHOKTMS.js +12 -0
  11. package/dist/{chunk-WEHMPOXK.js → chunk-2DDJ6YH6.js} +3 -1
  12. package/dist/{chunk-WEHMPOXK.js.map → chunk-2DDJ6YH6.js.map} +1 -1
  13. package/dist/{chunk-C5X5AJXK.js → chunk-2HVLQ26H.js} +5 -4
  14. package/dist/{chunk-C5X5AJXK.js.map → chunk-2HVLQ26H.js.map} +1 -1
  15. package/dist/{chunk-HDJWDK7W.js → chunk-2OQA3CPB.js} +300 -166
  16. package/dist/{chunk-HDJWDK7W.js.map → chunk-2OQA3CPB.js.map} +1 -1
  17. package/dist/{chunk-A4UJB2MM.js → chunk-32CGLXW2.js} +4 -3
  18. package/dist/{chunk-A4UJB2MM.js.map → chunk-32CGLXW2.js.map} +1 -1
  19. package/dist/{chunk-5CVXZA3Y.js → chunk-45NUOSXQ.js} +3 -2
  20. package/dist/{chunk-5CVXZA3Y.js.map → chunk-45NUOSXQ.js.map} +1 -1
  21. package/dist/{chunk-WUISZ3WU.js → chunk-53KJBGPZ.js} +5 -4
  22. package/dist/{chunk-WUISZ3WU.js.map → chunk-53KJBGPZ.js.map} +1 -1
  23. package/dist/{chunk-MBMPHOMA.js → chunk-55DXD22O.js} +3 -2
  24. package/dist/{chunk-MBMPHOMA.js.map → chunk-55DXD22O.js.map} +1 -1
  25. package/dist/{chunk-JT4JW7BC.js → chunk-5I4VRWBB.js} +3 -1
  26. package/dist/{chunk-JT4JW7BC.js.map → chunk-5I4VRWBB.js.map} +1 -1
  27. package/dist/{chunk-UK4TINKT.js → chunk-B27E3V37.js} +3 -1
  28. package/dist/{chunk-UK4TINKT.js.map → chunk-B27E3V37.js.map} +1 -1
  29. package/dist/{chunk-J6DBS4T3.js → chunk-BNKV5ZLF.js} +5 -4
  30. package/dist/{chunk-J6DBS4T3.js.map → chunk-BNKV5ZLF.js.map} +1 -1
  31. package/dist/{chunk-YM2DE7BQ.js → chunk-C5BF4L26.js} +13 -12
  32. package/dist/{chunk-YM2DE7BQ.js.map → chunk-C5BF4L26.js.map} +1 -1
  33. package/dist/{chunk-YBXQDQFX.js → chunk-C7CMD56T.js} +6 -5
  34. package/dist/{chunk-YBXQDQFX.js.map → chunk-C7CMD56T.js.map} +1 -1
  35. package/dist/{chunk-UGLEYEMP.js → chunk-CM7LVUKC.js} +3 -1
  36. package/dist/{chunk-UGLEYEMP.js.map → chunk-CM7LVUKC.js.map} +1 -1
  37. package/dist/{chunk-JVYQRRC4.js → chunk-CVYCLFMH.js} +3 -1
  38. package/dist/{chunk-JVYQRRC4.js.map → chunk-CVYCLFMH.js.map} +1 -1
  39. package/dist/{chunk-LJ2UTJJE.js → chunk-EAOSEIXB.js} +182 -97
  40. package/dist/chunk-EAOSEIXB.js.map +1 -0
  41. package/dist/{chunk-Y2PRMYLC.js → chunk-G3U2A6GS.js} +3 -2
  42. package/dist/{chunk-Y2PRMYLC.js.map → chunk-G3U2A6GS.js.map} +1 -1
  43. package/dist/{chunk-WZMYFJD3.js → chunk-G6YRCIYJ.js} +16 -7
  44. package/dist/{chunk-WZMYFJD3.js.map → chunk-G6YRCIYJ.js.map} +1 -1
  45. package/dist/{chunk-B3V6TG77.js → chunk-GC3PH6N7.js} +3 -1
  46. package/dist/{chunk-B3V6TG77.js.map → chunk-GC3PH6N7.js.map} +1 -1
  47. package/dist/{chunk-CJUO7J5Z.js → chunk-HXYOSSQO.js} +3 -2
  48. package/dist/{chunk-CJUO7J5Z.js.map → chunk-HXYOSSQO.js.map} +1 -1
  49. package/dist/{chunk-GYVRFZDY.js → chunk-I6OY3MWC.js} +310 -115
  50. package/dist/chunk-I6OY3MWC.js.map +1 -0
  51. package/dist/{chunk-BRM6FVZA.js → chunk-IEE7WCLN.js} +4 -3
  52. package/dist/{chunk-BRM6FVZA.js.map → chunk-IEE7WCLN.js.map} +1 -1
  53. package/dist/{chunk-DYWBUX5Q.js → chunk-IMSUVYQB.js} +6 -5
  54. package/dist/{chunk-DYWBUX5Q.js.map → chunk-IMSUVYQB.js.map} +1 -1
  55. package/dist/{chunk-LN6TCXD3.js → chunk-IUHHPMAX.js} +184 -48
  56. package/dist/chunk-IUHHPMAX.js.map +1 -0
  57. package/dist/{chunk-SGKD2OCN.js → chunk-IYQCWGU5.js} +6 -5
  58. package/dist/{chunk-SGKD2OCN.js.map → chunk-IYQCWGU5.js.map} +1 -1
  59. package/dist/{chunk-J2QWVSQY.js → chunk-JH7V627O.js} +8 -7
  60. package/dist/{chunk-J2QWVSQY.js.map → chunk-JH7V627O.js.map} +1 -1
  61. package/dist/{chunk-EESD3PY3.js → chunk-JHTIMBOL.js} +6 -5
  62. package/dist/{chunk-EESD3PY3.js.map → chunk-JHTIMBOL.js.map} +1 -1
  63. package/dist/{chunk-LNIRAOA3.js → chunk-L2J3PU7A.js} +7 -6
  64. package/dist/{chunk-LNIRAOA3.js.map → chunk-L2J3PU7A.js.map} +1 -1
  65. package/dist/{chunk-ZYRGEKWS.js → chunk-LJUMYUBO.js} +3 -1
  66. package/dist/{chunk-ZYRGEKWS.js.map → chunk-LJUMYUBO.js.map} +1 -1
  67. package/dist/{chunk-4RVYOY4O.js → chunk-LSW2OJVV.js} +4 -3
  68. package/dist/{chunk-4RVYOY4O.js.map → chunk-LSW2OJVV.js.map} +1 -1
  69. package/dist/{chunk-UTRTG6LH.js → chunk-LVS3MH5T.js} +3 -1
  70. package/dist/{chunk-UTRTG6LH.js.map → chunk-LVS3MH5T.js.map} +1 -1
  71. package/dist/chunk-M357MHH3.js +2 -0
  72. package/dist/{chunk-WRHIZYZF.js → chunk-MEQM4LI3.js} +6 -5
  73. package/dist/{chunk-WRHIZYZF.js.map → chunk-MEQM4LI3.js.map} +1 -1
  74. package/dist/chunk-NX4ADH73.js +14 -0
  75. package/dist/chunk-NX4ADH73.js.map +1 -0
  76. package/dist/{chunk-XCHWDJ22.js → chunk-OGTTUYNT.js} +3 -1
  77. package/dist/{chunk-XCHWDJ22.js.map → chunk-OGTTUYNT.js.map} +1 -1
  78. package/dist/{chunk-GIMRELBW.js → chunk-OYQQJL3J.js} +3 -1
  79. package/dist/{chunk-GIMRELBW.js.map → chunk-OYQQJL3J.js.map} +1 -1
  80. package/dist/{chunk-RSI7XYXL.js → chunk-PBCZL4JP.js} +6 -5
  81. package/dist/{chunk-RSI7XYXL.js.map → chunk-PBCZL4JP.js.map} +1 -1
  82. package/dist/{chunk-I7IWRQLN.js → chunk-PHBFJCEU.js} +17 -11
  83. package/dist/chunk-PHBFJCEU.js.map +1 -0
  84. package/dist/{chunk-3CTRFMAK.js → chunk-PKCAYHDC.js} +5 -4
  85. package/dist/{chunk-3CTRFMAK.js.map → chunk-PKCAYHDC.js.map} +1 -1
  86. package/dist/{chunk-K3HQAKQX.js → chunk-PMV53IMM.js} +3 -1
  87. package/dist/{chunk-K3HQAKQX.js.map → chunk-PMV53IMM.js.map} +1 -1
  88. package/dist/{chunk-YQEAA6LT.js → chunk-QHUE7E4N.js} +3 -2
  89. package/dist/{chunk-YQEAA6LT.js.map → chunk-QHUE7E4N.js.map} +1 -1
  90. package/dist/{chunk-OA6HFRQP.js → chunk-QMMCJGFI.js} +4 -3
  91. package/dist/{chunk-OA6HFRQP.js.map → chunk-QMMCJGFI.js.map} +1 -1
  92. package/dist/{chunk-YV4L3ILF.js → chunk-QV3DJVRZ.js} +3 -1
  93. package/dist/{chunk-YV4L3ILF.js.map → chunk-QV3DJVRZ.js.map} +1 -1
  94. package/dist/{chunk-447JLCBU.js → chunk-S2QH6EZ5.js} +3 -1
  95. package/dist/{chunk-447JLCBU.js.map → chunk-S2QH6EZ5.js.map} +1 -1
  96. package/dist/{chunk-PNY3JKBJ.js → chunk-SFH43JT2.js} +5 -4
  97. package/dist/{chunk-PNY3JKBJ.js.map → chunk-SFH43JT2.js.map} +1 -1
  98. package/dist/{chunk-AWZLIMKX.js → chunk-T46QZH3E.js} +3 -1
  99. package/dist/{chunk-AWZLIMKX.js.map → chunk-T46QZH3E.js.map} +1 -1
  100. package/dist/{chunk-5J4FOW6Q.js → chunk-TNEZNMSN.js} +3 -1
  101. package/dist/{chunk-5J4FOW6Q.js.map → chunk-TNEZNMSN.js.map} +1 -1
  102. package/dist/{chunk-22KP6LR5.js → chunk-TXS65BB2.js} +3 -1
  103. package/dist/{chunk-22KP6LR5.js.map → chunk-TXS65BB2.js.map} +1 -1
  104. package/dist/{chunk-H5X55XH7.js → chunk-UDPH5SLV.js} +4 -3
  105. package/dist/{chunk-H5X55XH7.js.map → chunk-UDPH5SLV.js.map} +1 -1
  106. package/dist/{chunk-VZTQ73XT.js → chunk-XEA5BCMA.js} +3 -1
  107. package/dist/{chunk-VZTQ73XT.js.map → chunk-XEA5BCMA.js.map} +1 -1
  108. package/dist/{chunk-5F4WR7X7.js → chunk-XS67YG5R.js} +3 -1
  109. package/dist/{chunk-5F4WR7X7.js.map → chunk-XS67YG5R.js.map} +1 -1
  110. package/dist/{chunk-TKOTKRWY.js → chunk-XTD7TZHW.js} +3 -1
  111. package/dist/{chunk-TKOTKRWY.js.map → chunk-XTD7TZHW.js.map} +1 -1
  112. package/dist/{chunk-A2PI6UMY.js → chunk-YSRBFJMQ.js} +3 -1
  113. package/dist/{chunk-A2PI6UMY.js.map → chunk-YSRBFJMQ.js.map} +1 -1
  114. package/dist/chunk-YVFRBXST.js +1420 -0
  115. package/dist/chunk-YVFRBXST.js.map +1 -0
  116. package/dist/{chunk-SCOTPE2I.js → chunk-YYLBXXTE.js} +3 -1
  117. package/dist/{chunk-SCOTPE2I.js.map → chunk-YYLBXXTE.js.map} +1 -1
  118. package/dist/{chunk-RHREZ7BO.js → chunk-ZUWF4NF7.js} +3 -1
  119. package/dist/{chunk-RHREZ7BO.js.map → chunk-ZUWF4NF7.js.map} +1 -1
  120. package/dist/clipboard.js +3 -2
  121. package/dist/components/composer-transcription-control.d.ts +4 -0
  122. package/dist/components/session-conversation.d.ts +6 -0
  123. package/dist/composer-voice-events.d.ts +8 -0
  124. package/dist/composer.js +15 -13
  125. package/dist/computer-viewer-ELNLZGHI.js +20 -0
  126. package/dist/connect.js +15 -12
  127. package/dist/connect.js.map +1 -1
  128. package/dist/desktop.js +6 -5
  129. package/dist/desktop.js.map +1 -1
  130. package/dist/diffs.js +2 -1
  131. package/dist/diffs.js.map +1 -1
  132. package/dist/editor.js +4 -3
  133. package/dist/editor.js.map +1 -1
  134. package/dist/embedded-voice-NIL4RDX3.js +69 -0
  135. package/dist/embedded-voice-NIL4RDX3.js.map +1 -0
  136. package/dist/{fleet-decision-row-YZ3ZKWMM.js → fleet-decision-row-SI63O5V4.js} +3 -1
  137. package/dist/{fleet-decision-row-YZ3ZKWMM.js.map → fleet-decision-row-SI63O5V4.js.map} +1 -1
  138. package/dist/hooks/use-client-config-flags.d.ts +6 -0
  139. package/dist/hooks/use-computer-session.d.ts +2 -0
  140. package/dist/hooks/use-realtime-voice-models.d.ts +9 -0
  141. package/dist/hooks/use-voice-input.d.ts +8 -1
  142. package/dist/index.js +47 -45
  143. package/dist/index.js.map +1 -1
  144. package/dist/interaction.js +14 -13
  145. package/dist/interaction.js.map +1 -1
  146. package/dist/{latest-question-GPYM2JNU.js → latest-question-2POFPYNB.js} +5 -4
  147. package/dist/{latest-question-GPYM2JNU.js.map → latest-question-2POFPYNB.js.map} +1 -1
  148. package/dist/machines.js +8 -7
  149. package/dist/{markdown-table-layout-QXSLK4BY.js → markdown-table-layout-DQ4DRNVT.js} +3 -1
  150. package/dist/{markdown-table-layout-QXSLK4BY.js.map → markdown-table-layout-DQ4DRNVT.js.map} +1 -1
  151. package/dist/model-policy-picker-menu-WRNSRFQC.js +15 -0
  152. package/dist/model-policy.js +2 -1
  153. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js → noto-sans-arabic-loader-XSH2KYWJ.js} +3 -1
  154. package/dist/{noto-sans-arabic-loader-IA2T4X2A.js.map → noto-sans-arabic-loader-XSH2KYWJ.js.map} +1 -1
  155. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js → noto-sans-jp-loader.generated-MR5L6V7K.js} +3 -1
  156. package/dist/{noto-sans-jp-loader.generated-HFGLYBR3.js.map → noto-sans-jp-loader.generated-MR5L6V7K.js.map} +1 -1
  157. package/dist/{platform-activity-row-627BFZ46.js → platform-activity-row-PJJVATTZ.js} +3 -1
  158. package/dist/{platform-activity-row-627BFZ46.js.map → platform-activity-row-PJJVATTZ.js.map} +1 -1
  159. package/dist/{queue-surface-implementation-ZHFI5JBP.js → queue-surface-implementation-67FS7FUI.js} +10 -9
  160. package/dist/{queue-surface-implementation-ZHFI5JBP.js.map → queue-surface-implementation-67FS7FUI.js.map} +1 -1
  161. package/dist/realtime/embedded-voice.d.ts +20 -0
  162. package/dist/realtime.js +22 -1353
  163. package/dist/realtime.js.map +1 -1
  164. package/dist/sandbox-files-34CJJNOY.js +21 -0
  165. package/dist/session-ui.js +32 -30
  166. package/dist/session-ui.js.map +1 -1
  167. package/dist/session.js +10 -9
  168. package/dist/sites.js +2 -1
  169. package/dist/terminal.js +6 -5
  170. package/dist/terminal.js.map +1 -1
  171. package/dist/{timeline-annotation-markers-GNDHTGBD.js → timeline-annotation-markers-U3G66KUD.js} +5 -4
  172. package/dist/{timeline-annotation-markers-GNDHTGBD.js.map → timeline-annotation-markers-U3G66KUD.js.map} +1 -1
  173. package/dist/{timeline-annotation-selection-DMLMM2TX.js → timeline-annotation-selection-BI7QTPTZ.js} +4 -3
  174. package/dist/{timeline-annotation-selection-DMLMM2TX.js.map → timeline-annotation-selection-BI7QTPTZ.js.map} +1 -1
  175. package/dist/{timeline-annotations-dialog-ELRDBHTS.js → timeline-annotations-dialog-FOMHSDTE.js} +7 -6
  176. package/dist/{timeline-annotations-dialog-ELRDBHTS.js.map → timeline-annotations-dialog-FOMHSDTE.js.map} +1 -1
  177. package/dist/usage.js +6 -5
  178. package/dist/usage.js.map +1 -1
  179. package/dist/voice-recording-owner.d.ts +7 -0
  180. package/package.json +3 -3
  181. package/src/components/composer-transcription-control.tsx +109 -27
  182. package/src/components/computer-viewer.tsx +139 -30
  183. package/src/components/markdown.tsx +7 -2
  184. package/src/components/message-timeline.tsx +10 -1
  185. package/src/components/session-conversation.tsx +44 -0
  186. package/src/components/session-list.tsx +1 -1
  187. package/src/components/session-proxy-scope.tsx +26 -5
  188. package/src/composer-voice-events.ts +12 -0
  189. package/src/connection-discovery.tsx +4 -1
  190. package/src/hooks/use-browser-session.ts +445 -168
  191. package/src/hooks/use-client-config-flags.ts +8 -0
  192. package/src/hooks/use-computer-session.ts +334 -98
  193. package/src/hooks/use-goal.ts +12 -1
  194. package/src/hooks/use-realtime-voice-models.ts +67 -0
  195. package/src/hooks/use-voice-input.ts +109 -18
  196. package/src/realtime/embedded-voice.tsx +81 -0
  197. package/src/realtime/realtime-control.tsx +82 -17
  198. package/src/voice-recording-owner.ts +22 -0
  199. package/styles/compiled.css +77 -18
  200. package/styles/index.css +49 -0
  201. package/styles/responsive.css +61 -4
  202. package/styles/session-capability.css +1 -1
  203. package/dist/browser-viewer-SHYKQRNX.js +0 -16
  204. package/dist/chat-interactive-block-UGBTNB3L.js +0 -11
  205. package/dist/chunk-GYVRFZDY.js.map +0 -1
  206. package/dist/chunk-I7IWRQLN.js.map +0 -1
  207. package/dist/chunk-IWIDV7SB.js +0 -1
  208. package/dist/chunk-LJ2UTJJE.js.map +0 -1
  209. package/dist/chunk-LN6TCXD3.js.map +0 -1
  210. package/dist/computer-viewer-6SRGWRUX.js +0 -19
  211. package/dist/model-policy-picker-menu-DXHUL73H.js +0 -14
  212. package/dist/sandbox-files-GORZOUUJ.js +0 -20
  213. /package/dist/{browser-viewer-SHYKQRNX.js.map → browser-viewer-5EEAS3IU.js.map} +0 -0
  214. /package/dist/{chat-interactive-block-UGBTNB3L.js.map → chat-interactive-block-ZFHOKTMS.js.map} +0 -0
  215. /package/dist/{chunk-IWIDV7SB.js.map → chunk-M357MHH3.js.map} +0 -0
  216. /package/dist/{computer-viewer-6SRGWRUX.js.map → computer-viewer-ELNLZGHI.js.map} +0 -0
  217. /package/dist/{model-policy-picker-menu-DXHUL73H.js.map → model-policy-picker-menu-WRNSRFQC.js.map} +0 -0
  218. /package/dist/{sandbox-files-GORZOUUJ.js.map → sandbox-files-34CJJNOY.js.map} +0 -0
@@ -1,3 +1,4 @@
1
+ "use client";
1
2
  import {
2
3
  InteractionInterventionBanner,
3
4
  isInteractionControlUnavailable,
@@ -6,16 +7,16 @@ import {
6
7
  useInteractionInterventions,
7
8
  useInteractionInvalidation,
8
9
  useViewerMenuDismiss
9
- } from "./chunk-J6DBS4T3.js";
10
+ } from "./chunk-BNKV5ZLF.js";
10
11
  import {
11
12
  copyTextToClipboard
12
- } from "./chunk-JVYQRRC4.js";
13
+ } from "./chunk-CVYCLFMH.js";
13
14
  import {
14
15
  interactionHostPlatform
15
- } from "./chunk-RHREZ7BO.js";
16
+ } from "./chunk-ZUWF4NF7.js";
16
17
  import {
17
18
  formatBytes
18
- } from "./chunk-UTRTG6LH.js";
19
+ } from "./chunk-LVS3MH5T.js";
19
20
  import {
20
21
  STREAM_CLOSE_REASON_NORMAL,
21
22
  STREAM_KIND_BROWSER,
@@ -24,16 +25,16 @@ import {
24
25
  decodeStreamOpenAck,
25
26
  encodeStreamClose,
26
27
  encodeStreamOpen
27
- } from "./chunk-5J4FOW6Q.js";
28
+ } from "./chunk-TNEZNMSN.js";
28
29
  import {
29
30
  usePageLiveActivity
30
- } from "./chunk-YQEAA6LT.js";
31
+ } from "./chunk-QHUE7E4N.js";
31
32
  import {
32
33
  useEmbeddedBrowserInteraction
33
- } from "./chunk-TKOTKRWY.js";
34
+ } from "./chunk-XTD7TZHW.js";
34
35
  import {
35
36
  cn
36
- } from "./chunk-22KP6LR5.js";
37
+ } from "./chunk-TXS65BB2.js";
37
38
 
38
39
  // src/components/browser-viewer.tsx
39
40
  import { interactionControlFailureFromError } from "@opengeni/sdk/interaction";
@@ -866,6 +867,11 @@ function useBrowserSession(options) {
866
867
  const { client, workspaceId } = useEmbeddedBrowserInteraction(options);
867
868
  const browserSessionId = options.browserSessionId;
868
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;
869
875
  const semanticObservationEnabled = options.semanticObservationEnabled ?? true;
870
876
  const semanticObservationEnabledRef = useRef5(semanticObservationEnabled);
871
877
  semanticObservationEnabledRef.current = semanticObservationEnabled;
@@ -875,13 +881,15 @@ function useBrowserSession(options) {
875
881
  });
876
882
  const pageLive = usePageLiveActivity();
877
883
  const pollIntervalMs = Math.max(750, options.pollIntervalMs ?? 2e3);
878
- const [state, setState] = useState5(() => emptyState4(browserSessionId, enabled));
879
- 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 };
880
886
  const refreshBlocked = isNonRetryableInteractionError(visible.error);
881
887
  const inputFailureRef = useRef5(visible.inputFailure);
882
888
  inputFailureRef.current = visible.inputFailure;
883
889
  const selectedTargetIdRef = useRef5(visible.selectedTargetId);
884
890
  selectedTargetIdRef.current = visible.selectedTargetId;
891
+ const selectionRevisionRef = useRef5(0);
892
+ const actionResultOrderRef = useRef5({ next: 0, settled: 0 });
885
893
  const selectedTargetRef = useRef5(
886
894
  visible.targets.find((target) => target.id === visible.selectedTargetId) ?? null
887
895
  );
@@ -902,17 +910,23 @@ function useBrowserSession(options) {
902
910
  });
903
911
  const mutationRef = useRef5({
904
912
  browserSessionId,
905
- count: 0
913
+ source,
914
+ count: 0,
915
+ needsInventoryReconcile: false
906
916
  });
907
917
  const mountedRef = useRef5(true);
918
+ const isCurrentSource = useCallback5(
919
+ () => mountedRef.current && sourceRef.current === source,
920
+ [source]
921
+ );
908
922
  const invalidateRefresh = useCallback5(() => {
909
923
  const id = requestRef.current.id + 1;
910
924
  requestRef.current.controller?.abort();
911
925
  requestRef.current = { id, controller: null };
912
926
  }, []);
913
927
  const refreshInventory = useCallback5(
914
- async (checkInput = false) => {
915
- if (!enabled || !browserSessionId) return;
928
+ async (checkInput = false, observeSelectedTarget = true) => {
929
+ if (!enabled || !browserSessionId || !isCurrentSource()) return;
916
930
  const checkedInputFailure = inputFailureRef.current;
917
931
  const id = requestRef.current.id + 1;
918
932
  requestRef.current.controller?.abort();
@@ -927,14 +941,18 @@ function useBrowserSession(options) {
927
941
  signal: controller.signal
928
942
  })
929
943
  ]);
930
- if (!mountedRef.current || requestRef.current.id !== id) return;
944
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
931
945
  const selected = chooseTarget(targetResponse.targets, selectedTargetIdRef.current);
932
946
  selectedTargetIdRef.current = selected?.id ?? null;
947
+ selectedTargetRef.current = selected;
933
948
  const previousObservation = observationRef.current.browserSessionId === browserSessionId ? observationRef.current.observation : null;
934
949
  const retainedObservation = selected && previousObservation && sameObservationTarget(previousObservation, selected) ? previousObservation : null;
935
- observationRef.current = { browserSessionId, observation: retainedObservation };
950
+ observationRef.current = {
951
+ browserSessionId,
952
+ observation: retainedObservation
953
+ };
936
954
  setState(
937
- (current) => current.browserSessionId === browserSessionId ? {
955
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
938
956
  ...current,
939
957
  session,
940
958
  targets: targetResponse.targets,
@@ -947,7 +965,7 @@ function useBrowserSession(options) {
947
965
  inputFailure: checkInput && session.id === browserSessionId && targetResponse.browserSessionId === browserSessionId && current.inputFailure === checkedInputFailure ? null : current.inputFailure
948
966
  } : current
949
967
  );
950
- if (!selected || !semanticObservationEnabledRef.current) return;
968
+ if (!selected || !observeSelectedTarget || !semanticObservationEnabledRef.current) return;
951
969
  try {
952
970
  const observation = await client.observeBrowserTarget(
953
971
  workspaceId,
@@ -955,22 +973,29 @@ function useBrowserSession(options) {
955
973
  selected.id,
956
974
  { signal: controller.signal }
957
975
  );
958
- if (!mountedRef.current || requestRef.current.id !== id) return;
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;
959
981
  observationRef.current = { browserSessionId, observation };
960
982
  lastObservationAtRef.current = Date.now();
961
983
  setState(
962
- (current) => current.browserSessionId === browserSessionId && current.selectedTargetId === observation.target.id ? { ...current, observation } : current
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
963
989
  );
964
990
  } catch (cause) {
965
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
991
+ if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id)
966
992
  return;
967
993
  void cause;
968
994
  }
969
995
  } catch (cause) {
970
- if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
971
- return;
996
+ if (controller.signal.aborted || !isCurrentSource() || requestRef.current.id !== id) return;
972
997
  setState(
973
- (current) => current.browserSessionId === browserSessionId ? {
998
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? {
974
999
  ...current,
975
1000
  loading: false,
976
1001
  error: cause instanceof Error ? cause : new Error(String(cause))
@@ -982,11 +1007,11 @@ function useBrowserSession(options) {
982
1007
  }
983
1008
  }
984
1009
  },
985
- [browserSessionId, client, enabled, workspaceId]
1010
+ [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]
986
1011
  );
987
1012
  const refresh = useCallback5(async () => await refreshInventory(true), [refreshInventory]);
988
1013
  const refreshSemantic = useCallback5(async () => {
989
- if (!enabled || !browserSessionId) return;
1014
+ if (!enabled || !browserSessionId || !isCurrentSource()) return;
990
1015
  const target = selectedTargetRef.current;
991
1016
  if (!target) return;
992
1017
  const id = requestRef.current.id + 1;
@@ -1000,34 +1025,34 @@ function useBrowserSession(options) {
1000
1025
  target.id,
1001
1026
  { signal: controller.signal }
1002
1027
  );
1003
- if (!mountedRef.current || requestRef.current.id !== id) return;
1028
+ if (!isCurrentSource() || requestRef.current.id !== id) return;
1004
1029
  if (!sameObservationTarget(observation, selectedTargetRef.current)) return;
1005
1030
  observationRef.current = { browserSessionId, observation };
1006
1031
  lastObservationAtRef.current = Date.now();
1007
1032
  setState(
1008
- (current) => current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
1033
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId && current.selectedTargetId === target.id ? { ...current, observation } : current
1009
1034
  );
1010
1035
  } catch {
1011
1036
  } finally {
1012
1037
  if (requestRef.current.id === id) requestRef.current = { id, controller: null };
1013
1038
  }
1014
- }, [browserSessionId, client, enabled, workspaceId]);
1039
+ }, [browserSessionId, client, enabled, isCurrentSource, source, workspaceId]);
1015
1040
  useEffect5(() => {
1016
1041
  mountedRef.current = true;
1017
1042
  if (!enabled) {
1018
1043
  invalidateRefresh();
1019
- setState(emptyState4(browserSessionId, false));
1044
+ setState({ ...emptyState4(browserSessionId, false), source });
1020
1045
  return;
1021
1046
  }
1022
1047
  setState(
1023
- (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 }
1024
1049
  );
1025
1050
  void refreshInventory();
1026
1051
  return () => {
1027
1052
  mountedRef.current = false;
1028
1053
  invalidateRefresh();
1029
1054
  };
1030
- }, [browserSessionId, enabled, invalidateRefresh, refreshInventory]);
1055
+ }, [browserSessionId, enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]);
1031
1056
  useEffect5(() => {
1032
1057
  const previous = previousSemanticObservationRef.current;
1033
1058
  previousSemanticObservationRef.current = {
@@ -1041,11 +1066,19 @@ function useBrowserSession(options) {
1041
1066
  }
1042
1067
  observationRef.current = { browserSessionId, observation: null };
1043
1068
  setState(
1044
- (current) => current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
1069
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === browserSessionId ? { ...current, observation: null } : current
1045
1070
  );
1046
1071
  void refreshSemantic();
1047
1072
  }
1048
- }, [browserSessionId, enabled, pollIntervalMs, refreshSemantic, semanticObservationEnabled]);
1073
+ }, [
1074
+ browserSessionId,
1075
+ enabled,
1076
+ isCurrentSource,
1077
+ pollIntervalMs,
1078
+ refreshSemantic,
1079
+ semanticObservationEnabled,
1080
+ source
1081
+ ]);
1049
1082
  useEffect5(() => {
1050
1083
  if (!enabled || !pageLive || visible.mutating || refreshBlocked) return;
1051
1084
  const timer = setInterval(() => {
@@ -1057,6 +1090,7 @@ function useBrowserSession(options) {
1057
1090
  if (!enabled || !browserSessionId || !pageLive || refreshBlocked) return;
1058
1091
  let disposed = false;
1059
1092
  const heartbeat = () => {
1093
+ if (!isCurrentSource()) return;
1060
1094
  void client.heartbeatBrowserSession(workspaceId, browserSessionId).catch(() => {
1061
1095
  if (!disposed) void refreshInventory();
1062
1096
  });
@@ -1066,151 +1100,216 @@ function useBrowserSession(options) {
1066
1100
  disposed = true;
1067
1101
  clearInterval(timer);
1068
1102
  };
1069
- }, [browserSessionId, client, enabled, pageLive, refreshInventory, refreshBlocked, workspaceId]);
1103
+ }, [
1104
+ browserSessionId,
1105
+ client,
1106
+ enabled,
1107
+ isCurrentSource,
1108
+ pageLive,
1109
+ refreshInventory,
1110
+ refreshBlocked,
1111
+ workspaceId
1112
+ ]);
1070
1113
  const runMutation = useCallback5(
1071
- 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;
1072
1119
  invalidateRefresh();
1073
- if (mutationRef.current.browserSessionId !== scopeBrowserSessionId) {
1120
+ const readRequestId = requestRef.current.id;
1121
+ if (mutationRef.current.browserSessionId !== scopeBrowserSessionId || mutationRef.current.source !== source) {
1074
1122
  mutationRef.current = {
1075
1123
  browserSessionId: scopeBrowserSessionId,
1076
- count: 0
1124
+ source,
1125
+ count: 0,
1126
+ needsInventoryReconcile: false
1077
1127
  };
1078
1128
  }
1079
1129
  mutationRef.current.count += 1;
1080
1130
  setState(
1081
- (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
1082
1132
  );
1083
1133
  try {
1084
- return await operation();
1134
+ return await operation(selectionRevision, readRequestId);
1085
1135
  } catch (cause) {
1086
1136
  const error = cause instanceof Error ? cause : new Error(String(cause));
1087
- setState(
1088
- (current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, error } : current
1089
- );
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
+ }
1090
1142
  throw error;
1091
1143
  } finally {
1092
- 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
+ }
1093
1148
  mutationRef.current.count = Math.max(0, mutationRef.current.count - 1);
1094
1149
  const mutating = mutationRef.current.count > 0;
1095
1150
  setState(
1096
- (current) => current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
1151
+ (current) => isCurrentSource() && current.source === source && current.browserSessionId === scopeBrowserSessionId ? { ...current, mutating } : current
1097
1152
  );
1153
+ if (!mutating && mutationRef.current.needsInventoryReconcile && isCurrentSource()) {
1154
+ mutationRef.current.needsInventoryReconcile = false;
1155
+ void refreshInventory(false, false);
1156
+ }
1098
1157
  }
1099
1158
  }
1100
1159
  },
1101
- [invalidateRefresh]
1160
+ [enabled, invalidateRefresh, isCurrentSource, refreshInventory, source]
1102
1161
  );
1103
1162
  const selectTarget = useCallback5(
1104
1163
  async (targetId) => {
1105
1164
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1106
- return await runMutation(browserSessionId, async () => {
1107
- let targets = visible.targets;
1108
- let target;
1109
- let observation;
1110
- try {
1111
- observation = await client.selectBrowserTarget(workspaceId, browserSessionId, targetId);
1112
- target = observation.target;
1113
- } catch (cause) {
1114
- if (!isMissingBrowserTarget(cause)) throw cause;
1115
- const response = await client.listBrowserTargets(workspaceId, browserSessionId);
1116
- targets = response.targets;
1117
- const fallback = chooseTarget(targets, null);
1118
- if (!fallback) {
1119
- selectedTargetIdRef.current = null;
1120
- observationRef.current = { browserSessionId, observation: null };
1121
- setState(
1122
- (current) => current.browserSessionId === browserSessionId ? {
1123
- ...current,
1124
- targets,
1125
- selectedTargetId: null,
1126
- observation: null
1127
- } : 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
1128
1200
  );
1129
- throw cause;
1201
+ target = observation.target;
1130
1202
  }
1131
- observation = await client.selectBrowserTarget(
1132
- workspaceId,
1133
- browserSessionId,
1134
- 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
1135
1218
  );
1136
- target = observation.target;
1137
- }
1138
- selectedTargetIdRef.current = target.id;
1139
- observationRef.current = { browserSessionId, observation };
1140
- lastObservationAtRef.current = Date.now();
1141
- setState(
1142
- (current) => current.browserSessionId === browserSessionId ? {
1143
- ...current,
1144
- targets: targets.map((candidate) => ({
1145
- ...candidate,
1146
- selected: candidate.id === target.id
1147
- })),
1148
- selectedTargetId: target.id,
1149
- observation
1150
- } : current
1151
- );
1152
- return target;
1153
- });
1219
+ return target;
1220
+ },
1221
+ true
1222
+ );
1154
1223
  },
1155
- [browserSessionId, client, runMutation, visible.targets, workspaceId]
1224
+ [browserSessionId, client, isCurrentSource, runMutation, source, visible.targets, workspaceId]
1156
1225
  );
1157
1226
  const openTarget = useCallback5(
1158
1227
  async (url) => {
1159
1228
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1160
- return await runMutation(browserSessionId, async () => {
1161
- const observation = await client.openBrowserTarget(
1162
- workspaceId,
1163
- browserSessionId,
1164
- url === void 0 ? {} : { url }
1165
- );
1166
- const target = observation.target;
1167
- selectedTargetIdRef.current = target.id;
1168
- observationRef.current = { browserSessionId, observation };
1169
- lastObservationAtRef.current = Date.now();
1170
- setState(
1171
- (current) => current.browserSessionId === browserSessionId ? {
1172
- ...current,
1173
- targets: [
1174
- ...current.targets.map((candidate) => ({
1175
- ...candidate,
1176
- selected: false
1177
- })),
1178
- target
1179
- ],
1180
- selectedTargetId: target.id,
1181
- observation
1182
- } : current
1183
- );
1184
- return target;
1185
- });
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
+ );
1186
1265
  },
1187
- [browserSessionId, client, runMutation, workspaceId]
1266
+ [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1188
1267
  );
1189
1268
  const closeTarget = useCallback5(
1190
1269
  async (targetId) => {
1191
1270
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1192
- await runMutation(browserSessionId, async () => {
1193
- const response = await client.closeBrowserTarget(workspaceId, browserSessionId, targetId);
1194
- const selected = chooseTarget(response.targets, null);
1195
- const observation = selected ? await client.observeBrowserTarget(workspaceId, browserSessionId, selected.id) : null;
1196
- selectedTargetIdRef.current = selected?.id ?? null;
1197
- observationRef.current = { browserSessionId, observation };
1198
- if (observation) lastObservationAtRef.current = Date.now();
1199
- setState(
1200
- (current) => current.browserSessionId === browserSessionId ? {
1201
- ...current,
1202
- targets: response.targets,
1203
- selectedTargetId: selected?.id ?? null,
1204
- observation
1205
- } : current
1206
- );
1207
- });
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
+ );
1208
1301
  },
1209
- [browserSessionId, client, runMutation, workspaceId]
1302
+ [browserSessionId, client, isCurrentSource, runMutation, source, workspaceId]
1210
1303
  );
1211
1304
  const dispatchAction = useCallback5(
1212
1305
  async (action, operationId, frame, observationMode = "none") => {
1213
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
+ }
1214
1313
  if (frame && frame.browserSessionId !== browserSessionId) {
1215
1314
  throw new Error("The displayed browser frame belongs to another BrowserSession.");
1216
1315
  }
@@ -1220,7 +1319,7 @@ function useBrowserSession(options) {
1220
1319
  expectedTargetGeneration: frame.targetGeneration,
1221
1320
  expectedDocumentGeneration: frame.documentGeneration,
1222
1321
  expectedFrameId: frame.frameId
1223
- } : currentObservation ? {
1322
+ } : currentObservation && sameObservationTarget(currentObservation, selectedTargetRef.current) ? {
1224
1323
  targetId: currentObservation.target.id,
1225
1324
  expectedTargetGeneration: currentObservation.target.targetGeneration,
1226
1325
  expectedDocumentGeneration: currentObservation.target.documentGeneration,
@@ -1229,6 +1328,21 @@ function useBrowserSession(options) {
1229
1328
  if (!fence) {
1230
1329
  throw new Error("The browser page is not ready for input.");
1231
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
+ };
1232
1346
  try {
1233
1347
  const receipt = await client.actInBrowser(workspaceId, browserSessionId, {
1234
1348
  operationId,
@@ -1236,40 +1350,49 @@ function useBrowserSession(options) {
1236
1350
  observationMode,
1237
1351
  action
1238
1352
  });
1239
- if (receipt.state !== "completed") {
1240
- const inputFailure = {
1241
- operationId: receipt.operationId,
1242
- state: receipt.state,
1243
- error: receipt.error
1244
- };
1245
- setState(
1246
- (current) => current.browserSessionId === browserSessionId ? { ...current, inputFailure } : current
1247
- );
1248
- }
1249
- 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;
1250
1361
  observationRef.current = {
1251
1362
  browserSessionId,
1252
- observation: receipt.observation
1363
+ observation: receivedObservation
1253
1364
  };
1365
+ selectedTargetRef.current = receivedObservation.target;
1254
1366
  lastObservationAtRef.current = Date.now();
1255
- setState(
1256
- (current) => current.browserSessionId === browserSessionId ? {
1257
- ...current,
1258
- observation: receipt.observation,
1259
- targets: replaceTarget(current.targets, receipt.observation.target)
1260
- } : current
1261
- );
1262
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
+ );
1263
1384
  return receipt;
1264
1385
  } catch (cause) {
1265
1386
  const error = cause instanceof Error ? cause : new Error(String(cause));
1266
- setState(
1267
- (current) => current.browserSessionId === browserSessionId ? { ...current, error } : current
1268
- );
1387
+ if (settleResult()) {
1388
+ setState(
1389
+ (current) => isCurrentResultView() && current.source === source && current.browserSessionId === browserSessionId && current.actionResultOrder <= resultOrder ? { ...current, error, actionResultOrder: resultOrder } : current
1390
+ );
1391
+ }
1269
1392
  throw error;
1270
1393
  }
1271
1394
  },
1272
- [browserSessionId, client, workspaceId]
1395
+ [browserSessionId, client, enabled, invalidateRefresh, isCurrentSource, source, workspaceId]
1273
1396
  );
1274
1397
  const act = useCallback5(
1275
1398
  async (action, operationId = crypto.randomUUID()) => await dispatchAction(action, operationId, null),
@@ -1281,13 +1404,16 @@ function useBrowserSession(options) {
1281
1404
  );
1282
1405
  const observeForInput = useCallback5(async () => {
1283
1406
  const target = selectedTargetRef.current;
1407
+ if (!enabled || !isCurrentSource()) {
1408
+ throw new Error("The browser source is no longer selected.");
1409
+ }
1284
1410
  if (!browserSessionId || !target) throw new Error("No browser tab is selected.");
1285
1411
  const observation = await client.observeBrowserTarget(workspaceId, browserSessionId, target.id);
1286
- if (!mountedRef.current || observation.browserSessionId !== browserSessionId || !sameObservationTarget(observation, selectedTargetRef.current)) {
1412
+ if (!isCurrentSource() || observation.browserSessionId !== browserSessionId || !sameObservationTarget(observation, selectedTargetRef.current)) {
1287
1413
  throw new Error("The browser page changed. Open the options again.");
1288
1414
  }
1289
1415
  return observation;
1290
- }, [browserSessionId, client, workspaceId]);
1416
+ }, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
1291
1417
  const actFromObservation = useCallback5(
1292
1418
  async (action, observation) => {
1293
1419
  if (!sameObservationTarget(observation, selectedTargetRef.current)) {
@@ -1295,6 +1421,7 @@ function useBrowserSession(options) {
1295
1421
  }
1296
1422
  return await dispatchAction(action, crypto.randomUUID(), {
1297
1423
  browserSessionId: observation.browserSessionId,
1424
+ controllerGeneration: observation.target.controllerGeneration,
1298
1425
  targetId: observation.target.id,
1299
1426
  targetGeneration: observation.target.targetGeneration,
1300
1427
  documentGeneration: observation.target.documentGeneration,
@@ -1305,6 +1432,9 @@ function useBrowserSession(options) {
1305
1432
  );
1306
1433
  const diagnostics = useCallback5(
1307
1434
  async (diagnosticOptions = {}) => {
1435
+ if (!enabled || !isCurrentSource()) {
1436
+ throw new Error("The browser source is no longer selected.");
1437
+ }
1308
1438
  const targetId = selectedTargetIdRef.current;
1309
1439
  if (!browserSessionId || !targetId) {
1310
1440
  throw new Error("No browser tab is selected.");
@@ -1316,16 +1446,19 @@ function useBrowserSession(options) {
1316
1446
  diagnosticOptions
1317
1447
  );
1318
1448
  },
1319
- [browserSessionId, client, workspaceId]
1449
+ [browserSessionId, client, enabled, isCurrentSource, workspaceId]
1320
1450
  );
1321
1451
  const readClipboard = useCallback5(async () => {
1452
+ if (!enabled || !isCurrentSource()) {
1453
+ throw new Error("The browser source is no longer selected.");
1454
+ }
1322
1455
  if (!browserSessionId) throw new Error("No BrowserSession is selected.");
1323
1456
  const clipboard = await client.readBrowserClipboard(workspaceId, browserSessionId);
1324
- if (clipboard.browserSessionId !== browserSessionId) {
1457
+ if (!isCurrentSource() || clipboard.browserSessionId !== browserSessionId) {
1325
1458
  throw new Error("Browser clipboard belongs to another BrowserSession.");
1326
1459
  }
1327
1460
  return clipboard;
1328
- }, [browserSessionId, client, workspaceId]);
1461
+ }, [browserSessionId, client, enabled, isCurrentSource, workspaceId]);
1329
1462
  return {
1330
1463
  session: visible.session,
1331
1464
  targets: visible.targets,
@@ -1357,7 +1490,8 @@ function emptyState4(browserSessionId, loading) {
1357
1490
  loading,
1358
1491
  mutating: false,
1359
1492
  error: null,
1360
- inputFailure: null
1493
+ inputFailure: null,
1494
+ actionResultOrder: 0
1361
1495
  };
1362
1496
  }
1363
1497
  function chooseTarget(targets, preferredId) {
@@ -4546,4 +4680,4 @@ export {
4546
4680
  useBrowserFrameStream,
4547
4681
  BrowserViewer
4548
4682
  };
4549
- //# sourceMappingURL=chunk-HDJWDK7W.js.map
4683
+ //# sourceMappingURL=chunk-2OQA3CPB.js.map