@opengeni/react 0.48.0 → 0.57.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (325) hide show
  1. package/README.md +138 -2
  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 +199 -0
  13. package/dist/artifacts.js.map +1 -0
  14. package/dist/{chunk-NJ7M2B6Z.js → chunk-5UQPNLIO.js} +16 -8
  15. package/dist/{chunk-NJ7M2B6Z.js.map → chunk-5UQPNLIO.js.map} +1 -1
  16. package/dist/{chunk-LHLYWG5R.js → chunk-6KLX2RH5.js} +461 -38
  17. package/dist/chunk-6KLX2RH5.js.map +1 -0
  18. package/dist/chunk-7WCLAEFV.js +2828 -0
  19. package/dist/chunk-7WCLAEFV.js.map +1 -0
  20. package/dist/{chunk-I4MPFDRP.js → chunk-B6WPZSTT.js} +104 -104
  21. package/dist/{chunk-I4MPFDRP.js.map → chunk-B6WPZSTT.js.map} +1 -1
  22. package/dist/{chunk-EKZH6IDG.js → chunk-C5ZPSSS5.js} +111 -1
  23. package/dist/chunk-C5ZPSSS5.js.map +1 -0
  24. package/dist/{chunk-OL6QVXGF.js → chunk-HA46CYQK.js} +152 -8
  25. package/dist/chunk-HA46CYQK.js.map +1 -0
  26. package/dist/chunk-HUIKIDM5.js +2148 -0
  27. package/dist/chunk-HUIKIDM5.js.map +1 -0
  28. package/dist/{chunk-FWXGSNXT.js → chunk-IGC6A2ZI.js} +585 -34
  29. package/dist/chunk-IGC6A2ZI.js.map +1 -0
  30. package/dist/chunk-JVYQRRC4.js +53 -0
  31. package/dist/chunk-JVYQRRC4.js.map +1 -0
  32. package/dist/chunk-KCGXPP5V.js +157 -0
  33. package/dist/chunk-KCGXPP5V.js.map +1 -0
  34. package/dist/chunk-LLOLUHVG.js +5937 -0
  35. package/dist/chunk-LLOLUHVG.js.map +1 -0
  36. package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
  37. package/dist/chunk-MKPIAGVL.js.map +1 -0
  38. package/dist/chunk-PEI6RANI.js +3234 -0
  39. package/dist/chunk-PEI6RANI.js.map +1 -0
  40. package/dist/chunk-R3AZOAB5.js +933 -0
  41. package/dist/chunk-R3AZOAB5.js.map +1 -0
  42. package/dist/{chunk-O4VXOBGF.js → chunk-WSK7G5LE.js} +70 -2
  43. package/dist/chunk-WSK7G5LE.js.map +1 -0
  44. package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
  45. package/dist/chunk-XBZWOM2K.js.map +1 -0
  46. package/dist/{chunk-XCNTXWBB.js → chunk-Y7OEDUJ5.js} +856 -338
  47. package/dist/chunk-Y7OEDUJ5.js.map +1 -0
  48. package/dist/client.d.ts +10 -2
  49. package/dist/clipboard.d.ts +1 -0
  50. package/dist/clipboard.js +7 -0
  51. package/dist/clipboard.js.map +1 -0
  52. package/dist/commands/index.d.ts +3 -3
  53. package/dist/commands/registry.d.ts +1 -1
  54. package/dist/commands/types.d.ts +1 -1
  55. package/dist/components/approval-surface.d.ts +2 -2
  56. package/dist/components/artifacts/artifact-surface.d.ts +18 -0
  57. package/dist/components/artifacts/document-editor.d.ts +172 -0
  58. package/dist/components/artifacts/editable-artifact-ui.d.ts +13 -0
  59. package/dist/components/artifacts/editable-artifact-workbench.d.ts +49 -0
  60. package/dist/components/artifacts/editable-document.d.ts +24 -0
  61. package/dist/components/artifacts/editable-presentation.d.ts +24 -0
  62. package/dist/components/artifacts/editable-spreadsheet.d.ts +32 -0
  63. package/dist/components/artifacts/index.d.ts +9 -0
  64. package/dist/components/artifacts/presentation-editor.d.ts +297 -0
  65. package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
  66. package/dist/components/artifacts/spreadsheet-canvas.d.ts +120 -0
  67. package/dist/components/artifacts/spreadsheet-grid.d.ts +113 -0
  68. package/dist/components/browser-input.d.ts +7 -0
  69. package/dist/components/browser-viewer.d.ts +30 -0
  70. package/dist/components/chat-composer.d.ts +8 -8
  71. package/dist/components/code-editor.d.ts +25 -3
  72. package/dist/components/command-palette.d.ts +2 -2
  73. package/dist/components/composer-transcription-control.d.ts +2 -2
  74. package/dist/components/composer.d.ts +17 -17
  75. package/dist/components/computer-viewer.d.ts +24 -0
  76. package/dist/components/copy-button.d.ts +2 -2
  77. package/dist/components/desktop-viewer.d.ts +50 -22
  78. package/dist/components/diff-view.d.ts +1 -1
  79. package/dist/components/enrollment-consent.d.ts +1 -1
  80. package/dist/components/enrollment-device-flow.d.ts +1 -1
  81. package/dist/components/file-browser.d.ts +2 -2
  82. package/dist/components/fleet-tile.d.ts +1 -1
  83. package/dist/components/generated-video-player.d.ts +10 -0
  84. package/dist/components/human-input-form.d.ts +1 -1
  85. package/dist/components/human-input-surface.d.ts +2 -2
  86. package/dist/components/interaction-intervention-banner.d.ts +11 -0
  87. package/dist/components/interactive-workbench-viewers.d.ts +2 -0
  88. package/dist/components/machine-card.d.ts +2 -2
  89. package/dist/components/machine-dock-bar.d.ts +3 -3
  90. package/dist/components/machine-health-pill.d.ts +2 -2
  91. package/dist/components/machine-input-display.d.ts +1 -1
  92. package/dist/components/machine-metrics.d.ts +2 -2
  93. package/dist/components/machine-status-pill.d.ts +4 -4
  94. package/dist/components/machines/health.d.ts +1 -1
  95. package/dist/components/machines/machine-detail.d.ts +3 -3
  96. package/dist/components/machines/metric-history-chart.d.ts +1 -1
  97. package/dist/components/machines/metric-sparkline.d.ts +2 -2
  98. package/dist/components/machines/series.d.ts +2 -2
  99. package/dist/components/machines-dashboard.d.ts +2 -2
  100. package/dist/components/markdown.d.ts +1 -1
  101. package/dist/components/message-timeline.d.ts +12 -5
  102. package/dist/components/model-picker.d.ts +2 -2
  103. package/dist/components/model-policy-picker.d.ts +7 -7
  104. package/dist/components/pierre-diff.d.ts +1 -1
  105. package/dist/components/pierre-file.d.ts +1 -1
  106. package/dist/components/queue-draft-policy.d.ts +1 -1
  107. package/dist/components/queue-surface-implementation.d.ts +3 -3
  108. package/dist/components/queue-surface-state.d.ts +4 -4
  109. package/dist/components/queue-surface.d.ts +5 -5
  110. package/dist/components/sandbox-files.d.ts +3 -3
  111. package/dist/components/sandbox-terminal.d.ts +24 -4
  112. package/dist/components/sandbox-workspace.d.ts +28 -9
  113. package/dist/components/session-chrome.d.ts +4 -4
  114. package/dist/components/session-status.d.ts +2 -2
  115. package/dist/components/timeline-annotation-selection.d.ts +9 -0
  116. package/dist/components/timeline-annotations-dialog.d.ts +16 -0
  117. package/dist/components/timeline-annotations.d.ts +13 -0
  118. package/dist/components/tooltip.d.ts +4 -4
  119. package/dist/components/user-message-body.d.ts +2 -2
  120. package/dist/components/workbench-changes.d.ts +2 -2
  121. package/dist/components/workspace-dock.d.ts +1 -1
  122. package/dist/composer.d.ts +11 -11
  123. package/dist/composer.js +7 -7
  124. package/dist/hooks/internal.d.ts +1 -1
  125. package/dist/hooks/use-attached-browsers.d.ts +21 -0
  126. package/dist/hooks/use-auth-runs.d.ts +32 -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-downloads.d.ts +25 -0
  130. package/dist/hooks/use-browser-frame-stream.d.ts +28 -0
  131. package/dist/hooks/use-browser-identities.d.ts +30 -0
  132. package/dist/hooks/use-browser-session.d.ts +29 -0
  133. package/dist/hooks/use-browser-sessions.d.ts +32 -0
  134. package/dist/hooks/use-channels.d.ts +26 -0
  135. package/dist/hooks/use-codex-accounts.d.ts +2 -2
  136. package/dist/hooks/use-composer.d.ts +36 -9
  137. package/dist/hooks/use-computer-frame-stream.d.ts +24 -0
  138. package/dist/hooks/use-computer-session.d.ts +29 -0
  139. package/dist/hooks/use-computer-sessions.d.ts +28 -0
  140. package/dist/hooks/use-desktop-stream.d.ts +5 -1
  141. package/dist/hooks/use-environments.d.ts +1 -1
  142. package/dist/hooks/use-file-attachments.d.ts +1 -1
  143. package/dist/hooks/use-goal.d.ts +2 -2
  144. package/dist/hooks/use-human-input.d.ts +2 -2
  145. package/dist/hooks/use-interaction-interventions.d.ts +27 -0
  146. package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
  147. package/dist/hooks/use-last-started-turn-policy.d.ts +2 -2
  148. package/dist/hooks/use-machine-chip.d.ts +2 -2
  149. package/dist/hooks/use-machines.d.ts +4 -4
  150. package/dist/hooks/use-network-routes.d.ts +25 -0
  151. package/dist/hooks/use-packs.d.ts +7 -2
  152. package/dist/hooks/use-rigs.d.ts +1 -1
  153. package/dist/hooks/use-sandbox-files.d.ts +6 -4
  154. package/dist/hooks/use-sandbox-git.d.ts +6 -5
  155. package/dist/hooks/use-sandbox-terminal.d.ts +1 -1
  156. package/dist/hooks/use-scheduled-tasks.d.ts +1 -1
  157. package/dist/hooks/use-session-capabilities.d.ts +5 -4
  158. package/dist/hooks/use-session-control.d.ts +1 -1
  159. package/dist/hooks/use-session-events.d.ts +2 -2
  160. package/dist/hooks/use-session-lineage.d.ts +1 -1
  161. package/dist/hooks/use-session-mcp-approval-policy.d.ts +2 -2
  162. package/dist/hooks/use-session.d.ts +2 -2
  163. package/dist/hooks/use-site-auth-connections.d.ts +25 -0
  164. package/dist/hooks/use-slash-commands.d.ts +1 -1
  165. package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
  166. package/dist/hooks/use-terminal-stream.d.ts +11 -2
  167. package/dist/hooks/use-turn-queue.d.ts +2 -2
  168. package/dist/hooks/use-video-artifact-playback.d.ts +9 -0
  169. package/dist/hooks/use-voice-input.d.ts +1 -1
  170. package/dist/hooks/use-workspace-capture.d.ts +1 -1
  171. package/dist/hooks/use-workspace-edit.d.ts +1 -1
  172. package/dist/hooks/use-workspace-sessions.d.ts +1 -1
  173. package/dist/hooks/use-workspaces.d.ts +1 -1
  174. package/dist/index.d.ts +162 -155
  175. package/dist/index.js +1822 -1703
  176. package/dist/index.js.map +1 -1
  177. package/dist/interaction.d.ts +33 -0
  178. package/dist/interaction.js +550 -0
  179. package/dist/interaction.js.map +1 -0
  180. package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
  181. package/dist/interactive-workbench-viewers-TOHBXJEE.js.map +1 -0
  182. package/dist/lib/host-platform.d.ts +5 -0
  183. package/dist/lib/relay-wire.d.ts +24 -1
  184. package/dist/lib/sandbox-liveness.d.ts +9 -0
  185. package/dist/lib/terminal-capability.d.ts +5 -0
  186. package/dist/lib/xterm-theme.d.ts +1 -1
  187. package/dist/machines.d.ts +30 -30
  188. package/dist/machines.js +2 -2
  189. package/dist/model-policy.d.ts +1 -1
  190. package/dist/model-policy.js +1 -1
  191. package/dist/provider.d.ts +6 -5
  192. package/dist/{queue-surface-implementation-U5JFG7HT.js → queue-surface-implementation-2KVJTYXH.js} +100 -48
  193. package/dist/queue-surface-implementation-2KVJTYXH.js.map +1 -0
  194. package/dist/realtime/dropdown-menu.d.ts +8 -8
  195. package/dist/realtime/realtime-control.d.ts +8 -8
  196. package/dist/realtime.d.ts +4 -4
  197. package/dist/realtime.js +38 -11
  198. package/dist/realtime.js.map +1 -1
  199. package/dist/session-context.d.ts +28 -2
  200. package/dist/session-ui.d.ts +14 -14
  201. package/dist/session-ui.js +5 -4
  202. package/dist/session.d.ts +28 -28
  203. package/dist/session.js +10 -10
  204. package/dist/timeline/activity-rail.d.ts +5 -3
  205. package/dist/timeline/disclosure-context.d.ts +1 -1
  206. package/dist/timeline/fleet-decision-projection.d.ts +1 -1
  207. package/dist/timeline/fleet-decision-row.d.ts +3 -3
  208. package/dist/timeline/index.d.ts +19 -19
  209. package/dist/timeline/parsers.d.ts +4 -1
  210. package/dist/timeline/projection.d.ts +3 -3
  211. package/dist/timeline/registry.d.ts +8 -2
  212. package/dist/timeline/screenshot-lightbox.d.ts +2 -2
  213. package/dist/timeline/shared.d.ts +21 -10
  214. package/dist/timeline/tool-diff.d.ts +2 -2
  215. package/dist/timeline/tool-renderers.d.ts +1 -1
  216. package/dist/timeline/turn-summary.d.ts +3 -3
  217. package/dist/timeline/types.d.ts +44 -3
  218. package/dist/timeline-annotation-selection-3L7LHGG2.js +187 -0
  219. package/dist/timeline-annotation-selection-3L7LHGG2.js.map +1 -0
  220. package/dist/timeline-annotations-dialog-U7EVLR75.js +196 -0
  221. package/dist/timeline-annotations-dialog-U7EVLR75.js.map +1 -0
  222. package/package.json +36 -7
  223. package/src/artifacts-document.ts +31 -0
  224. package/src/artifacts-presentation.ts +36 -0
  225. package/src/artifacts-spreadsheet.ts +21 -0
  226. package/src/artifacts.ts +89 -0
  227. package/src/client.ts +157 -1
  228. package/src/clipboard.ts +2 -0
  229. package/src/components/artifacts/artifact-surface.tsx +93 -0
  230. package/src/components/artifacts/document-editor.tsx +2255 -0
  231. package/src/components/artifacts/editable-artifact-ui.tsx +89 -0
  232. package/src/components/artifacts/editable-artifact-workbench.tsx +203 -0
  233. package/src/components/artifacts/editable-document.tsx +657 -0
  234. package/src/components/artifacts/editable-presentation.tsx +1020 -0
  235. package/src/components/artifacts/editable-spreadsheet.tsx +733 -0
  236. package/src/components/artifacts/index.ts +104 -0
  237. package/src/components/artifacts/presentation-editor.tsx +3148 -0
  238. package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
  239. package/src/components/artifacts/spreadsheet-canvas.ts +799 -0
  240. package/src/components/artifacts/spreadsheet-grid.tsx +2129 -0
  241. package/src/components/browser-input.ts +65 -0
  242. package/src/components/browser-viewer.tsx +2702 -0
  243. package/src/components/chat-composer.tsx +13 -0
  244. package/src/components/code-editor.tsx +89 -30
  245. package/src/components/composer-transcription-control.tsx +10 -6
  246. package/src/components/computer-viewer.tsx +1636 -0
  247. package/src/components/desktop-viewer.tsx +215 -74
  248. package/src/components/generated-video-player.tsx +89 -0
  249. package/src/components/interaction-intervention-banner.tsx +99 -0
  250. package/src/components/interactive-workbench-viewers.ts +2 -0
  251. package/src/components/machine-input-display.ts +3 -0
  252. package/src/components/machines/machine-detail.tsx +1 -1
  253. package/src/components/message-timeline.tsx +289 -32
  254. package/src/components/model-policy-picker.tsx +15 -0
  255. package/src/components/queue-surface-implementation.tsx +116 -60
  256. package/src/components/sandbox-files.tsx +2 -2
  257. package/src/components/sandbox-terminal.tsx +128 -46
  258. package/src/components/sandbox-workspace.tsx +256 -90
  259. package/src/components/session-chrome.tsx +53 -13
  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/workbench-changes.tsx +188 -31
  264. package/src/components/workspace-dock.tsx +59 -5
  265. package/src/hooks/use-attached-browsers.ts +154 -0
  266. package/src/hooks/use-auth-runs.ts +278 -0
  267. package/src/hooks/use-browser-downloads.ts +218 -0
  268. package/src/hooks/use-browser-frame-stream.ts +425 -0
  269. package/src/hooks/use-browser-identities.ts +266 -0
  270. package/src/hooks/use-browser-session.ts +543 -0
  271. package/src/hooks/use-browser-sessions.ts +286 -0
  272. package/src/hooks/use-channels.ts +99 -0
  273. package/src/hooks/use-composer.ts +769 -24
  274. package/src/hooks/use-computer-frame-stream.ts +434 -0
  275. package/src/hooks/use-computer-session.ts +462 -0
  276. package/src/hooks/use-computer-sessions.ts +251 -0
  277. package/src/hooks/use-desktop-stream.ts +39 -7
  278. package/src/hooks/use-interaction-interventions.ts +242 -0
  279. package/src/hooks/use-interaction-invalidation.ts +66 -0
  280. package/src/hooks/use-machine-chip.ts +15 -3
  281. package/src/hooks/use-machines.ts +33 -3
  282. package/src/hooks/use-network-routes.ts +214 -0
  283. package/src/hooks/use-packs.ts +51 -0
  284. package/src/hooks/use-sandbox-files.ts +98 -55
  285. package/src/hooks/use-sandbox-git.ts +93 -38
  286. package/src/hooks/use-sandbox-terminal.ts +3 -2
  287. package/src/hooks/use-session-capabilities.ts +59 -46
  288. package/src/hooks/use-site-auth-connections.ts +226 -0
  289. package/src/hooks/use-supergrok-accounts.ts +81 -0
  290. package/src/hooks/use-terminal-stream.ts +359 -47
  291. package/src/hooks/use-video-artifact-playback.ts +28 -0
  292. package/src/hooks/use-workspace-edit.ts +2 -1
  293. package/src/index.ts +18 -0
  294. package/src/interaction.ts +101 -0
  295. package/src/lib/host-platform.ts +10 -0
  296. package/src/lib/relay-wire.ts +47 -4
  297. package/src/lib/sandbox-liveness.ts +11 -0
  298. package/src/lib/terminal-capability.ts +10 -0
  299. package/src/model-policy.ts +19 -4
  300. package/src/provider.tsx +133 -1
  301. package/src/realtime/realtime-control.tsx +34 -6
  302. package/src/session-context.ts +156 -1
  303. package/src/timeline/activity-rail.tsx +25 -3
  304. package/src/timeline/index.ts +3 -0
  305. package/src/timeline/parsers.ts +14 -1
  306. package/src/timeline/projection.ts +269 -14
  307. package/src/timeline/registry.ts +13 -1
  308. package/src/timeline/screenshot-lightbox.tsx +19 -9
  309. package/src/timeline/shared.tsx +56 -8
  310. package/src/timeline/tool-renderers.tsx +303 -50
  311. package/src/timeline/types.ts +57 -3
  312. package/styles/compiled.css +312 -21
  313. package/styles/index.css +15 -0
  314. package/styles/responsive.css +13 -1
  315. package/dist/chunk-23EJ676W.js.map +0 -1
  316. package/dist/chunk-EKZH6IDG.js.map +0 -1
  317. package/dist/chunk-FWXGSNXT.js.map +0 -1
  318. package/dist/chunk-LHLYWG5R.js.map +0 -1
  319. package/dist/chunk-O4VXOBGF.js.map +0 -1
  320. package/dist/chunk-OL6QVXGF.js.map +0 -1
  321. package/dist/chunk-WYA65Y3F.js.map +0 -1
  322. package/dist/chunk-XCNTXWBB.js.map +0 -1
  323. package/dist/fleet-decision-projection-SLNZMS42.js +0 -241
  324. package/dist/fleet-decision-projection-SLNZMS42.js.map +0 -1
  325. package/dist/queue-surface-implementation-U5JFG7HT.js.map +0 -1
@@ -0,0 +1,214 @@
1
+ import type {
2
+ CreateNetworkRouteRequest,
3
+ NetworkRoute,
4
+ NetworkRouteMutationResponse,
5
+ UpdateNetworkRouteRequest,
6
+ } from "@opengeni/sdk/interaction";
7
+ import { useCallback, useEffect, useRef, useState } from "react";
8
+ import type { EmbeddedBrowserInteractionClientLike } from "../client";
9
+ import {
10
+ type EmbeddedBrowserInteractionClientOverride,
11
+ useEmbeddedBrowserInteraction,
12
+ } from "../session-context";
13
+ import { useInteractionInvalidation } from "./use-interaction-invalidation";
14
+
15
+ export type UseNetworkRoutesOptions = EmbeddedBrowserInteractionClientOverride & {
16
+ enabled?: boolean | undefined;
17
+ includeArchived?: boolean | undefined;
18
+ };
19
+
20
+ export type UseNetworkRoutesResult = {
21
+ revision: number;
22
+ routes: NetworkRoute[];
23
+ loading: boolean;
24
+ refreshing: boolean;
25
+ mutating: boolean;
26
+ error: Error | null;
27
+ refresh: () => Promise<void>;
28
+ create: (
29
+ request: Omit<CreateNetworkRouteRequest, "operationId"> & { operationId?: string },
30
+ ) => Promise<NetworkRouteMutationResponse>;
31
+ update: (
32
+ networkRouteId: string,
33
+ request: Omit<UpdateNetworkRouteRequest, "operationId"> & { operationId?: string },
34
+ ) => Promise<NetworkRouteMutationResponse>;
35
+ };
36
+
37
+ /** Workspace network-route registry. Route mutations never expose brokered credentials. */
38
+ export function useNetworkRoutes(options: UseNetworkRoutesOptions = {}): UseNetworkRoutesResult {
39
+ const { client, workspaceId, workspaceInteractionEvent, workspaceInteractionConnectionState } =
40
+ useEmbeddedBrowserInteraction(options);
41
+ const enabled = options.enabled ?? true;
42
+ const includeArchived = options.includeArchived ?? false;
43
+ const key = `${workspaceId}:${includeArchived ? "all" : "active"}`;
44
+ const [state, setState] = useState(() => emptyState(key, enabled));
45
+ const [mutationCount, setMutationCount] = useState(0);
46
+ const requestRef = useRef<{ id: number; controller: AbortController | null }>({
47
+ id: 0,
48
+ controller: null,
49
+ });
50
+ const mountedRef = useRef(true);
51
+ const visible = state.key === key ? state : emptyState(key, enabled);
52
+
53
+ const cancelLoad = useCallback(() => {
54
+ requestRef.current.controller?.abort();
55
+ requestRef.current = { id: requestRef.current.id + 1, controller: null };
56
+ }, []);
57
+
58
+ const load = useCallback(
59
+ async (foreground: boolean): Promise<void> => {
60
+ if (!enabled) return;
61
+ const id = requestRef.current.id + 1;
62
+ requestRef.current.controller?.abort();
63
+ const controller = new AbortController();
64
+ requestRef.current = { id, controller };
65
+ setState((current) => ({
66
+ ...(current.key === key ? current : emptyState(key, true)),
67
+ loading: foreground,
68
+ refreshing: !foreground,
69
+ error: foreground || current.key !== key ? null : current.error,
70
+ }));
71
+ try {
72
+ const response = await client.listNetworkRoutes(workspaceId, {
73
+ includeArchived,
74
+ signal: controller.signal,
75
+ });
76
+ if (!mountedRef.current || requestRef.current.id !== id) return;
77
+ setState({
78
+ key,
79
+ revision: response.revision,
80
+ routes: sortRoutes(response.routes),
81
+ loading: false,
82
+ refreshing: false,
83
+ error: null,
84
+ });
85
+ } catch (cause) {
86
+ if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id)
87
+ return;
88
+ setState((current) => ({
89
+ ...(current.key === key ? current : emptyState(key, true)),
90
+ loading: false,
91
+ refreshing: false,
92
+ error: asError(cause),
93
+ }));
94
+ } finally {
95
+ if (requestRef.current.id === id) requestRef.current = { id, controller: null };
96
+ }
97
+ },
98
+ [client, enabled, includeArchived, key, workspaceId],
99
+ );
100
+
101
+ useEffect(() => {
102
+ mountedRef.current = true;
103
+ if (!enabled) {
104
+ cancelLoad();
105
+ setState(emptyState(key, false));
106
+ return;
107
+ }
108
+ void load(true);
109
+ return () => {
110
+ mountedRef.current = false;
111
+ cancelLoad();
112
+ };
113
+ }, [cancelLoad, enabled, key, load]);
114
+
115
+ const refresh = useCallback(async () => await load(false), [load]);
116
+ useInteractionInvalidation({
117
+ workspaceId,
118
+ key,
119
+ enabled,
120
+ event: workspaceInteractionEvent,
121
+ connectionState: workspaceInteractionConnectionState,
122
+ knownRevision: visible.revision,
123
+ refresh,
124
+ });
125
+
126
+ const mutate = useCallback(async <T>(operation: () => Promise<T>): Promise<T> => {
127
+ setMutationCount((count) => count + 1);
128
+ try {
129
+ return await operation();
130
+ } finally {
131
+ if (mountedRef.current) setMutationCount((count) => Math.max(0, count - 1));
132
+ }
133
+ }, []);
134
+
135
+ const merge = useCallback(
136
+ (route: NetworkRoute) => {
137
+ setState((current) => {
138
+ if (current.key !== key) return current;
139
+ const routes = current.routes.filter((candidate) => candidate.id !== route.id);
140
+ if (includeArchived || route.status !== "archived") routes.push(route);
141
+ return { ...current, routes: sortRoutes(routes), error: null };
142
+ });
143
+ },
144
+ [includeArchived, key],
145
+ );
146
+
147
+ const create = useCallback(
148
+ async (
149
+ request: Omit<CreateNetworkRouteRequest, "operationId"> & { operationId?: string },
150
+ ): Promise<NetworkRouteMutationResponse> =>
151
+ await mutate(async () => {
152
+ const response = await client.createNetworkRoute(workspaceId, {
153
+ ...request,
154
+ operationId: request.operationId ?? crypto.randomUUID(),
155
+ });
156
+ merge(response.route);
157
+ return response;
158
+ }),
159
+ [client, merge, mutate, workspaceId],
160
+ );
161
+
162
+ const update = useCallback(
163
+ async (
164
+ networkRouteId: string,
165
+ request: Omit<UpdateNetworkRouteRequest, "operationId"> & { operationId?: string },
166
+ ): Promise<NetworkRouteMutationResponse> =>
167
+ await mutate(async () => {
168
+ const response = await client.updateNetworkRoute(workspaceId, networkRouteId, {
169
+ ...request,
170
+ operationId: request.operationId ?? crypto.randomUUID(),
171
+ });
172
+ merge(response.route);
173
+ return response;
174
+ }),
175
+ [client, merge, mutate, workspaceId],
176
+ );
177
+
178
+ return {
179
+ revision: visible.revision,
180
+ routes: visible.routes,
181
+ loading: visible.loading,
182
+ refreshing: visible.refreshing,
183
+ mutating: mutationCount > 0,
184
+ error: visible.error,
185
+ refresh,
186
+ create,
187
+ update,
188
+ };
189
+ }
190
+
191
+ function emptyState(key: string, loading: boolean) {
192
+ return {
193
+ key,
194
+ revision: 0,
195
+ routes: [] as NetworkRoute[],
196
+ loading,
197
+ refreshing: false,
198
+ error: null as Error | null,
199
+ };
200
+ }
201
+
202
+ function sortRoutes(routes: readonly NetworkRoute[]): NetworkRoute[] {
203
+ return [...routes].sort(
204
+ (left, right) =>
205
+ Number(left.status === "archived") - Number(right.status === "archived") ||
206
+ left.name.localeCompare(right.name),
207
+ );
208
+ }
209
+
210
+ function asError(cause: unknown): Error {
211
+ return cause instanceof Error ? cause : new Error(String(cause));
212
+ }
213
+
214
+ export type NetworkRoutesClient = EmbeddedBrowserInteractionClientLike;
@@ -1,8 +1,14 @@
1
1
  import type {
2
2
  CapabilityPack,
3
3
  EnablePackRequest,
4
+ InstallPackRequest,
5
+ PackInstallationPreview,
4
6
  PackInstallation,
7
+ PackUninstallPreview,
8
+ PreviewPackInstallationRequest,
5
9
  RegisterCapabilityPackRequest,
10
+ UninstallPackRequest,
11
+ UninstallPackResult,
6
12
  WorkspaceRegisteredPack,
7
13
  } from "@opengeni/sdk";
8
14
  import { useCallback } from "react";
@@ -28,7 +34,15 @@ export type UsePacksResult = {
28
34
  refresh: () => Promise<void>;
29
35
  /** Register (or replace) a workspace-scoped pack manifest. */
30
36
  register: (manifest: RegisterCapabilityPackRequest) => Promise<WorkspaceRegisteredPack | null>;
37
+ /** @deprecated Use previewInstallation + install for component/Rig-aware Packs. */
31
38
  enable: (packId: string, request?: EnablePackRequest) => Promise<PackInstallation | null>;
39
+ previewInstallation: (
40
+ packId: string,
41
+ request?: PreviewPackInstallationRequest,
42
+ ) => Promise<PackInstallationPreview | null>;
43
+ install: (packId: string, request: InstallPackRequest) => Promise<PackInstallation | null>;
44
+ previewUninstall: (packId: string) => Promise<PackUninstallPreview | null>;
45
+ uninstall: (packId: string, request: UninstallPackRequest) => Promise<UninstallPackResult | null>;
32
46
  /** Unregister a workspace-scoped pack (built-ins cannot be removed). */
33
47
  remove: (packId: string) => Promise<boolean>;
34
48
  mutating: boolean;
@@ -68,6 +82,39 @@ export function usePacks(options: UsePacksOptions = {}): UsePacksResult {
68
82
  [client, workspaceId, run, refresh],
69
83
  );
70
84
 
85
+ const previewInstallation = useCallback(
86
+ async (
87
+ packId: string,
88
+ request: PreviewPackInstallationRequest = {},
89
+ ): Promise<PackInstallationPreview | null> =>
90
+ await run(() => client.previewPackInstallation(workspaceId, packId, request)),
91
+ [client, workspaceId, run],
92
+ );
93
+
94
+ const install = useCallback(
95
+ async (packId: string, request: InstallPackRequest): Promise<PackInstallation | null> => {
96
+ const result = await run(() => client.installPack(workspaceId, packId, request));
97
+ if (result) await refresh();
98
+ return result;
99
+ },
100
+ [client, workspaceId, run, refresh],
101
+ );
102
+
103
+ const previewUninstall = useCallback(
104
+ async (packId: string): Promise<PackUninstallPreview | null> =>
105
+ await run(() => client.previewPackUninstall(workspaceId, packId)),
106
+ [client, workspaceId, run],
107
+ );
108
+
109
+ const uninstall = useCallback(
110
+ async (packId: string, request: UninstallPackRequest): Promise<UninstallPackResult | null> => {
111
+ const result = await run(() => client.uninstallPack(workspaceId, packId, request));
112
+ if (result) await refresh();
113
+ return result;
114
+ },
115
+ [client, workspaceId, run, refresh],
116
+ );
117
+
71
118
  const remove = useCallback(
72
119
  async (packId: string): Promise<boolean> => {
73
120
  const result = await run(async () => {
@@ -98,6 +145,10 @@ export function usePacks(options: UsePacksOptions = {}): UsePacksResult {
98
145
  refresh,
99
146
  register,
100
147
  enable,
148
+ previewInstallation,
149
+ install,
150
+ previewUninstall,
151
+ uninstall,
101
152
  remove,
102
153
  mutating,
103
154
  mutationError,
@@ -7,11 +7,13 @@ import type {
7
7
  GitFileStatusCode,
8
8
  GitStatusResponse,
9
9
  OpenGeniRequestOptions,
10
+ SessionCapabilities,
10
11
  SessionEvent,
11
12
  WorkspaceCaptureManifest,
12
13
  } from "@opengeni/sdk";
13
14
  import { useCallback, useEffect, useMemo, useRef, useState } from "react";
14
15
  import { type ClientOverride, useOpenGeni } from "../provider";
16
+ import { sandboxAcceptsLiveIo } from "../lib/sandbox-liveness";
15
17
 
16
18
  /** The git-status overlay a file row may carry (tints modified files in the tree). */
17
19
  export type FileTreeStatus = "added" | "modified" | "deleted" | "renamed" | "untracked";
@@ -87,12 +89,14 @@ export type UseSandboxFilesOptions = ClientOverride & {
87
89
  active?: boolean | undefined;
88
90
  /** Repository roots advertised by capabilities, relative to the workspace. */
89
91
  repoPaths?: readonly string[] | undefined;
90
- /** The lease liveness ("cold" | "warm" | "draining"). The structured FileSystem
92
+ /** The lease liveness. `null` means capability negotiation has not produced
93
+ * an authoritative lifecycle value yet and therefore blocks live I/O. Omitting
94
+ * the option preserves the legacy direct-hook behavior. The structured FileSystem
91
95
  * capability is advertised even on a COLD box, so the mount-time list can race
92
96
  * the box: it lists before the box is warm, gets an empty/errored result, and
93
97
  * (with no `fs.changed` event) never re-lists. Passing liveness re-lists when
94
98
  * the box first becomes warm, so the tree populates as soon as the box is up. */
95
- liveness?: string | undefined;
99
+ liveness?: SessionCapabilities["liveness"] | null | undefined;
96
100
  /** The latest turn-end workspace capture (from `useWorkspaceCapture`). The tree
97
101
  * paints immediately from this durable index, then a warm box reconciles live
98
102
  * in place. If that live read fails, the capture remains a truthful read-only
@@ -487,7 +491,13 @@ export function useSandboxFiles(
487
491
  ].join("\u0000");
488
492
  const repoPaths = useMemo(() => repoPathsKey.split("\u0000"), [repoPathsKey]);
489
493
  const capture = options.capture ?? null;
490
- const isLive = options.liveness === "warm" || options.liveness === "draining";
494
+ const isLive = sandboxAcceptsLiveIo(options.liveness);
495
+ // `liveness` predates this hook option. Preserve legacy embedders that omit it;
496
+ // explicit `null` (negotiating) and lifecycle values require authoritative warm.
497
+ const acceptsEventReads = options.liveness === undefined || isLive;
498
+ const acceptsEventReadsRef = useRef(acceptsEventReads);
499
+ acceptsEventReadsRef.current = acceptsEventReads;
500
+ const hasLiveIoFence = options.liveness !== undefined;
491
501
  const identityKey = `${workspaceId}\u0000${sessionId ?? ""}\u0000${rootPath}\u0000${repoPathsKey}`;
492
502
 
493
503
  const [tree, setTree] = useState<FileTreeNode[]>([]);
@@ -512,6 +522,7 @@ export function useSandboxFiles(
512
522
  const refreshGenerationRef = useRef(0);
513
523
  const refreshAbortRef = useRef<AbortController | null>(null);
514
524
  const identityAbortRef = useRef(new AbortController());
525
+ const eventReadAbortRef = useRef(new AbortController());
515
526
  const identityGenerationRef = useRef(0);
516
527
  const lastSeqRef = useRef(0);
517
528
  const pendingParentsRef = useRef<Set<string>>(new Set());
@@ -846,20 +857,18 @@ export function useSandboxFiles(
846
857
  // of a parent that isn't loaded (collapsed/unmounted) is a no-op — there's
847
858
  // nothing visible to update, and it re-lists fresh when the user expands it.
848
859
  const reconcilePath = useCallback(
849
- async (path: string) => {
860
+ async (path: string, requestSignal?: AbortSignal) => {
850
861
  if (!sessionId) return;
851
862
  const identityGeneration = identityGenerationRef.current;
852
863
  const identitySignal = identityAbortRef.current.signal;
864
+ const signal = requestSignal
865
+ ? AbortSignal.any([identitySignal, requestSignal])
866
+ : identitySignal;
853
867
  // Skip parents that aren't currently loaded/expanded in the tree (nothing
854
868
  // visible to update; they re-list fresh on the next expand).
855
869
  if (!parentIsLoaded(treeRef.current, path)) return;
856
870
  try {
857
- const listed = await client.fsList(
858
- workspaceId,
859
- sessionId,
860
- { path, depth: 1 },
861
- { signal: identitySignal },
862
- );
871
+ const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
863
872
  if (identityGenerationRef.current !== identityGeneration) return;
864
873
  const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
865
874
  if (path === "") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
@@ -1075,9 +1084,10 @@ export function useSandboxFiles(
1075
1084
 
1076
1085
  // Initial paint + reset on identity change. Source selection:
1077
1086
  // • any box WITH a capture → paint instantly from the durable capture index;
1078
- // a warm/draining box then reconciles live in place.
1087
+ // a holder-confirmed warm box then reconciles live in place.
1079
1088
  // • if that live reconciliation fails, keep the capture visible and read-only.
1080
- // • cold box with NO capture best-effort live list (status quo — never worse).
1089
+ // • legacy callers that omit liveness and have no capture retain their live
1090
+ // fallback; an explicitly cold/draining lease never wakes from a passive mount.
1081
1091
  // Key the seed on the capture's REVISION (a primitive), not the manifest object
1082
1092
  // — a consumer passing a fresh object each render (or a new revision) must not
1083
1093
  // spin the effect. The latest manifest is read from a ref at run time.
@@ -1092,6 +1102,8 @@ export function useSandboxFiles(
1092
1102
  refreshAbortRef.current?.abort();
1093
1103
  refreshAbortRef.current = null;
1094
1104
  refreshGenerationRef.current += 1;
1105
+ eventReadAbortRef.current.abort();
1106
+ eventReadAbortRef.current = new AbortController();
1095
1107
  const identityChanged = previousIdentityRef.current !== identityKey;
1096
1108
  previousIdentityRef.current = identityKey;
1097
1109
  if (identityChanged || !enabled) {
@@ -1135,53 +1147,69 @@ export function useSandboxFiles(
1135
1147
  }
1136
1148
  return;
1137
1149
  }
1138
- if (isLive || !currentCapture) {
1150
+ if (isLive || (!hasLiveIoFence && !currentCapture)) {
1139
1151
  void refresh();
1140
1152
  }
1141
1153
  return () => {
1142
1154
  refreshAbortRef.current?.abort();
1143
1155
  refreshAbortRef.current = null;
1144
1156
  refreshGenerationRef.current += 1;
1157
+ eventReadAbortRef.current.abort();
1145
1158
  };
1146
- }, [enabled, active, isLive, captureRevision, identityKey, refresh, seedFromCapture]);
1159
+ }, [
1160
+ enabled,
1161
+ active,
1162
+ isLive,
1163
+ hasLiveIoFence,
1164
+ captureRevision,
1165
+ identityKey,
1166
+ refresh,
1167
+ seedFromCapture,
1168
+ ]);
1147
1169
 
1148
1170
  // Re-pull JUST the git-status overlay and re-tint the existing tree in place —
1149
1171
  // no fs re-list, no collapse. This is all a `git.changed` (commit/stage/checkout)
1150
1172
  // needs: the tree SHAPE is unchanged, only the tints move.
1151
- const refreshGitOverlay = useCallback(async () => {
1152
- if (!sessionId) return;
1153
- const identityGeneration = identityGenerationRef.current;
1154
- const identitySignal = identityAbortRef.current.signal;
1155
- setGitLoading(true);
1156
- try {
1157
- const batch = await client.gitReadBatch(
1158
- workspaceId,
1159
- sessionId,
1160
- { requests: repoPaths.map((root) => ({ status: { path: root } })) },
1161
- { signal: identitySignal },
1162
- );
1163
- const statuses = batch.results.map((result, index) => ({
1164
- root: repoPaths[index] ?? "",
1165
- status: result.status,
1166
- }));
1167
- if (identityGenerationRef.current !== identityGeneration) return;
1168
- const overlay = new Map<string, FileTreeStatus>();
1169
- for (const { root, status } of statuses) {
1170
- for (const file of status.files) {
1171
- const code = file.worktree ?? file.index;
1172
- const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;
1173
- if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
1173
+ const refreshGitOverlay = useCallback(
1174
+ async (requestSignal?: AbortSignal) => {
1175
+ if (!sessionId) return;
1176
+ const identityGeneration = identityGenerationRef.current;
1177
+ const identitySignal = identityAbortRef.current.signal;
1178
+ const signal = requestSignal
1179
+ ? AbortSignal.any([identitySignal, requestSignal])
1180
+ : identitySignal;
1181
+ setGitLoading(true);
1182
+ try {
1183
+ const batch = await client.gitReadBatch(
1184
+ workspaceId,
1185
+ sessionId,
1186
+ { requests: repoPaths.map((root) => ({ status: { path: root } })) },
1187
+ { signal },
1188
+ );
1189
+ const statuses = batch.results.map((result, index) => ({
1190
+ root: repoPaths[index] ?? "",
1191
+ status: result.status,
1192
+ }));
1193
+ if (identityGenerationRef.current !== identityGeneration) return;
1194
+ const overlay = new Map<string, FileTreeStatus>();
1195
+ for (const { root, status } of statuses) {
1196
+ for (const file of status.files) {
1197
+ const code = file.worktree ?? file.index;
1198
+ const mapped = code ? GIT_STATUS_TO_TREE[code] : undefined;
1199
+ if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
1200
+ }
1174
1201
  }
1202
+ statusRef.current = overlay;
1203
+ setGitSummary(summarizeStatuses(statuses));
1204
+ setTree((prev) => applyStatus(prev));
1205
+ } catch {
1206
+ /* a non-repo box has no overlay — leave the tree untinted */
1207
+ } finally {
1208
+ if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
1175
1209
  }
1176
- statusRef.current = overlay;
1177
- setGitSummary(summarizeStatuses(statuses));
1178
- setTree((prev) => applyStatus(prev));
1179
- } catch {
1180
- /* a non-repo box has no overlay — leave the tree untinted */
1181
- } finally {
1182
- if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
1183
- }
1184
- }, [client, workspaceId, sessionId, repoPaths, applyStatus]);
1210
+ },
1211
+ [client, workspaceId, sessionId, repoPaths, applyStatus],
1212
+ );
1185
1213
 
1186
1214
  // Auto-reconcile on fs/git change notifications — TARGETED, never a root
1187
1215
  // collapse-reload, and de-duped against our OWN mutations.
@@ -1197,13 +1225,20 @@ export function useSandboxFiles(
1197
1225
 
1198
1226
  useEffect(() => {
1199
1227
  if (!enabled || !events) return;
1200
- // An inactive tab must not replay the entire historical event log when it is
1201
- // opened. Keep its cursor current without performing any reconciliation;
1202
- // only events that arrive while Files is visible should trigger live reads.
1203
- if (!active) {
1228
+ // An inactive tab or holderless draining/cold lease must not replay the
1229
+ // historical log into live provider reads. The capture already includes
1230
+ // those events; a later warm transition performs one authoritative refresh.
1231
+ if (!active || !acceptsEventReads) {
1204
1232
  for (const event of events) {
1205
1233
  if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
1206
1234
  }
1235
+ if (debounceRef.current) {
1236
+ clearTimeout(debounceRef.current);
1237
+ debounceRef.current = null;
1238
+ }
1239
+ pendingParentsRef.current = new Set();
1240
+ pendingGitRef.current = false;
1241
+ pendingAgentToolRef.current = false;
1207
1242
  return;
1208
1243
  }
1209
1244
  let sawNew = false;
@@ -1277,6 +1312,12 @@ export function useSandboxFiles(
1277
1312
  debounceRef.current = setTimeout(() => {
1278
1313
  debounceRef.current = null;
1279
1314
  if (identityGenerationRef.current !== identityGeneration) return;
1315
+ if (!acceptsEventReadsRef.current) {
1316
+ pendingParentsRef.current = new Set();
1317
+ pendingGitRef.current = false;
1318
+ pendingAgentToolRef.current = false;
1319
+ return;
1320
+ }
1280
1321
  if (sawCommandDelta) {
1281
1322
  lastCommandRefreshAtRef.current = Date.now();
1282
1323
  setContentRevision((revision) => revision + 1);
@@ -1289,18 +1330,19 @@ export function useSandboxFiles(
1289
1330
  pendingAgentToolRef.current = false;
1290
1331
  // Reconcile the changed directories and Git overlay concurrently. Both are
1291
1332
  // independent remote reads; applyStatus makes their completion order safe.
1333
+ const eventReadSignal = eventReadAbortRef.current.signal;
1292
1334
  void (async () => {
1293
1335
  const paths = wantAgentTool ? loadedDirectoryPaths(treeRef.current) : [...parents];
1294
1336
  await Promise.all([
1295
- wantGit ? refreshGitOverlay() : Promise.resolve(),
1296
- Promise.all(paths.map((path) => reconcilePath(path))),
1337
+ wantGit ? refreshGitOverlay(eventReadSignal) : Promise.resolve(),
1338
+ Promise.all(paths.map((path) => reconcilePath(path, eventReadSignal))),
1297
1339
  ]);
1298
1340
  })();
1299
1341
  }, delay);
1300
- }, [enabled, active, events, reconcilePath, refreshGitOverlay]);
1342
+ }, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
1301
1343
 
1302
1344
  useEffect(() => {
1303
- if (active) return;
1345
+ if (active && acceptsEventReads) return;
1304
1346
  if (debounceRef.current) {
1305
1347
  clearTimeout(debounceRef.current);
1306
1348
  debounceRef.current = null;
@@ -1308,12 +1350,13 @@ export function useSandboxFiles(
1308
1350
  pendingParentsRef.current = new Set();
1309
1351
  pendingGitRef.current = false;
1310
1352
  pendingAgentToolRef.current = false;
1311
- }, [active]);
1353
+ }, [active, acceptsEventReads]);
1312
1354
 
1313
1355
  useEffect(
1314
1356
  () => () => {
1315
1357
  refreshAbortRef.current?.abort();
1316
1358
  identityAbortRef.current.abort();
1359
+ eventReadAbortRef.current.abort();
1317
1360
  identityGenerationRef.current += 1;
1318
1361
  if (debounceRef.current) clearTimeout(debounceRef.current);
1319
1362
  },