@opengeni/react 0.52.1 → 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 (132) hide show
  1. package/README.md +48 -0
  2. package/dist/artifacts.d.ts +1 -1
  3. package/dist/artifacts.js +43 -14
  4. package/dist/artifacts.js.map +1 -1
  5. package/dist/{chunk-ROIRTM37.js → chunk-5UQPNLIO.js} +6 -6
  6. package/dist/{chunk-JR3QFY6B.js → chunk-6KLX2RH5.js} +49 -2
  7. package/dist/chunk-6KLX2RH5.js.map +1 -0
  8. package/dist/{chunk-FWS3KC3Z.js → chunk-B6WPZSTT.js} +104 -104
  9. package/dist/{chunk-FWS3KC3Z.js.map → chunk-B6WPZSTT.js.map} +1 -1
  10. package/dist/{chunk-HDBG7XGO.js → chunk-C5ZPSSS5.js} +50 -1
  11. package/dist/chunk-C5ZPSSS5.js.map +1 -0
  12. package/dist/{chunk-X4S56Y2H.js → chunk-HA46CYQK.js} +135 -11
  13. package/dist/chunk-HA46CYQK.js.map +1 -0
  14. package/dist/{chunk-724BW4TW.js → chunk-IGC6A2ZI.js} +2 -2
  15. package/dist/chunk-LLOLUHVG.js +5937 -0
  16. package/dist/chunk-LLOLUHVG.js.map +1 -0
  17. package/dist/{chunk-WYA65Y3F.js → chunk-MKPIAGVL.js} +13 -3
  18. package/dist/chunk-MKPIAGVL.js.map +1 -0
  19. package/dist/chunk-R3AZOAB5.js +933 -0
  20. package/dist/chunk-R3AZOAB5.js.map +1 -0
  21. package/dist/{chunk-23EJ676W.js → chunk-XBZWOM2K.js} +9 -4
  22. package/dist/chunk-XBZWOM2K.js.map +1 -0
  23. package/dist/{chunk-JAGDUCZQ.js → chunk-Y7OEDUJ5.js} +17 -13
  24. package/dist/chunk-Y7OEDUJ5.js.map +1 -0
  25. package/dist/client.d.ts +6 -4
  26. package/dist/components/artifacts/index.d.ts +1 -0
  27. package/dist/components/artifacts/published-html-artifact-frame.d.ts +14 -0
  28. package/dist/components/browser-input.d.ts +7 -0
  29. package/dist/components/browser-viewer.d.ts +5 -4
  30. package/dist/components/computer-viewer.d.ts +2 -1
  31. package/dist/components/desktop-viewer.d.ts +26 -2
  32. package/dist/components/interaction-intervention-banner.d.ts +11 -0
  33. package/dist/components/sandbox-terminal.d.ts +4 -1
  34. package/dist/components/sandbox-workspace.d.ts +3 -0
  35. package/dist/composer.js +6 -6
  36. package/dist/hooks/use-auth-runs.d.ts +32 -0
  37. package/dist/hooks/use-browser-downloads.d.ts +25 -0
  38. package/dist/hooks/use-browser-identities.d.ts +5 -1
  39. package/dist/hooks/use-browser-session.d.ts +2 -1
  40. package/dist/hooks/use-browser-sessions.d.ts +1 -1
  41. package/dist/hooks/use-channels.d.ts +26 -0
  42. package/dist/hooks/use-computer-frame-stream.d.ts +1 -1
  43. package/dist/hooks/use-computer-session.d.ts +2 -1
  44. package/dist/hooks/use-computer-sessions.d.ts +1 -1
  45. package/dist/hooks/use-desktop-stream.d.ts +4 -0
  46. package/dist/hooks/use-interaction-interventions.d.ts +27 -0
  47. package/dist/hooks/use-interaction-invalidation.d.ts +19 -0
  48. package/dist/hooks/use-network-routes.d.ts +25 -0
  49. package/dist/hooks/use-site-auth-connections.d.ts +25 -0
  50. package/dist/hooks/use-supergrok-accounts.d.ts +26 -0
  51. package/dist/hooks/use-terminal-stream.d.ts +3 -0
  52. package/dist/index.d.ts +4 -0
  53. package/dist/index.js +1220 -1545
  54. package/dist/index.js.map +1 -1
  55. package/dist/interaction.d.ts +13 -3
  56. package/dist/interaction.js +520 -6
  57. package/dist/interaction.js.map +1 -1
  58. package/dist/interactive-workbench-viewers-TOHBXJEE.js +15 -0
  59. package/dist/lib/host-platform.d.ts +5 -0
  60. package/dist/lib/relay-wire.d.ts +20 -1
  61. package/dist/machines.js +3 -3
  62. package/dist/model-policy.d.ts +1 -1
  63. package/dist/model-policy.js +1 -1
  64. package/dist/provider.d.ts +3 -2
  65. package/dist/realtime/realtime-control.d.ts +2 -2
  66. package/dist/realtime.js +36 -9
  67. package/dist/realtime.js.map +1 -1
  68. package/dist/session-context.d.ts +10 -2
  69. package/dist/session-ui.js +3 -3
  70. package/dist/session.js +11 -11
  71. package/dist/timeline/types.d.ts +5 -0
  72. package/package.json +2 -2
  73. package/src/artifacts.ts +3 -0
  74. package/src/client.ts +73 -3
  75. package/src/components/artifacts/index.ts +5 -0
  76. package/src/components/artifacts/published-html-artifact-frame.tsx +38 -0
  77. package/src/components/browser-input.ts +65 -0
  78. package/src/components/browser-viewer.tsx +1287 -177
  79. package/src/components/computer-viewer.tsx +611 -114
  80. package/src/components/desktop-viewer.tsx +114 -1
  81. package/src/components/interaction-intervention-banner.tsx +99 -0
  82. package/src/components/machines/machine-detail.tsx +1 -1
  83. package/src/components/model-policy-picker.tsx +15 -0
  84. package/src/components/sandbox-terminal.tsx +24 -7
  85. package/src/components/sandbox-workspace.tsx +42 -4
  86. package/src/components/workspace-dock.tsx +59 -5
  87. package/src/hooks/use-attached-browsers.ts +13 -11
  88. package/src/hooks/use-auth-runs.ts +278 -0
  89. package/src/hooks/use-browser-downloads.ts +218 -0
  90. package/src/hooks/use-browser-frame-stream.ts +168 -59
  91. package/src/hooks/use-browser-identities.ts +47 -6
  92. package/src/hooks/use-browser-session.ts +94 -27
  93. package/src/hooks/use-browser-sessions.ts +19 -14
  94. package/src/hooks/use-channels.ts +99 -0
  95. package/src/hooks/use-computer-frame-stream.ts +184 -61
  96. package/src/hooks/use-computer-session.ts +22 -2
  97. package/src/hooks/use-computer-sessions.ts +19 -13
  98. package/src/hooks/use-desktop-stream.ts +39 -7
  99. package/src/hooks/use-interaction-interventions.ts +242 -0
  100. package/src/hooks/use-interaction-invalidation.ts +66 -0
  101. package/src/hooks/use-machine-chip.ts +13 -3
  102. package/src/hooks/use-network-routes.ts +214 -0
  103. package/src/hooks/use-site-auth-connections.ts +226 -0
  104. package/src/hooks/use-supergrok-accounts.ts +81 -0
  105. package/src/hooks/use-terminal-stream.ts +288 -55
  106. package/src/index.ts +8 -0
  107. package/src/interaction.ts +30 -0
  108. package/src/lib/host-platform.ts +10 -0
  109. package/src/lib/relay-wire.ts +43 -4
  110. package/src/lib/terminal-capability.ts +1 -1
  111. package/src/model-policy.ts +19 -4
  112. package/src/provider.tsx +133 -1
  113. package/src/realtime/realtime-control.tsx +34 -6
  114. package/src/session-context.ts +66 -1
  115. package/src/timeline/activity-rail.tsx +6 -0
  116. package/src/timeline/projection.ts +58 -0
  117. package/src/timeline/types.ts +2 -0
  118. package/styles/compiled.css +64 -6
  119. package/dist/chunk-23EJ676W.js.map +0 -1
  120. package/dist/chunk-6BBWWOOU.js +0 -82
  121. package/dist/chunk-6BBWWOOU.js.map +0 -1
  122. package/dist/chunk-7I2KB3OK.js +0 -3895
  123. package/dist/chunk-7I2KB3OK.js.map +0 -1
  124. package/dist/chunk-HDBG7XGO.js.map +0 -1
  125. package/dist/chunk-JAGDUCZQ.js.map +0 -1
  126. package/dist/chunk-JR3QFY6B.js.map +0 -1
  127. package/dist/chunk-WYA65Y3F.js.map +0 -1
  128. package/dist/chunk-X4S56Y2H.js.map +0 -1
  129. package/dist/interactive-workbench-viewers-KHNOHWQE.js +0 -13
  130. /package/dist/{chunk-ROIRTM37.js.map → chunk-5UQPNLIO.js.map} +0 -0
  131. /package/dist/{chunk-724BW4TW.js.map → chunk-IGC6A2ZI.js.map} +0 -0
  132. /package/dist/{interactive-workbench-viewers-KHNOHWQE.js.map → interactive-workbench-viewers-TOHBXJEE.js.map} +0 -0
package/dist/index.js CHANGED
@@ -1,60 +1,58 @@
1
1
  import {
2
- ActivityDisclosure,
3
- ActivityRail,
4
- BUILT_IN_TURN_SUMMARY_FACET_IDS,
5
- BodyNote,
6
- CopyButton,
7
- CopyHoverFrame,
8
- DisclosureDefaultsProvider,
9
- GeneratedVideoPlayer,
10
- HumanInputForm,
11
- HumanInputSurface,
12
- LightboxProvider,
13
- Markdown,
14
- MediaEmpty,
15
- MediaSkeleton,
16
- MessageTimeline,
17
- PayloadBlock,
18
- PierreDiff,
19
- QueueSurface,
20
- SESSION_STATUS_META,
21
- ScreenshotFigure,
22
- SessionChrome,
23
- SessionStatus,
24
- StatusDot,
25
- TermBlock,
26
- Thumbnail,
27
- TimelineRow,
28
- TurnSummary,
29
- UserMessageBody,
30
- answersFromDrafts,
31
- applyPatchOps,
32
- applyPatchOpsFromToolItem,
33
- controlCaret,
34
- createDefaultToolRegistry,
35
- createToolRegistry,
36
- defaultHumanInputFormMessages,
37
- defaultToolRegistry,
38
- execTruncated,
39
- gitFileDiffToPatch,
40
- isApplyPatch,
41
- isExecSessionLostBanner,
42
- looksBinary,
43
- parseExecBannerSessionId,
44
- parseFreeformApplyPatch,
45
- parseToolArgs,
46
- rawTypeOf,
47
- sandboxCommandExitCode,
48
- sessionChromeGoalPillState,
49
- stripExecBanner,
50
- tailPeek,
51
- unwrapMcpOutput,
52
- useLightbox,
53
- useLightboxOptional,
54
- useThemeType,
55
- userMessageLikelyNeedsDisclosure,
56
- v4aToGitFileDiff
57
- } from "./chunk-JAGDUCZQ.js";
2
+ SESSION_EVENT_BROWSER_MAX_BYTES,
3
+ SESSION_EVENT_BROWSER_MAX_COUNT,
4
+ SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
5
+ SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
6
+ SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
7
+ approvalsFromRequiresAction,
8
+ boundBrowserSessionEventWindow,
9
+ humanInputRequestFromEvent,
10
+ isGoalEvent,
11
+ isHumanInputEvent,
12
+ isLineageRefreshEvent,
13
+ isSessionMcpApprovalPolicyEvent,
14
+ isTitleEvent,
15
+ isTurnQueueEvent,
16
+ projectPendingApprovals,
17
+ projectPendingHumanInputRequests,
18
+ useFileAttachments,
19
+ useGoal,
20
+ useHumanInputRequests,
21
+ useSession,
22
+ useSessionControl,
23
+ useSessionEvents,
24
+ useSessionLineage,
25
+ useSessionMcpApprovalPolicy,
26
+ useTurnQueue
27
+ } from "./chunk-B6WPZSTT.js";
28
+ import {
29
+ CONNECTION_STATUS_META,
30
+ ConnectionDot,
31
+ ConnectionStatusPill,
32
+ EnrollmentConsent,
33
+ EnrollmentDeviceFlow,
34
+ HEALTH_TOKEN,
35
+ MACHINE_STATE_BADGE_META,
36
+ METRICS,
37
+ METRIC_WINDOWS,
38
+ MachineCard,
39
+ MachineDetail,
40
+ MachineDockBar,
41
+ MachineHealthPill,
42
+ MachineMetrics,
43
+ MachineStatusPill,
44
+ MachinesDashboard,
45
+ MetricHistoryChart,
46
+ MetricSparkline,
47
+ OpenGeniProvider,
48
+ SharedMachineDisclosure,
49
+ WINDOW_LABEL,
50
+ connectionStatusForState,
51
+ deriveHealth,
52
+ healthPulses,
53
+ pointsFor,
54
+ useMachines
55
+ } from "./chunk-HA46CYQK.js";
58
56
  import {
59
57
  Actions,
60
58
  AttachButton,
@@ -107,7 +105,14 @@ import {
107
105
  useSlashCommands,
108
106
  useTranscription,
109
107
  useVoiceInput
110
- } from "./chunk-ROIRTM37.js";
108
+ } from "./chunk-5UQPNLIO.js";
109
+ import {
110
+ FILE_ONLY_MESSAGE_TEXT,
111
+ composeSendInput,
112
+ shouldSteerOnKey,
113
+ shouldSubmitOnKey,
114
+ useComposer
115
+ } from "./chunk-IGC6A2ZI.js";
111
116
  import {
112
117
  BillingClassMark,
113
118
  ModelPolicyPicker,
@@ -116,7 +121,7 @@ import {
116
121
  PickerBackHeader,
117
122
  PickerNavRow,
118
123
  defaultModelPolicyPickerMessages
119
- } from "./chunk-WYA65Y3F.js";
124
+ } from "./chunk-MKPIAGVL.js";
120
125
  import {
121
126
  advancedSourceSummary,
122
127
  availabilityReasonLabel,
@@ -134,90 +139,106 @@ import {
134
139
  projectPickerRows,
135
140
  runnableLatencyModesForModel,
136
141
  sortPickerRows
137
- } from "./chunk-23EJ676W.js";
142
+ } from "./chunk-XBZWOM2K.js";
143
+ import {
144
+ ActivityDisclosure,
145
+ ActivityRail,
146
+ BUILT_IN_TURN_SUMMARY_FACET_IDS,
147
+ BodyNote,
148
+ CopyButton,
149
+ CopyHoverFrame,
150
+ DisclosureDefaultsProvider,
151
+ GeneratedVideoPlayer,
152
+ HumanInputForm,
153
+ HumanInputSurface,
154
+ LightboxProvider,
155
+ Markdown,
156
+ MediaEmpty,
157
+ MediaSkeleton,
158
+ MessageTimeline,
159
+ PayloadBlock,
160
+ PierreDiff,
161
+ QueueSurface,
162
+ SESSION_STATUS_META,
163
+ ScreenshotFigure,
164
+ SessionChrome,
165
+ SessionStatus,
166
+ StatusDot,
167
+ TermBlock,
168
+ Thumbnail,
169
+ TimelineRow,
170
+ TurnSummary,
171
+ UserMessageBody,
172
+ answersFromDrafts,
173
+ applyPatchOps,
174
+ applyPatchOpsFromToolItem,
175
+ controlCaret,
176
+ createDefaultToolRegistry,
177
+ createToolRegistry,
178
+ defaultHumanInputFormMessages,
179
+ defaultToolRegistry,
180
+ execTruncated,
181
+ gitFileDiffToPatch,
182
+ isApplyPatch,
183
+ isExecSessionLostBanner,
184
+ looksBinary,
185
+ parseExecBannerSessionId,
186
+ parseFreeformApplyPatch,
187
+ parseToolArgs,
188
+ rawTypeOf,
189
+ sandboxCommandExitCode,
190
+ sessionChromeGoalPillState,
191
+ stripExecBanner,
192
+ tailPeek,
193
+ unwrapMcpOutput,
194
+ useLightbox,
195
+ useLightboxOptional,
196
+ useThemeType,
197
+ userMessageLikelyNeedsDisclosure,
198
+ v4aToGitFileDiff
199
+ } from "./chunk-Y7OEDUJ5.js";
200
+ import {
201
+ buildTimeline,
202
+ creditExhaustedFromEvents,
203
+ extractSessionRef,
204
+ groupTimeline,
205
+ sessionStatusFromEvents,
206
+ toolDisplayName
207
+ } from "./chunk-6KLX2RH5.js";
208
+ import {
209
+ TimelineAnnotationsChip
210
+ } from "./chunk-WSK7G5LE.js";
138
211
  import {
139
212
  Tooltip,
140
213
  TooltipContent,
141
214
  TooltipProvider,
142
215
  TooltipTrigger
143
216
  } from "./chunk-FK6VG4LL.js";
144
- import {
145
- TimelineAnnotationsChip
146
- } from "./chunk-WSK7G5LE.js";
147
217
  import {
148
218
  usePortalTokenSource,
149
219
  usePortalTokenStyle
150
220
  } from "./chunk-VZTQ73XT.js";
151
221
  import {
152
- CONNECTION_STATUS_META,
153
- ConnectionDot,
154
- ConnectionStatusPill,
155
- EnrollmentConsent,
156
- EnrollmentDeviceFlow,
157
- HEALTH_TOKEN,
158
- MACHINE_STATE_BADGE_META,
159
- METRICS,
160
- METRIC_WINDOWS,
161
- MachineCard,
162
- MachineDetail,
163
- MachineDockBar,
164
- MachineHealthPill,
165
- MachineMetrics,
166
- MachineStatusPill,
167
- MachinesDashboard,
168
- MetricHistoryChart,
169
- MetricSparkline,
170
- OpenGeniProvider,
171
- SharedMachineDisclosure,
172
- WINDOW_LABEL,
173
- connectionStatusForState,
174
- deriveHealth,
175
- healthPulses,
176
- pointsFor,
177
- useMachines
178
- } from "./chunk-X4S56Y2H.js";
179
- import {
180
- SESSION_EVENT_BROWSER_MAX_BYTES,
181
- SESSION_EVENT_BROWSER_MAX_COUNT,
182
- SESSION_EVENT_BROWSER_PENDING_MAX_BYTES,
183
- SESSION_EVENT_BROWSER_PENDING_MAX_COUNT,
184
- SESSION_EVENT_BROWSER_SINGLE_EVENT_MAX_BYTES,
185
- approvalsFromRequiresAction,
186
- boundBrowserSessionEventWindow,
187
- humanInputRequestFromEvent,
188
- isGoalEvent,
189
- isHumanInputEvent,
190
- isLineageRefreshEvent,
191
- isSessionMcpApprovalPolicyEvent,
192
- isTitleEvent,
193
- isTurnQueueEvent,
194
- projectPendingApprovals,
195
- projectPendingHumanInputRequests,
196
- useFileAttachments,
197
- useGoal,
198
- useHumanInputRequests,
199
- useSession,
200
- useSessionControl,
201
- useSessionEvents,
202
- useSessionLineage,
203
- useSessionMcpApprovalPolicy,
204
- useTurnQueue
205
- } from "./chunk-FWS3KC3Z.js";
222
+ DesktopViewer,
223
+ STREAM_KIND_PTY,
224
+ STREAM_ROLE_CLIENT,
225
+ decodeStreamFrame,
226
+ decodeStreamOpenAck,
227
+ encodeStreamFrame,
228
+ encodeStreamOpen,
229
+ useDesktopStream,
230
+ useRelayFrameStream
231
+ } from "./chunk-R3AZOAB5.js";
206
232
  import {
207
- buildTimeline,
208
- creditExhaustedFromEvents,
209
- extractSessionRef,
210
- groupTimeline,
211
- sessionStatusFromEvents,
212
- toolDisplayName
213
- } from "./chunk-JR3QFY6B.js";
233
+ useMutationRunner,
234
+ usePageLiveActivity,
235
+ usePolledValue,
236
+ useSessionEventTrigger
237
+ } from "./chunk-KZ73RL76.js";
214
238
  import {
215
- FILE_ONLY_MESSAGE_TEXT,
216
- composeSendInput,
217
- shouldSteerOnKey,
218
- shouldSubmitOnKey,
219
- useComposer
220
- } from "./chunk-724BW4TW.js";
239
+ useOpenGeni,
240
+ useOpenGeniClient
241
+ } from "./chunk-C5ZPSSS5.js";
221
242
  import {
222
243
  COMPOSER_PAYMENT_REQUIRED_MESSAGE,
223
244
  CREDIT_EXHAUSTION_MESSAGE,
@@ -231,23 +252,6 @@ import {
231
252
  truncate,
232
253
  tryParseJson
233
254
  } from "./chunk-LAKLF4PA.js";
234
- import {
235
- STREAM_KIND_DESKTOP,
236
- STREAM_ROLE_CLIENT,
237
- decodeStreamFrame,
238
- decodeStreamOpenAck,
239
- encodeStreamOpen
240
- } from "./chunk-6BBWWOOU.js";
241
- import {
242
- useMutationRunner,
243
- usePageLiveActivity,
244
- usePolledValue,
245
- useSessionEventTrigger
246
- } from "./chunk-KZ73RL76.js";
247
- import {
248
- useOpenGeni,
249
- useOpenGeniClient
250
- } from "./chunk-HDBG7XGO.js";
251
255
  import {
252
256
  cn
253
257
  } from "./chunk-22KP6LR5.js";
@@ -451,19 +455,103 @@ function useVariableSets(options = {}) {
451
455
  if (result) {
452
456
  await refresh();
453
457
  }
454
- return result === true;
458
+ return result === true;
459
+ },
460
+ [client, workspaceId, run, refresh]
461
+ );
462
+ const readVariable = useCallback4(
463
+ async (variableSetId, name) => await run(() => client.getVariableSetVariable(workspaceId, variableSetId, name)),
464
+ [client, workspaceId, run]
465
+ );
466
+ const setVariable = useCallback4(
467
+ async (variableSetId, name, value) => {
468
+ const result = await run(
469
+ () => client.setVariableSetVariable(workspaceId, variableSetId, name, value)
470
+ );
471
+ if (result) {
472
+ await refresh();
473
+ }
474
+ return result;
475
+ },
476
+ [client, workspaceId, run, refresh]
477
+ );
478
+ const deleteVariable = useCallback4(
479
+ async (variableSetId, name) => {
480
+ const result = await run(async () => {
481
+ await client.deleteVariableSetVariable(workspaceId, variableSetId, name);
482
+ return true;
483
+ });
484
+ if (result) {
485
+ await refresh();
486
+ }
487
+ return result === true;
488
+ },
489
+ [client, workspaceId, run, refresh]
490
+ );
491
+ return {
492
+ variableSets: data ?? [],
493
+ loading,
494
+ error,
495
+ refresh,
496
+ create,
497
+ update,
498
+ remove,
499
+ readVariable,
500
+ setVariable,
501
+ deleteVariable,
502
+ mutating,
503
+ mutationError,
504
+ clearMutationError
505
+ };
506
+ }
507
+ function useEnvironments(options = {}) {
508
+ const legacyClient = useMemo2(() => {
509
+ if (!options.client) {
510
+ return void 0;
511
+ }
512
+ return {
513
+ ...options.client,
514
+ listVariableSets: options.client.listEnvironments ?? options.client.listVariableSets,
515
+ createVariableSet: options.client.createEnvironment ?? options.client.createVariableSet,
516
+ updateVariableSet: options.client.updateEnvironment ?? options.client.updateVariableSet,
517
+ deleteVariableSet: options.client.deleteEnvironment ?? options.client.deleteVariableSet,
518
+ setVariableSetVariable: options.client.setEnvironmentVariable ?? options.client.setVariableSetVariable,
519
+ deleteVariableSetVariable: options.client.deleteEnvironmentVariable ?? options.client.deleteVariableSetVariable
520
+ };
521
+ }, [options.client]);
522
+ const result = useVariableSets({
523
+ ...options,
524
+ ...legacyClient ? { client: legacyClient } : {}
525
+ });
526
+ return { ...result, environments: result.variableSets };
527
+ }
528
+
529
+ // src/hooks/use-channels.ts
530
+ import { useCallback as useCallback5 } from "react";
531
+ function useChannels(options = {}) {
532
+ const { client, workspaceId } = useOpenGeni(options);
533
+ const load = useCallback5(
534
+ async () => await client.listChannels(workspaceId),
535
+ [client, workspaceId]
536
+ );
537
+ const { data, loading, error, refresh } = usePolledValue(load, {
538
+ pollIntervalMs: options.pollIntervalMs,
539
+ enabled: options.enabled
540
+ });
541
+ const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
542
+ const create = useCallback5(
543
+ async (request) => {
544
+ const result = await run(() => client.createChannel(workspaceId, request));
545
+ if (result) {
546
+ await refresh();
547
+ }
548
+ return result;
455
549
  },
456
550
  [client, workspaceId, run, refresh]
457
551
  );
458
- const readVariable = useCallback4(
459
- async (variableSetId, name) => await run(() => client.getVariableSetVariable(workspaceId, variableSetId, name)),
460
- [client, workspaceId, run]
461
- );
462
- const setVariable = useCallback4(
463
- async (variableSetId, name, value) => {
464
- const result = await run(
465
- () => client.setVariableSetVariable(workspaceId, variableSetId, name, value)
466
- );
552
+ const update = useCallback5(
553
+ async (channelId, request) => {
554
+ const result = await run(() => client.updateChannel(workspaceId, channelId, request));
467
555
  if (result) {
468
556
  await refresh();
469
557
  }
@@ -471,10 +559,10 @@ function useVariableSets(options = {}) {
471
559
  },
472
560
  [client, workspaceId, run, refresh]
473
561
  );
474
- const deleteVariable = useCallback4(
475
- async (variableSetId, name) => {
562
+ const remove = useCallback5(
563
+ async (channelId) => {
476
564
  const result = await run(async () => {
477
- await client.deleteVariableSetVariable(workspaceId, variableSetId, name);
565
+ await client.deleteChannel(workspaceId, channelId);
478
566
  return true;
479
567
  });
480
568
  if (result) {
@@ -484,55 +572,38 @@ function useVariableSets(options = {}) {
484
572
  },
485
573
  [client, workspaceId, run, refresh]
486
574
  );
575
+ const moveSession = useCallback5(
576
+ async (sessionId, channelId) => {
577
+ return await run(() => client.updateSessionChannel(workspaceId, sessionId, { channelId }));
578
+ },
579
+ [client, workspaceId, run]
580
+ );
487
581
  return {
488
- variableSets: data ?? [],
582
+ channels: data ?? [],
489
583
  loading,
490
584
  error,
491
585
  refresh,
492
586
  create,
493
587
  update,
494
588
  remove,
495
- readVariable,
496
- setVariable,
497
- deleteVariable,
589
+ moveSession,
498
590
  mutating,
499
591
  mutationError,
500
592
  clearMutationError
501
593
  };
502
594
  }
503
- function useEnvironments(options = {}) {
504
- const legacyClient = useMemo2(() => {
505
- if (!options.client) {
506
- return void 0;
507
- }
508
- return {
509
- ...options.client,
510
- listVariableSets: options.client.listEnvironments ?? options.client.listVariableSets,
511
- createVariableSet: options.client.createEnvironment ?? options.client.createVariableSet,
512
- updateVariableSet: options.client.updateEnvironment ?? options.client.updateVariableSet,
513
- deleteVariableSet: options.client.deleteEnvironment ?? options.client.deleteVariableSet,
514
- setVariableSetVariable: options.client.setEnvironmentVariable ?? options.client.setVariableSetVariable,
515
- deleteVariableSetVariable: options.client.deleteEnvironmentVariable ?? options.client.deleteVariableSetVariable
516
- };
517
- }, [options.client]);
518
- const result = useVariableSets({
519
- ...options,
520
- ...legacyClient ? { client: legacyClient } : {}
521
- });
522
- return { ...result, environments: result.variableSets };
523
- }
524
595
 
525
596
  // src/hooks/use-rigs.ts
526
- import { useCallback as useCallback5 } from "react";
597
+ import { useCallback as useCallback6 } from "react";
527
598
  function useRigs(options = {}) {
528
599
  const { client, workspaceId } = useOpenGeni(options);
529
- const load = useCallback5(async () => await client.listRigs(workspaceId), [client, workspaceId]);
600
+ const load = useCallback6(async () => await client.listRigs(workspaceId), [client, workspaceId]);
530
601
  const { data, loading, error, refresh } = usePolledValue(load, {
531
602
  pollIntervalMs: options.pollIntervalMs,
532
603
  enabled: options.enabled
533
604
  });
534
605
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
535
- const create = useCallback5(
606
+ const create = useCallback6(
536
607
  async (request) => {
537
608
  const result = await run(() => client.createRig(workspaceId, request));
538
609
  if (result) {
@@ -542,7 +613,7 @@ function useRigs(options = {}) {
542
613
  },
543
614
  [client, workspaceId, run, refresh]
544
615
  );
545
- const update = useCallback5(
616
+ const update = useCallback6(
546
617
  async (rigId, request) => {
547
618
  const result = await run(() => client.updateRig(workspaceId, rigId, request));
548
619
  if (result) {
@@ -552,7 +623,7 @@ function useRigs(options = {}) {
552
623
  },
553
624
  [client, workspaceId, run, refresh]
554
625
  );
555
- const remove = useCallback5(
626
+ const remove = useCallback6(
556
627
  async (rigId) => {
557
628
  const result = await run(async () => {
558
629
  await client.deleteRig(workspaceId, rigId);
@@ -565,13 +636,13 @@ function useRigs(options = {}) {
565
636
  },
566
637
  [client, workspaceId, run, refresh]
567
638
  );
568
- const listVersions = useCallback5(
639
+ const listVersions = useCallback6(
569
640
  async (rigId) => {
570
641
  return await run(() => client.listRigVersions(workspaceId, rigId));
571
642
  },
572
643
  [client, workspaceId, run]
573
644
  );
574
- const activateVersion = useCallback5(
645
+ const activateVersion = useCallback6(
575
646
  async (rigId, versionId) => {
576
647
  const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
577
648
  if (result) {
@@ -581,13 +652,13 @@ function useRigs(options = {}) {
581
652
  },
582
653
  [client, workspaceId, run, refresh]
583
654
  );
584
- const listChanges = useCallback5(
655
+ const listChanges = useCallback6(
585
656
  async (rigId) => {
586
657
  return await run(() => client.listRigChanges(workspaceId, rigId));
587
658
  },
588
659
  [client, workspaceId, run]
589
660
  );
590
- const proposeChange = useCallback5(
661
+ const proposeChange = useCallback6(
591
662
  async (rigId, request) => {
592
663
  const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
593
664
  if (result) {
@@ -616,7 +687,7 @@ function useRigs(options = {}) {
616
687
  }
617
688
  function useRig(rigId, options = {}) {
618
689
  const { client, workspaceId } = useOpenGeni(options);
619
- const load = useCallback5(
690
+ const load = useCallback6(
620
691
  async () => await client.getRig(workspaceId, rigId),
621
692
  [client, workspaceId, rigId]
622
693
  );
@@ -625,7 +696,7 @@ function useRig(rigId, options = {}) {
625
696
  enabled: options.enabled
626
697
  });
627
698
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
628
- const update = useCallback5(
699
+ const update = useCallback6(
629
700
  async (request) => {
630
701
  const result = await run(() => client.updateRig(workspaceId, rigId, request));
631
702
  if (result) {
@@ -635,14 +706,14 @@ function useRig(rigId, options = {}) {
635
706
  },
636
707
  [client, workspaceId, rigId, run, refresh]
637
708
  );
638
- const remove = useCallback5(async () => {
709
+ const remove = useCallback6(async () => {
639
710
  const result = await run(async () => {
640
711
  await client.deleteRig(workspaceId, rigId);
641
712
  return true;
642
713
  });
643
714
  return result === true;
644
715
  }, [client, workspaceId, rigId, run]);
645
- const activateVersion = useCallback5(
716
+ const activateVersion = useCallback6(
646
717
  async (versionId) => {
647
718
  const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
648
719
  if (result) {
@@ -652,7 +723,7 @@ function useRig(rigId, options = {}) {
652
723
  },
653
724
  [client, workspaceId, rigId, run, refresh]
654
725
  );
655
- const proposeChange = useCallback5(
726
+ const proposeChange = useCallback6(
656
727
  async (request) => {
657
728
  const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
658
729
  if (result) {
@@ -662,7 +733,7 @@ function useRig(rigId, options = {}) {
662
733
  },
663
734
  [client, workspaceId, rigId, run, refresh]
664
735
  );
665
- const verifyChange = useCallback5(
736
+ const verifyChange = useCallback6(
666
737
  async (changeId) => {
667
738
  const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));
668
739
  if (result) {
@@ -672,7 +743,7 @@ function useRig(rigId, options = {}) {
672
743
  },
673
744
  [client, workspaceId, rigId, run, refresh]
674
745
  );
675
- const promoteChange = useCallback5(
746
+ const promoteChange = useCallback6(
676
747
  async (changeId) => {
677
748
  const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));
678
749
  if (result) {
@@ -682,7 +753,7 @@ function useRig(rigId, options = {}) {
682
753
  },
683
754
  [client, workspaceId, rigId, run, refresh]
684
755
  );
685
- const verify = useCallback5(async () => {
756
+ const verify = useCallback6(async () => {
686
757
  return await run(() => client.verifyRig(workspaceId, rigId));
687
758
  }, [client, workspaceId, rigId, run]);
688
759
  return {
@@ -704,7 +775,7 @@ function useRig(rigId, options = {}) {
704
775
  }
705
776
  function useRigVersions(rigId, options = {}) {
706
777
  const { client, workspaceId } = useOpenGeni(options);
707
- const load = useCallback5(
778
+ const load = useCallback6(
708
779
  async () => await client.listRigVersions(workspaceId, rigId),
709
780
  [client, workspaceId, rigId]
710
781
  );
@@ -721,7 +792,7 @@ function useRigVersions(rigId, options = {}) {
721
792
  }
722
793
  function useRigChanges(rigId, options = {}) {
723
794
  const { client, workspaceId } = useOpenGeni(options);
724
- const load = useCallback5(
795
+ const load = useCallback6(
725
796
  async () => await client.listRigChanges(workspaceId, rigId),
726
797
  [client, workspaceId, rigId]
727
798
  );
@@ -738,17 +809,17 @@ function useRigChanges(rigId, options = {}) {
738
809
  }
739
810
 
740
811
  // src/hooks/use-packs.ts
741
- import { useCallback as useCallback6 } from "react";
812
+ import { useCallback as useCallback7 } from "react";
742
813
  var EMPTY_INSTALLATIONS = [];
743
814
  function usePacks(options = {}) {
744
815
  const { client, workspaceId } = useOpenGeni(options);
745
- const load = useCallback6(async () => await client.listPacks(workspaceId), [client, workspaceId]);
816
+ const load = useCallback7(async () => await client.listPacks(workspaceId), [client, workspaceId]);
746
817
  const { data, loading, error, refresh } = usePolledValue(load, {
747
818
  pollIntervalMs: options.pollIntervalMs,
748
819
  enabled: options.enabled
749
820
  });
750
821
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
751
- const register = useCallback6(
822
+ const register = useCallback7(
752
823
  async (manifest) => {
753
824
  const result = await run(() => client.registerPack(workspaceId, manifest));
754
825
  if (result) {
@@ -758,7 +829,7 @@ function usePacks(options = {}) {
758
829
  },
759
830
  [client, workspaceId, run, refresh]
760
831
  );
761
- const enable = useCallback6(
832
+ const enable = useCallback7(
762
833
  async (packId, request = {}) => {
763
834
  const result = await run(() => client.enablePack(workspaceId, packId, request));
764
835
  if (result) {
@@ -768,11 +839,11 @@ function usePacks(options = {}) {
768
839
  },
769
840
  [client, workspaceId, run, refresh]
770
841
  );
771
- const previewInstallation = useCallback6(
842
+ const previewInstallation = useCallback7(
772
843
  async (packId, request = {}) => await run(() => client.previewPackInstallation(workspaceId, packId, request)),
773
844
  [client, workspaceId, run]
774
845
  );
775
- const install = useCallback6(
846
+ const install = useCallback7(
776
847
  async (packId, request) => {
777
848
  const result = await run(() => client.installPack(workspaceId, packId, request));
778
849
  if (result) await refresh();
@@ -780,11 +851,11 @@ function usePacks(options = {}) {
780
851
  },
781
852
  [client, workspaceId, run, refresh]
782
853
  );
783
- const previewUninstall = useCallback6(
854
+ const previewUninstall = useCallback7(
784
855
  async (packId) => await run(() => client.previewPackUninstall(workspaceId, packId)),
785
856
  [client, workspaceId, run]
786
857
  );
787
- const uninstall = useCallback6(
858
+ const uninstall = useCallback7(
788
859
  async (packId, request) => {
789
860
  const result = await run(() => client.uninstallPack(workspaceId, packId, request));
790
861
  if (result) await refresh();
@@ -792,7 +863,7 @@ function usePacks(options = {}) {
792
863
  },
793
864
  [client, workspaceId, run, refresh]
794
865
  );
795
- const remove = useCallback6(
866
+ const remove = useCallback7(
796
867
  async (packId) => {
797
868
  const result = await run(async () => {
798
869
  await client.deletePack(workspaceId, packId);
@@ -806,7 +877,7 @@ function usePacks(options = {}) {
806
877
  [client, workspaceId, run, refresh]
807
878
  );
808
879
  const installations = data?.installations ?? EMPTY_INSTALLATIONS;
809
- const installationFor = useCallback6(
880
+ const installationFor = useCallback7(
810
881
  (packId) => installations.find((installation) => installation.packId === packId) ?? null,
811
882
  [installations]
812
883
  );
@@ -831,16 +902,16 @@ function usePacks(options = {}) {
831
902
  }
832
903
 
833
904
  // src/hooks/use-workspaces.ts
834
- import { useCallback as useCallback7 } from "react";
905
+ import { useCallback as useCallback8 } from "react";
835
906
  function useWorkspaces(options = {}) {
836
907
  const client = useOpenGeniClient(options);
837
- const load = useCallback7(async () => await client.listWorkspaces(), [client]);
908
+ const load = useCallback8(async () => await client.listWorkspaces(), [client]);
838
909
  const { data, loading, error, refresh } = usePolledValue(load, {
839
910
  pollIntervalMs: options.pollIntervalMs,
840
911
  enabled: options.enabled
841
912
  });
842
913
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
843
- const create = useCallback7(
914
+ const create = useCallback8(
844
915
  async (request) => {
845
916
  const result = await run(() => client.createWorkspace(request));
846
917
  if (result) {
@@ -850,7 +921,7 @@ function useWorkspaces(options = {}) {
850
921
  },
851
922
  [client, run, refresh]
852
923
  );
853
- const update = useCallback7(
924
+ const update = useCallback8(
854
925
  async (workspaceId, request) => {
855
926
  const result = await run(() => client.updateWorkspace(workspaceId, request));
856
927
  if (result) {
@@ -874,12 +945,12 @@ function useWorkspaces(options = {}) {
874
945
  }
875
946
 
876
947
  // src/hooks/use-billing-usage.ts
877
- import { useCallback as useCallback8 } from "react";
948
+ import { useCallback as useCallback9 } from "react";
878
949
  function useBillingUsage(options = {}) {
879
950
  const client = useOpenGeniClient(options);
880
951
  const accountId = options.accountId;
881
952
  const workspaceId = options.workspaceId;
882
- const load = useCallback8(
953
+ const load = useCallback9(
883
954
  async () => await client.getBillingUsage({
884
955
  ...accountId !== void 0 ? { accountId } : {},
885
956
  ...workspaceId !== void 0 ? { workspaceId } : {}
@@ -900,10 +971,10 @@ function useBillingUsage(options = {}) {
900
971
  }
901
972
 
902
973
  // src/hooks/use-available-models.ts
903
- import { useCallback as useCallback9 } from "react";
974
+ import { useCallback as useCallback10 } from "react";
904
975
  function useAvailableModels(options = {}) {
905
976
  const client = useOpenGeniClient(options);
906
- const load = useCallback9(async () => await client.getClientConfig(), [client]);
977
+ const load = useCallback10(async () => await client.getClientConfig(), [client]);
907
978
  const state = usePolledValue(load, {
908
979
  pollIntervalMs: options.pollIntervalMs,
909
980
  enabled: options.enabled
@@ -918,7 +989,7 @@ function useAvailableModels(options = {}) {
918
989
  }
919
990
  function useWorkspaceModelCatalog(options) {
920
991
  const client = useOpenGeniClient(options);
921
- const load = useCallback9(async () => {
992
+ const load = useCallback10(async () => {
922
993
  if (!options.workspaceId) {
923
994
  return { models: [], defaultModel: null };
924
995
  }
@@ -951,7 +1022,7 @@ import {
951
1022
  OpenGeniApiError,
952
1023
  applyUrlRotation
953
1024
  } from "@opengeni/sdk";
954
- import { useCallback as useCallback10, useEffect as useEffect3, useRef as useRef2, useState } from "react";
1025
+ import { useCallback as useCallback11, useEffect as useEffect3, useRef as useRef2, useState } from "react";
955
1026
 
956
1027
  // src/lib/sandbox-liveness.ts
957
1028
  function sandboxAcceptsLiveIo(liveness) {
@@ -961,7 +1032,7 @@ function sandboxAcceptsLiveIo(liveness) {
961
1032
  // src/lib/terminal-capability.ts
962
1033
  function terminalCanAcquirePty(cell) {
963
1034
  if (!cell?.ptyCapable) return false;
964
- if (cell.transport === "pty-ws") return true;
1035
+ if (cell.transport === "pty-ws" || cell.transport === "relay-pty") return true;
965
1036
  return cell.reason === "lease_cold" || cell.reason === "not_provisioned";
966
1037
  }
967
1038
 
@@ -1001,7 +1072,7 @@ function useSessionCapabilities(sessionId, options = {}) {
1001
1072
  const epochRef = useRef2(0);
1002
1073
  const viewerIdRef = useRef2(null);
1003
1074
  const previousIdentityRef = useRef2(identityKey);
1004
- const renegotiate = useCallback10(() => {
1075
+ const renegotiate = useCallback11(() => {
1005
1076
  setNonce((n) => n + 1);
1006
1077
  }, []);
1007
1078
  useEffect3(() => {
@@ -1164,422 +1235,94 @@ function useSessionCapabilities(sessionId, options = {}) {
1164
1235
  );
1165
1236
  }
1166
1237
  } else if (cause.status === 429) {
1167
- setViewerCapReached(true);
1168
- } else if (cause.status === 403) {
1169
- setState("error");
1170
- setError(cause);
1171
- return;
1172
- } else {
1173
- throw cause;
1174
- }
1175
- } else {
1176
- throw cause;
1177
- }
1178
- }
1179
- }
1180
- const warmUpRequested = wantDesktopAttach || wantTerminalAttach || wantFilesAttach;
1181
- if (sandboxAcceptsLiveIo(caps.liveness) || localViewerId) {
1182
- setState("ready");
1183
- startHeartbeat(caps);
1184
- } else if (warmUpRequested) {
1185
- setState("cold");
1186
- pollUntilWarm();
1187
- } else {
1188
- setState("on-demand");
1189
- }
1190
- } catch (cause) {
1191
- if (cancelled) return;
1192
- if (cause instanceof OpenGeniApiError && cause.status === 403) {
1193
- setState("error");
1194
- setError(new Error("not permitted to view this session's sandbox"));
1195
- return;
1196
- }
1197
- setState("error");
1198
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1199
- }
1200
- })();
1201
- return () => {
1202
- cancelled = true;
1203
- requestAbort.abort();
1204
- clearTimers();
1205
- const releaseId = localViewerId ?? viewerIdRef.current;
1206
- if (releaseId) {
1207
- void client.detachViewer(workspaceId, sessionId, releaseId).catch(() => {
1208
- });
1209
- }
1210
- };
1211
- }, [
1212
- client,
1213
- workspaceId,
1214
- sessionId,
1215
- lifecycleEnabled,
1216
- attachDesktop,
1217
- attachTerminal,
1218
- attachFiles,
1219
- warmingPollMs,
1220
- warmingDeadlineMs,
1221
- nonce,
1222
- identityKey
1223
- ]);
1224
- const events = options.events;
1225
- useEffect3(() => {
1226
- if (!events || events.length === 0) return;
1227
- setCapabilities((prev) => {
1228
- if (!prev || !prev.DesktopStream.url) return prev;
1229
- let next = prev.DesktopStream;
1230
- let changed = false;
1231
- for (const rotation of rotationsFrom(events)) {
1232
- if (rotation.viewerId && viewerIdRef.current && rotation.viewerId !== viewerIdRef.current) {
1233
- continue;
1234
- }
1235
- const applied = applyUrlRotation(next, rotation, epochRef.current);
1236
- if (applied) {
1237
- next = { ...next, url: applied.url, token: applied.token, expiresAt: applied.expiresAt };
1238
- epochRef.current = Math.max(epochRef.current, rotation.leaseEpoch);
1239
- changed = true;
1240
- }
1241
- }
1242
- return changed ? { ...prev, DesktopStream: next, leaseEpoch: epochRef.current } : prev;
1243
- });
1244
- }, [events]);
1245
- const identityMatches = enabled && stateIdentity === identityKey;
1246
- return {
1247
- capabilities: identityMatches ? capabilities : null,
1248
- state: identityMatches ? state : "idle",
1249
- error: identityMatches ? error : null,
1250
- acknowledgmentRequired: identityMatches ? acknowledgmentRequired : null,
1251
- viewerCapReached: identityMatches && viewerCapReached,
1252
- viewerId: identityMatches ? viewerId : null,
1253
- renegotiate
1254
- };
1255
- }
1256
-
1257
- // src/hooks/use-desktop-stream.ts
1258
- import {
1259
- desktopSocketUrl,
1260
- nextDesktopState
1261
- } from "@opengeni/sdk";
1262
- import { useEffect as useEffect5, useRef as useRef4, useState as useState3 } from "react";
1263
-
1264
- // src/hooks/use-relay-frame-stream.ts
1265
- import { useEffect as useEffect4, useRef as useRef3, useState as useState2 } from "react";
1266
- var TAG_OPEN = 1;
1267
- var TAG_OPENACK = 2;
1268
- var TAG_FRAME = 3;
1269
- function datagram(tag, body) {
1270
- const out = new Uint8Array(body.length + 1);
1271
- out[0] = tag;
1272
- out.set(body, 1);
1273
- return out;
1274
- }
1275
- function defaultWebSocketFactory(url) {
1276
- return new WebSocket(url);
1277
- }
1278
- function useRelayFrameStream(options) {
1279
- const { capability, containerRef, webSocketFactory } = options;
1280
- const [state, setState] = useState2("idle");
1281
- const [error, setError] = useState2(null);
1282
- const [nonce, setNonce] = useState2(0);
1283
- const stateRef = useRef3("idle");
1284
- const setBoth = (next) => {
1285
- stateRef.current = next;
1286
- setState(next);
1287
- };
1288
- const reconnect = () => setNonce((n) => n + 1);
1289
- const transport = capability?.transport ?? null;
1290
- const url = capability?.url ?? null;
1291
- const token = capability?.token ?? null;
1292
- const factoryRef = useRef3(webSocketFactory);
1293
- factoryRef.current = webSocketFactory;
1294
- useEffect4(() => {
1295
- if (typeof window === "undefined") return;
1296
- if (transport !== "relay-frames" || !url) {
1297
- setBoth("idle");
1298
- return;
1299
- }
1300
- const container = containerRef.current;
1301
- if (!container) {
1302
- setBoth("idle");
1303
- return;
1304
- }
1305
- let channel;
1306
- try {
1307
- const u = new URL(url);
1308
- channel = {
1309
- channelId: u.searchParams.get("channel") ?? "",
1310
- workspaceId: u.searchParams.get("ws") ?? "",
1311
- agentId: u.searchParams.get("agent") ?? "",
1312
- kind: STREAM_KIND_DESKTOP,
1313
- port: Number(u.searchParams.get("port") ?? "0")
1314
- };
1315
- } catch (cause) {
1316
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1317
- setBoth("error");
1318
- return;
1319
- }
1320
- setError(null);
1321
- setBoth("connecting");
1322
- const canvas = document.createElement("canvas");
1323
- canvas.setAttribute("data-opengeni-desktop-frames", "");
1324
- const style = canvas.style;
1325
- style.position = "absolute";
1326
- style.top = "0";
1327
- style.left = "0";
1328
- style.right = "0";
1329
- style.bottom = "0";
1330
- style.margin = "auto";
1331
- style.maxWidth = "100%";
1332
- style.maxHeight = "100%";
1333
- style.width = "auto";
1334
- style.height = "auto";
1335
- style.display = "block";
1336
- style.imageRendering = "auto";
1337
- container.appendChild(canvas);
1338
- const ctx = canvas.getContext("2d");
1339
- let disposed = false;
1340
- let acked = false;
1341
- let decoding = false;
1342
- let pending = null;
1343
- const drainLatest = async () => {
1344
- if (decoding) return;
1345
- decoding = true;
1346
- try {
1347
- while (pending) {
1348
- if (disposed) break;
1349
- const data = pending;
1350
- pending = null;
1351
- let bmp;
1352
- try {
1353
- bmp = await createImageBitmap(new Blob([new Uint8Array(data)], { type: "image/png" }));
1354
- } catch {
1355
- continue;
1356
- }
1357
- if (disposed) {
1358
- bmp.close();
1359
- break;
1360
- }
1361
- if (canvas.width !== bmp.width || canvas.height !== bmp.height) {
1362
- canvas.width = bmp.width;
1363
- canvas.height = bmp.height;
1364
- }
1365
- ctx?.drawImage(bmp, 0, 0, canvas.width, canvas.height);
1366
- bmp.close();
1367
- if (!disposed && stateRef.current !== "connected") setBoth("connected");
1368
- }
1369
- } finally {
1370
- decoding = false;
1371
- }
1372
- };
1373
- let ws;
1374
- try {
1375
- ws = (factoryRef.current ?? defaultWebSocketFactory)(url);
1376
- } catch (cause) {
1377
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1378
- setBoth("error");
1379
- canvas.remove();
1380
- return;
1381
- }
1382
- ws.binaryType = "arraybuffer";
1383
- const onOpen = () => {
1384
- if (disposed) return;
1385
- const body = encodeStreamOpen({
1386
- channel,
1387
- token: token ?? "",
1388
- role: STREAM_ROLE_CLIENT,
1389
- resumeFromSeq: "0"
1390
- });
1391
- try {
1392
- ws.send(datagram(TAG_OPEN, body).buffer);
1393
- } catch {
1394
- }
1395
- };
1396
- const onMessage = (ev) => {
1397
- if (disposed) return;
1398
- const data = ev.data;
1399
- if (!(data instanceof ArrayBuffer)) return;
1400
- const buf = new Uint8Array(data);
1401
- if (buf.length === 0) return;
1402
- const tag = buf[0];
1403
- const rest = buf.subarray(1);
1404
- if (tag === TAG_OPENACK) {
1405
- let ack;
1406
- try {
1407
- ack = decodeStreamOpenAck(rest);
1408
- } catch {
1409
- return;
1410
- }
1411
- if (!ack.accepted) {
1412
- setError(
1413
- new Error(
1414
- ack.error?.message ? `desktop stream rejected: ${ack.error.message}` : "desktop stream rejected by the relay"
1415
- )
1416
- );
1417
- setBoth("error");
1418
- try {
1419
- ws.close();
1420
- } catch {
1421
- }
1422
- return;
1423
- }
1424
- acked = true;
1425
- } else if (tag === TAG_FRAME) {
1426
- let fr;
1427
- try {
1428
- fr = decodeStreamFrame(rest);
1429
- } catch {
1430
- return;
1431
- }
1432
- if (fr.data && fr.data.length > 0) {
1433
- pending = fr.data;
1434
- void drainLatest();
1435
- }
1436
- }
1437
- };
1438
- const onError = () => {
1439
- if (disposed) return;
1440
- setError((prev) => prev ?? new Error("desktop stream connection error"));
1441
- setBoth("error");
1442
- };
1443
- const onClose = () => {
1444
- if (disposed) return;
1445
- setError(
1446
- (prev) => prev ?? new Error(acked ? "desktop stream closed" : "desktop stream closed before it opened")
1447
- );
1448
- setBoth("error");
1449
- };
1450
- ws.addEventListener("open", onOpen);
1451
- ws.addEventListener("message", onMessage);
1452
- ws.addEventListener("error", onError);
1453
- ws.addEventListener("close", onClose);
1454
- return () => {
1455
- disposed = true;
1456
- pending = null;
1457
- ws.removeEventListener("open", onOpen);
1458
- ws.removeEventListener("message", onMessage);
1459
- ws.removeEventListener("error", onError);
1460
- ws.removeEventListener("close", onClose);
1461
- try {
1462
- ws.close();
1463
- } catch {
1464
- }
1465
- canvas.remove();
1466
- };
1467
- }, [url, token, transport, nonce]);
1468
- return { state, error, reconnect };
1469
- }
1470
-
1471
- // src/hooks/use-desktop-stream.ts
1472
- async function defaultRfbFactory() {
1473
- const mod = await import("@novnc/novnc");
1474
- const RFB = mod.default;
1475
- return (target, url, opts) => new RFB(target, url, opts);
1476
- }
1477
- function useDesktopStream(options) {
1478
- const { capability, containerRef, interactive, scaleViewport, rfbFactory, webSocketFactory } = options;
1479
- const frameStream = useRelayFrameStream({
1480
- capability,
1481
- containerRef,
1482
- ...webSocketFactory ? { webSocketFactory } : {}
1483
- });
1484
- const [state, setState] = useState3("idle");
1485
- const [error, setError] = useState3(null);
1486
- const [nonce, setNonce] = useState3(0);
1487
- const stateRef = useRef4("idle");
1488
- const setBoth = (next) => {
1489
- stateRef.current = next;
1490
- setState(next);
1491
- };
1492
- const reconnect = () => setNonce((n) => n + 1);
1493
- const url = capability?.url ?? null;
1494
- const token = capability?.token ?? null;
1495
- const transport = capability?.transport ?? null;
1496
- const mode = capability?.mode ?? "read-only";
1497
- const rfbRef = useRef4(null);
1498
- const interactiveRef = useRef4(interactive);
1499
- const scaleViewportRef = useRef4(scaleViewport);
1500
- const modeRef = useRef4(mode);
1501
- const rfbFactoryRef = useRef4(rfbFactory);
1502
- interactiveRef.current = interactive;
1503
- scaleViewportRef.current = scaleViewport;
1504
- modeRef.current = mode;
1505
- rfbFactoryRef.current = rfbFactory;
1506
- const viewOnlyFor = (m, want) => m === "read-only" || !want;
1507
- useEffect5(() => {
1508
- if (typeof window === "undefined") return;
1509
- if (transport !== "vnc-ws" || !url) {
1510
- setBoth("idle");
1511
- return;
1512
- }
1513
- const container = containerRef.current;
1514
- if (!container) {
1515
- setBoth("idle");
1516
- return;
1517
- }
1518
- let rfb = null;
1519
- let disposed = false;
1520
- setError(null);
1521
- setBoth("negotiating");
1522
- const onConnect = () => {
1523
- if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: "connected" }));
1524
- };
1525
- const onDisconnect = () => {
1526
- if (!disposed) setBoth(nextDesktopState(stateRef.current, { type: "disconnected" }));
1527
- };
1528
- const onSecurityFailure = () => {
1529
- if (disposed) return;
1530
- setError(new Error("desktop authentication failed (token expired or revoked)"));
1531
- setBoth(nextDesktopState(stateRef.current, { type: "fail" }));
1532
- };
1533
- void (async () => {
1534
- try {
1535
- const factory = rfbFactoryRef.current ?? await defaultRfbFactory();
1536
- if (disposed) return;
1537
- const socketUrl = desktopSocketUrl({ url });
1538
- setBoth(nextDesktopState(stateRef.current, { type: "negotiated" }));
1539
- rfb = factory(container, socketUrl, {
1540
- credentials: token ? { password: token } : void 0
1541
- });
1542
- rfb.viewOnly = viewOnlyFor(modeRef.current, interactiveRef.current);
1543
- rfb.clipViewport = false;
1544
- rfb.scaleViewport = scaleViewportRef.current ?? true;
1545
- rfb.addEventListener("connect", onConnect);
1546
- rfb.addEventListener("disconnect", onDisconnect);
1547
- rfb.addEventListener("securityfailure", onSecurityFailure);
1548
- rfbRef.current = rfb;
1238
+ setViewerCapReached(true);
1239
+ } else if (cause.status === 403) {
1240
+ setState("error");
1241
+ setError(cause);
1242
+ return;
1243
+ } else {
1244
+ throw cause;
1245
+ }
1246
+ } else {
1247
+ throw cause;
1248
+ }
1249
+ }
1250
+ }
1251
+ const warmUpRequested = wantDesktopAttach || wantTerminalAttach || wantFilesAttach;
1252
+ if (sandboxAcceptsLiveIo(caps.liveness) || localViewerId) {
1253
+ setState("ready");
1254
+ startHeartbeat(caps);
1255
+ } else if (warmUpRequested) {
1256
+ setState("cold");
1257
+ pollUntilWarm();
1258
+ } else {
1259
+ setState("on-demand");
1260
+ }
1549
1261
  } catch (cause) {
1550
- if (!disposed) {
1551
- setError(cause instanceof Error ? cause : new Error(String(cause)));
1552
- setBoth("error");
1262
+ if (cancelled) return;
1263
+ if (cause instanceof OpenGeniApiError && cause.status === 403) {
1264
+ setState("error");
1265
+ setError(new Error("not permitted to view this session's sandbox"));
1266
+ return;
1553
1267
  }
1268
+ setState("error");
1269
+ setError(cause instanceof Error ? cause : new Error(String(cause)));
1554
1270
  }
1555
1271
  })();
1556
1272
  return () => {
1557
- disposed = true;
1558
- if (rfbRef.current === rfb) rfbRef.current = null;
1559
- if (rfb) {
1560
- rfb.removeEventListener?.("connect", onConnect);
1561
- rfb.removeEventListener?.("disconnect", onDisconnect);
1562
- rfb.removeEventListener?.("securityfailure", onSecurityFailure);
1563
- try {
1564
- rfb.disconnect();
1565
- } catch {
1566
- }
1273
+ cancelled = true;
1274
+ requestAbort.abort();
1275
+ clearTimers();
1276
+ const releaseId = localViewerId ?? viewerIdRef.current;
1277
+ if (releaseId) {
1278
+ void client.detachViewer(workspaceId, sessionId, releaseId).catch(() => {
1279
+ });
1567
1280
  }
1568
1281
  };
1569
- }, [url, token, transport, nonce]);
1570
- useEffect5(() => {
1571
- const rfb = rfbRef.current;
1572
- if (rfb) rfb.viewOnly = viewOnlyFor(mode, interactive);
1573
- }, [interactive, mode, state]);
1574
- useEffect5(() => {
1575
- const rfb = rfbRef.current;
1576
- if (rfb) {
1577
- rfb.clipViewport = false;
1578
- rfb.scaleViewport = scaleViewport ?? true;
1579
- }
1580
- }, [scaleViewport, state]);
1581
- if (transport === "relay-frames") return frameStream;
1582
- return { state, error, reconnect };
1282
+ }, [
1283
+ client,
1284
+ workspaceId,
1285
+ sessionId,
1286
+ lifecycleEnabled,
1287
+ attachDesktop,
1288
+ attachTerminal,
1289
+ attachFiles,
1290
+ warmingPollMs,
1291
+ warmingDeadlineMs,
1292
+ nonce,
1293
+ identityKey
1294
+ ]);
1295
+ const events = options.events;
1296
+ useEffect3(() => {
1297
+ if (!events || events.length === 0) return;
1298
+ setCapabilities((prev) => {
1299
+ if (!prev || !prev.DesktopStream.url) return prev;
1300
+ let next = prev.DesktopStream;
1301
+ let changed = false;
1302
+ for (const rotation of rotationsFrom(events)) {
1303
+ if (rotation.viewerId && viewerIdRef.current && rotation.viewerId !== viewerIdRef.current) {
1304
+ continue;
1305
+ }
1306
+ const applied = applyUrlRotation(next, rotation, epochRef.current);
1307
+ if (applied) {
1308
+ next = { ...next, url: applied.url, token: applied.token, expiresAt: applied.expiresAt };
1309
+ epochRef.current = Math.max(epochRef.current, rotation.leaseEpoch);
1310
+ changed = true;
1311
+ }
1312
+ }
1313
+ return changed ? { ...prev, DesktopStream: next, leaseEpoch: epochRef.current } : prev;
1314
+ });
1315
+ }, [events]);
1316
+ const identityMatches = enabled && stateIdentity === identityKey;
1317
+ return {
1318
+ capabilities: identityMatches ? capabilities : null,
1319
+ state: identityMatches ? state : "idle",
1320
+ error: identityMatches ? error : null,
1321
+ acknowledgmentRequired: identityMatches ? acknowledgmentRequired : null,
1322
+ viewerCapReached: identityMatches && viewerCapReached,
1323
+ viewerId: identityMatches ? viewerId : null,
1324
+ renegotiate
1325
+ };
1583
1326
  }
1584
1327
 
1585
1328
  // src/hooks/use-terminal-stream.ts
@@ -1591,11 +1334,51 @@ import {
1591
1334
  ttydResizeFrame,
1592
1335
  TtydServerCommand
1593
1336
  } from "@opengeni/sdk";
1594
- import { useEffect as useEffect6, useMemo as useMemo3, useRef as useRef5, useState as useState4 } from "react";
1337
+ import { useCallback as useCallback12, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
1595
1338
  var MAX_PENDING_TERMINAL_INPUT_CODE_UNITS = 64 * 1024;
1596
1339
  var TERMINAL_CONNECT_EXPIRY_SKEW_MS = 5e3;
1340
+ var TERMINAL_RECONNECT_INITIAL_MS = 100;
1341
+ var TERMINAL_RECONNECT_MAX_MS = 3e3;
1342
+ var RELAY_TAG_OPEN = 1;
1343
+ var RELAY_TAG_OPEN_ACK = 2;
1344
+ var RELAY_TAG_FRAME = 3;
1345
+ function relayDatagram(tag, body) {
1346
+ const bytes = new Uint8Array(body.length + 1);
1347
+ bytes[0] = tag;
1348
+ bytes.set(body, 1);
1349
+ return bytes.buffer;
1350
+ }
1351
+ function relayChannelFromUrl(url) {
1352
+ const endpoint = new URL(url);
1353
+ const channel = {
1354
+ channelId: endpoint.searchParams.get("channel") ?? "",
1355
+ workspaceId: endpoint.searchParams.get("ws") ?? "",
1356
+ agentId: endpoint.searchParams.get("agent") ?? "",
1357
+ kind: STREAM_KIND_PTY,
1358
+ port: Number(endpoint.searchParams.get("port") ?? "0")
1359
+ };
1360
+ if (!channel.channelId || !channel.workspaceId || !channel.agentId || !Number.isSafeInteger(channel.port) || channel.port <= 0) {
1361
+ throw new Error("terminal relay endpoint is incomplete");
1362
+ }
1363
+ return channel;
1364
+ }
1365
+ function sendRelayInput(socket, channel, sequence, data) {
1366
+ socket.send(
1367
+ relayDatagram(
1368
+ RELAY_TAG_FRAME,
1369
+ encodeStreamFrame({
1370
+ channelId: channel.channelId,
1371
+ seq: sequence.toString(),
1372
+ data: new TextEncoder().encode(data),
1373
+ producedAtMs: String(Date.now())
1374
+ })
1375
+ )
1376
+ );
1377
+ }
1597
1378
  function terminalStreamCredentialUsable(capability, nowMs = Date.now()) {
1598
- if (capability?.transport !== "pty-ws" || !capability.url) return false;
1379
+ if (capability?.transport !== "pty-ws" && capability?.transport !== "relay-pty" || !capability.url) {
1380
+ return false;
1381
+ }
1599
1382
  if (!capability.expiresAt) return true;
1600
1383
  const expiresAt = Date.parse(capability.expiresAt);
1601
1384
  return Number.isFinite(expiresAt) && expiresAt > nowMs + TERMINAL_CONNECT_EXPIRY_SKEW_MS;
@@ -1616,32 +1399,58 @@ function closeSocket(socket) {
1616
1399
  }
1617
1400
  }
1618
1401
  function useTerminalStream(options) {
1619
- const { capability, onOutput, onTitle, initialCols, initialRows } = options;
1620
- const [status, setStatus] = useState4("closed");
1621
- const wsRef = useRef5(null);
1622
- const pendingInputRef = useRef5("");
1623
- const socketFailedRef = useRef5(false);
1624
- const socketAuthenticatedRef = useRef5(false);
1625
- const sizeRef = useRef5({
1402
+ const { capability, onOutput, onTitle, onReconnectNeeded, initialCols, initialRows } = options;
1403
+ const [status, setStatus] = useState2("closed");
1404
+ const [reconnectGeneration, setReconnectGeneration] = useState2(0);
1405
+ const wsRef = useRef3(null);
1406
+ const pendingInputRef = useRef3("");
1407
+ const socketFailedRef = useRef3(false);
1408
+ const socketAuthenticatedRef = useRef3(false);
1409
+ const relayChannelRef = useRef3(null);
1410
+ const relayIdentityRef = useRef3(null);
1411
+ const relayInputSequenceRef = useRef3(0n);
1412
+ const relayOutputSequenceRef = useRef3(0n);
1413
+ const reconnectAttemptRef = useRef3(0);
1414
+ const credentialIdentityRef = useRef3(null);
1415
+ const refreshRequestedRef = useRef3(false);
1416
+ const intentionalCloseRef = useRef3(/* @__PURE__ */ new WeakSet());
1417
+ const sizeRef = useRef3({
1626
1418
  cols: initialCols ?? 80,
1627
1419
  rows: initialRows ?? 24
1628
1420
  });
1629
- const onOutputRef = useRef5(onOutput);
1630
- const onTitleRef = useRef5(onTitle);
1421
+ const onOutputRef = useRef3(onOutput);
1422
+ const onTitleRef = useRef3(onTitle);
1423
+ const onReconnectNeededRef = useRef3(onReconnectNeeded);
1631
1424
  onOutputRef.current = onOutput;
1632
1425
  onTitleRef.current = onTitle;
1426
+ onReconnectNeededRef.current = onReconnectNeeded;
1633
1427
  const transport = capability?.transport ?? null;
1634
1428
  const url = capability?.url ?? null;
1635
1429
  const token = capability?.token ?? null;
1636
1430
  const expiresAt = capability?.expiresAt ?? null;
1637
- useEffect6(() => {
1431
+ const transportRef = useRef3(transport);
1432
+ transportRef.current = transport;
1433
+ useEffect4(() => {
1638
1434
  if (typeof window === "undefined" || typeof WebSocket === "undefined") return;
1639
- if (transport !== "pty-ws") {
1435
+ const relay = transport === "relay-pty";
1436
+ if (transport !== "pty-ws" && !relay) {
1640
1437
  pendingInputRef.current = "";
1641
1438
  socketAuthenticatedRef.current = false;
1439
+ relayIdentityRef.current = null;
1440
+ relayInputSequenceRef.current = 0n;
1441
+ relayOutputSequenceRef.current = 0n;
1442
+ reconnectAttemptRef.current = 0;
1443
+ credentialIdentityRef.current = null;
1444
+ refreshRequestedRef.current = false;
1642
1445
  setStatus("closed");
1643
1446
  return;
1644
1447
  }
1448
+ const credentialIdentity = `${transport}\0${url ?? ""}\0${token ?? ""}\0${expiresAt ?? ""}`;
1449
+ if (credentialIdentityRef.current !== credentialIdentity) {
1450
+ credentialIdentityRef.current = credentialIdentity;
1451
+ reconnectAttemptRef.current = 0;
1452
+ refreshRequestedRef.current = false;
1453
+ }
1645
1454
  socketFailedRef.current = false;
1646
1455
  socketAuthenticatedRef.current = false;
1647
1456
  if (!url) {
@@ -1650,13 +1459,37 @@ function useTerminalStream(options) {
1650
1459
  }
1651
1460
  if (!terminalStreamCredentialUsable({ transport, url, expiresAt })) {
1652
1461
  setStatus("closed");
1462
+ if (!refreshRequestedRef.current) {
1463
+ refreshRequestedRef.current = true;
1464
+ onReconnectNeededRef.current?.();
1465
+ }
1653
1466
  return;
1654
1467
  }
1655
1468
  let disposed = false;
1469
+ let reconnectTimer = null;
1656
1470
  let socket;
1471
+ let relayChannel = null;
1472
+ const relayOutputDecoder = new TextDecoder();
1657
1473
  setStatus("connecting");
1658
1474
  try {
1659
- socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);
1475
+ if (relay) {
1476
+ relayChannel = relayChannelFromUrl(url);
1477
+ const relayIdentity = [
1478
+ relayChannel.workspaceId,
1479
+ relayChannel.agentId,
1480
+ relayChannel.port,
1481
+ relayChannel.channelId
1482
+ ].join("\0");
1483
+ if (relayIdentityRef.current !== relayIdentity) {
1484
+ relayIdentityRef.current = relayIdentity;
1485
+ relayInputSequenceRef.current = 0n;
1486
+ relayOutputSequenceRef.current = 0n;
1487
+ }
1488
+ relayChannelRef.current = relayChannel;
1489
+ socket = new WebSocket(url);
1490
+ } else {
1491
+ socket = new WebSocket(terminalSocketUrl({ url }), TTYD_SUBPROTOCOL);
1492
+ }
1660
1493
  } catch {
1661
1494
  socketFailedRef.current = true;
1662
1495
  setStatus("error");
@@ -1664,8 +1497,52 @@ function useTerminalStream(options) {
1664
1497
  }
1665
1498
  socket.binaryType = "arraybuffer";
1666
1499
  wsRef.current = socket;
1500
+ const requestFreshGrant = () => {
1501
+ if (disposed || refreshRequestedRef.current || intentionalCloseRef.current.has(socket))
1502
+ return;
1503
+ refreshRequestedRef.current = true;
1504
+ onReconnectNeededRef.current?.();
1505
+ };
1506
+ const scheduleTransportReconnect = () => {
1507
+ if (disposed || reconnectTimer !== null || intentionalCloseRef.current.has(socket)) return;
1508
+ if (refreshRequestedRef.current) return;
1509
+ if (!terminalStreamCredentialUsable({ transport, url, expiresAt })) {
1510
+ requestFreshGrant();
1511
+ return;
1512
+ }
1513
+ const attempt = reconnectAttemptRef.current++;
1514
+ const delay = Math.min(
1515
+ TERMINAL_RECONNECT_INITIAL_MS * 2 ** Math.min(attempt, 8),
1516
+ TERMINAL_RECONNECT_MAX_MS
1517
+ );
1518
+ setStatus("connecting");
1519
+ reconnectTimer = setTimeout(() => {
1520
+ reconnectTimer = null;
1521
+ if (!disposed) setReconnectGeneration((generation) => generation + 1);
1522
+ }, delay);
1523
+ };
1667
1524
  socket.onopen = () => {
1668
1525
  if (disposed) return;
1526
+ if (relay && relayChannel) {
1527
+ try {
1528
+ socket.send(
1529
+ relayDatagram(
1530
+ RELAY_TAG_OPEN,
1531
+ encodeStreamOpen({
1532
+ channel: relayChannel,
1533
+ token: token ?? "",
1534
+ role: STREAM_ROLE_CLIENT,
1535
+ resumeFromSeq: relayOutputSequenceRef.current.toString()
1536
+ })
1537
+ )
1538
+ );
1539
+ } catch {
1540
+ socketFailedRef.current = false;
1541
+ setStatus("connecting");
1542
+ closeSocket(socket);
1543
+ }
1544
+ return;
1545
+ }
1669
1546
  try {
1670
1547
  socket.send(ttydAuthFrame({ columns: sizeRef.current.cols, rows: sizeRef.current.rows }));
1671
1548
  socket.send(ttydResizeFrame(sizeRef.current.cols, sizeRef.current.rows));
@@ -1675,15 +1552,60 @@ function useTerminalStream(options) {
1675
1552
  }
1676
1553
  socketAuthenticatedRef.current = true;
1677
1554
  } catch {
1678
- socketFailedRef.current = true;
1679
- setStatus("error");
1555
+ socketFailedRef.current = false;
1556
+ setStatus("connecting");
1680
1557
  closeSocket(socket);
1681
1558
  return;
1682
1559
  }
1683
1560
  setStatus("open");
1561
+ reconnectAttemptRef.current = 0;
1562
+ refreshRequestedRef.current = false;
1684
1563
  };
1685
1564
  socket.onmessage = (ev) => {
1686
1565
  if (disposed) return;
1566
+ if (relay) {
1567
+ if (!(ev.data instanceof ArrayBuffer) || !relayChannel) return;
1568
+ const bytes = new Uint8Array(ev.data);
1569
+ if (bytes.length < 1) return;
1570
+ const tag = bytes[0];
1571
+ const body = bytes.subarray(1);
1572
+ if (tag === RELAY_TAG_OPEN_ACK) {
1573
+ try {
1574
+ const ack = decodeStreamOpenAck(body);
1575
+ if (!ack.accepted) throw new Error(ack.error?.message ?? "terminal relay rejected");
1576
+ reconnectAttemptRef.current = 0;
1577
+ refreshRequestedRef.current = false;
1578
+ socketAuthenticatedRef.current = true;
1579
+ if (pendingInputRef.current.length > 0) {
1580
+ sendRelayInput(
1581
+ socket,
1582
+ relayChannel,
1583
+ relayInputSequenceRef.current++,
1584
+ pendingInputRef.current
1585
+ );
1586
+ pendingInputRef.current = "";
1587
+ }
1588
+ setStatus("open");
1589
+ } catch {
1590
+ socketFailedRef.current = true;
1591
+ setStatus("error");
1592
+ requestFreshGrant();
1593
+ closeSocket(socket);
1594
+ }
1595
+ } else if (tag === RELAY_TAG_FRAME && socketAuthenticatedRef.current) {
1596
+ try {
1597
+ const frame = decodeStreamFrame(body);
1598
+ const sequence = BigInt(frame.seq);
1599
+ if (sequence < relayOutputSequenceRef.current) return;
1600
+ relayOutputSequenceRef.current = sequence + 1n;
1601
+ if (frame.data.length > 0) {
1602
+ onOutputRef.current?.(relayOutputDecoder.decode(frame.data, { stream: true }));
1603
+ }
1604
+ } catch {
1605
+ }
1606
+ }
1607
+ return;
1608
+ }
1687
1609
  const { command, payload } = decodeFrame(ev.data);
1688
1610
  switch (command) {
1689
1611
  case TtydServerCommand.OUTPUT:
@@ -1699,76 +1621,90 @@ function useTerminalStream(options) {
1699
1621
  };
1700
1622
  socket.onerror = () => {
1701
1623
  if (!disposed) {
1702
- socketFailedRef.current = true;
1703
- setStatus("error");
1624
+ socketFailedRef.current = false;
1625
+ setStatus("connecting");
1626
+ closeSocket(socket);
1704
1627
  }
1705
1628
  };
1706
1629
  socket.onclose = () => {
1707
1630
  if (wsRef.current === socket) wsRef.current = null;
1708
1631
  socketAuthenticatedRef.current = false;
1709
- if (!disposed) setStatus(socketFailedRef.current ? "error" : "closed");
1632
+ if (!disposed) {
1633
+ if (socketFailedRef.current) setStatus("error");
1634
+ else scheduleTransportReconnect();
1635
+ }
1710
1636
  };
1711
1637
  return () => {
1712
1638
  disposed = true;
1713
1639
  wsRef.current = null;
1714
1640
  socketAuthenticatedRef.current = false;
1641
+ relayChannelRef.current = null;
1642
+ if (reconnectTimer !== null) clearTimeout(reconnectTimer);
1715
1643
  socket.onopen = null;
1716
1644
  socket.onmessage = null;
1717
1645
  socket.onerror = null;
1718
1646
  socket.onclose = null;
1719
1647
  closeSocket(socket);
1720
1648
  };
1721
- }, [transport, url, token, expiresAt]);
1722
- return useMemo3(() => {
1723
- const write = (data) => {
1724
- const ws = wsRef.current;
1725
- if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
1726
- try {
1649
+ }, [transport, url, token, expiresAt, reconnectGeneration]);
1650
+ const write = useCallback12((data) => {
1651
+ const currentTransport = transportRef.current;
1652
+ const ws = wsRef.current;
1653
+ if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
1654
+ try {
1655
+ const relayChannel = relayChannelRef.current;
1656
+ if (currentTransport === "relay-pty" && relayChannel) {
1657
+ sendRelayInput(ws, relayChannel, relayInputSequenceRef.current++, data);
1658
+ } else {
1727
1659
  ws.send(ttydInputFrame(data));
1728
- } catch {
1729
- socketFailedRef.current = true;
1730
- setStatus("error");
1731
- closeSocket(ws);
1732
- }
1733
- } else if (transport === "pty-ws" && !socketFailedRef.current) {
1734
- const nextSize = pendingInputRef.current.length + data.length;
1735
- if (nextSize > MAX_PENDING_TERMINAL_INPUT_CODE_UNITS) {
1736
- pendingInputRef.current = "";
1737
- socketFailedRef.current = true;
1738
- setStatus("error");
1739
- closeSocket(ws);
1740
- return;
1741
1660
  }
1742
- pendingInputRef.current += data;
1661
+ } catch {
1662
+ socketFailedRef.current = false;
1663
+ setStatus("connecting");
1664
+ closeSocket(ws);
1743
1665
  }
1744
- };
1745
- const resize = (cols, rows) => {
1746
- if (cols <= 0 || rows <= 0) return;
1747
- sizeRef.current = { cols, rows };
1748
- const ws = wsRef.current;
1749
- if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
1750
- try {
1751
- ws.send(ttydResizeFrame(cols, rows));
1752
- } catch {
1753
- }
1666
+ } else if ((currentTransport === "pty-ws" || currentTransport === "relay-pty") && !socketFailedRef.current) {
1667
+ const nextSize = pendingInputRef.current.length + data.length;
1668
+ if (nextSize > MAX_PENDING_TERMINAL_INPUT_CODE_UNITS) {
1669
+ pendingInputRef.current = "";
1670
+ socketFailedRef.current = true;
1671
+ setStatus("error");
1672
+ closeSocket(ws);
1673
+ return;
1754
1674
  }
1755
- };
1756
- const disconnect = () => {
1757
- const ws = wsRef.current;
1758
- wsRef.current = null;
1759
- pendingInputRef.current = "";
1760
- socketFailedRef.current = false;
1761
- socketAuthenticatedRef.current = false;
1762
- setStatus("closed");
1763
- closeSocket(ws);
1764
- };
1675
+ pendingInputRef.current += data;
1676
+ }
1677
+ }, []);
1678
+ const resize = useCallback12((cols, rows) => {
1679
+ if (cols <= 0 || rows <= 0) return;
1680
+ sizeRef.current = { cols, rows };
1681
+ const ws = wsRef.current;
1682
+ if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
1683
+ try {
1684
+ if (transportRef.current === "pty-ws") ws.send(ttydResizeFrame(cols, rows));
1685
+ } catch {
1686
+ }
1687
+ }
1688
+ }, []);
1689
+ const disconnect = useCallback12(() => {
1690
+ const ws = wsRef.current;
1691
+ wsRef.current = null;
1692
+ pendingInputRef.current = "";
1693
+ socketFailedRef.current = false;
1694
+ socketAuthenticatedRef.current = false;
1695
+ relayChannelRef.current = null;
1696
+ setStatus("closed");
1697
+ if (ws) intentionalCloseRef.current.add(ws);
1698
+ closeSocket(ws);
1699
+ }, []);
1700
+ return useMemo3(() => {
1765
1701
  return { connected: status === "open", status, write, resize, disconnect };
1766
- }, [status, transport]);
1702
+ }, [status, write, resize, disconnect]);
1767
1703
  }
1768
1704
 
1769
1705
  // src/hooks/use-sandbox-terminal.ts
1770
1706
  import { OpenGeniApiError as OpenGeniApiError2 } from "@opengeni/sdk";
1771
- import { useCallback as useCallback11, useEffect as useEffect7, useMemo as useMemo4, useRef as useRef6, useState as useState5 } from "react";
1707
+ import { useCallback as useCallback13, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4, useState as useState3 } from "react";
1772
1708
  function useSandboxTerminal(sessionId, options) {
1773
1709
  const { client, workspaceId } = useOpenGeni(options);
1774
1710
  const includeAgentFirehose = options.includeAgentFirehose ?? true;
@@ -1776,20 +1712,20 @@ function useSandboxTerminal(sessionId, options) {
1776
1712
  const ptyFilter = options.ptyId;
1777
1713
  const liveness = options.liveness;
1778
1714
  const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${interactive}\0${ptyFilter ?? ""}`;
1779
- const [openedPtyId, setOpenedPtyId] = useState5(null);
1780
- const [openError, setOpenError] = useState5(null);
1781
- const [stateIdentity, setStateIdentity] = useState5(identityKey);
1782
- const [reopenNonce, setReopenNonce] = useState5(0);
1783
- const currentIdentityRef = useRef6(identityKey);
1715
+ const [openedPtyId, setOpenedPtyId] = useState3(null);
1716
+ const [openError, setOpenError] = useState3(null);
1717
+ const [stateIdentity, setStateIdentity] = useState3(identityKey);
1718
+ const [reopenNonce, setReopenNonce] = useState3(0);
1719
+ const currentIdentityRef = useRef4(identityKey);
1784
1720
  currentIdentityRef.current = identityKey;
1785
- useEffect7(() => {
1721
+ useEffect5(() => {
1786
1722
  setStateIdentity(identityKey);
1787
1723
  setOpenedPtyId(null);
1788
1724
  setOpenError(null);
1789
1725
  }, [identityKey]);
1790
- const openInFlight = useRef6(false);
1726
+ const openInFlight = useRef4(false);
1791
1727
  const boxWarm = liveness === void 0 || sandboxAcceptsLiveIo(liveness);
1792
- useEffect7(() => {
1728
+ useEffect5(() => {
1793
1729
  if (!interactive || !sessionId || ptyFilter || !boxWarm) {
1794
1730
  setOpenError(null);
1795
1731
  return;
@@ -1884,7 +1820,7 @@ function useSandboxTerminal(sessionId, options) {
1884
1820
  });
1885
1821
  };
1886
1822
  }, [client, workspaceId, sessionId, activePtyId, canWrite, openedPtyId, identityKey]);
1887
- const close = useCallback11(() => {
1823
+ const close = useCallback13(() => {
1888
1824
  if (!activePtyId || !sessionId) return;
1889
1825
  void client.terminalPtyClose(workspaceId, sessionId, { ptyId: activePtyId }).catch(() => {
1890
1826
  });
@@ -1900,7 +1836,7 @@ function useSandboxTerminal(sessionId, options) {
1900
1836
  }
1901
1837
 
1902
1838
  // src/hooks/use-sandbox-files.ts
1903
- import { useCallback as useCallback12, useEffect as useEffect8, useMemo as useMemo5, useRef as useRef7, useState as useState6 } from "react";
1839
+ import { useCallback as useCallback14, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef5, useState as useState4 } from "react";
1904
1840
  var CapturedFileUnavailableError = class extends Error {
1905
1841
  constructor(path, reason) {
1906
1842
  super(
@@ -2138,37 +2074,37 @@ function useSandboxFiles(sessionId, options = {}) {
2138
2074
  const capture = options.capture ?? null;
2139
2075
  const isLive2 = sandboxAcceptsLiveIo(options.liveness);
2140
2076
  const acceptsEventReads = options.liveness === void 0 || isLive2;
2141
- const acceptsEventReadsRef = useRef7(acceptsEventReads);
2077
+ const acceptsEventReadsRef = useRef5(acceptsEventReads);
2142
2078
  acceptsEventReadsRef.current = acceptsEventReads;
2143
2079
  const hasLiveIoFence = options.liveness !== void 0;
2144
2080
  const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${rootPath}\0${repoPathsKey}`;
2145
- const [tree, setTree] = useState6([]);
2146
- const treeRef = useRef7([]);
2147
- const [loading, setLoading] = useState6(false);
2148
- const [expandingPaths, setExpandingPaths] = useState6(/* @__PURE__ */ new Set());
2149
- const [error, setError] = useState6(null);
2150
- const [gitSummary, setGitSummary] = useState6(null);
2151
- const [gitLoading, setGitLoading] = useState6(false);
2152
- const [contentRevision, setContentRevision] = useState6(0);
2153
- const [stateIdentity, setStateIdentity] = useState6(identityKey);
2154
- const [source, setSource] = useState6(null);
2155
- const sourceRef = useRef7(null);
2156
- const statusRef = useRef7(/* @__PURE__ */ new Map());
2157
- const refreshGenerationRef = useRef7(0);
2158
- const refreshAbortRef = useRef7(null);
2159
- const identityAbortRef = useRef7(new AbortController());
2160
- const eventReadAbortRef = useRef7(new AbortController());
2161
- const identityGenerationRef = useRef7(0);
2162
- const lastSeqRef = useRef7(0);
2163
- const pendingParentsRef = useRef7(/* @__PURE__ */ new Set());
2164
- const pendingGitRef = useRef7(false);
2165
- const pendingAgentToolRef = useRef7(false);
2166
- const debounceRef = useRef7(null);
2167
- const lastCommandRefreshAtRef = useRef7(0);
2168
- const ownRevisionsRef = useRef7(/* @__PURE__ */ new Set());
2081
+ const [tree, setTree] = useState4([]);
2082
+ const treeRef = useRef5([]);
2083
+ const [loading, setLoading] = useState4(false);
2084
+ const [expandingPaths, setExpandingPaths] = useState4(/* @__PURE__ */ new Set());
2085
+ const [error, setError] = useState4(null);
2086
+ const [gitSummary, setGitSummary] = useState4(null);
2087
+ const [gitLoading, setGitLoading] = useState4(false);
2088
+ const [contentRevision, setContentRevision] = useState4(0);
2089
+ const [stateIdentity, setStateIdentity] = useState4(identityKey);
2090
+ const [source, setSource] = useState4(null);
2091
+ const sourceRef = useRef5(null);
2092
+ const statusRef = useRef5(/* @__PURE__ */ new Map());
2093
+ const refreshGenerationRef = useRef5(0);
2094
+ const refreshAbortRef = useRef5(null);
2095
+ const identityAbortRef = useRef5(new AbortController());
2096
+ const eventReadAbortRef = useRef5(new AbortController());
2097
+ const identityGenerationRef = useRef5(0);
2098
+ const lastSeqRef = useRef5(0);
2099
+ const pendingParentsRef = useRef5(/* @__PURE__ */ new Set());
2100
+ const pendingGitRef = useRef5(false);
2101
+ const pendingAgentToolRef = useRef5(false);
2102
+ const debounceRef = useRef5(null);
2103
+ const lastCommandRefreshAtRef = useRef5(0);
2104
+ const ownRevisionsRef = useRef5(/* @__PURE__ */ new Set());
2169
2105
  const onMutationError = options.onMutationError;
2170
2106
  treeRef.current = tree;
2171
- const applyStatus = useCallback12((nodes) => {
2107
+ const applyStatus = useCallback14((nodes) => {
2172
2108
  const overlay = statusRef.current;
2173
2109
  const walk = (list) => {
2174
2110
  let changed = false;
@@ -2191,7 +2127,7 @@ function useSandboxFiles(sessionId, options = {}) {
2191
2127
  };
2192
2128
  return walk(nodes);
2193
2129
  }, []);
2194
- const refresh = useCallback12(async () => {
2130
+ const refresh = useCallback14(async () => {
2195
2131
  if (!sessionId) return;
2196
2132
  refreshAbortRef.current?.abort();
2197
2133
  const refreshAbort = new AbortController();
@@ -2275,7 +2211,7 @@ function useSandboxFiles(sessionId, options = {}) {
2275
2211
  if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
2276
2212
  }
2277
2213
  }, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
2278
- const seedFromCapture = useCallback12(
2214
+ const seedFromCapture = useCallback14(
2279
2215
  (manifest) => {
2280
2216
  const overlay = /* @__PURE__ */ new Map();
2281
2217
  for (const file of manifest.files) {
@@ -2303,7 +2239,7 @@ function useSandboxFiles(sessionId, options = {}) {
2303
2239
  },
2304
2240
  [applyStatus]
2305
2241
  );
2306
- const expand = useCallback12(
2242
+ const expand = useCallback14(
2307
2243
  async (path) => {
2308
2244
  if (!sessionId) return;
2309
2245
  if (source === "capture" && capture) return;
@@ -2340,7 +2276,7 @@ function useSandboxFiles(sessionId, options = {}) {
2340
2276
  },
2341
2277
  [client, workspaceId, sessionId, capture, source, applyStatus]
2342
2278
  );
2343
- const readFile = useCallback12(
2279
+ const readFile = useCallback14(
2344
2280
  async (path, requestOptions = {}) => {
2345
2281
  if (!sessionId) throw new Error("no session");
2346
2282
  const identitySignal = identityAbortRef.current.signal;
@@ -2415,7 +2351,7 @@ function useSandboxFiles(sessionId, options = {}) {
2415
2351
  },
2416
2352
  [client, workspaceId, sessionId, capture, source]
2417
2353
  );
2418
- const reconcilePath = useCallback12(
2354
+ const reconcilePath = useCallback14(
2419
2355
  async (path, requestSignal) => {
2420
2356
  if (!sessionId) return;
2421
2357
  const identityGeneration = identityGenerationRef.current;
@@ -2438,7 +2374,7 @@ function useSandboxFiles(sessionId, options = {}) {
2438
2374
  },
2439
2375
  [client, workspaceId, sessionId, applyStatus]
2440
2376
  );
2441
- const runOptimistic = useCallback12(
2377
+ const runOptimistic = useCallback14(
2442
2378
  async (opName, apply, op, reconcileParents) => {
2443
2379
  const identityGeneration = identityGenerationRef.current;
2444
2380
  const snapshot = treeRef.current;
@@ -2468,7 +2404,7 @@ function useSandboxFiles(sessionId, options = {}) {
2468
2404
  },
2469
2405
  [applyStatus, reconcilePath, onMutationError]
2470
2406
  );
2471
- const writeFile = useCallback12(
2407
+ const writeFile = useCallback14(
2472
2408
  async (path, content, writeOptions = {}) => {
2473
2409
  if (!sessionId) throw new Error("no session");
2474
2410
  const identityGeneration = identityGenerationRef.current;
@@ -2514,7 +2450,7 @@ function useSandboxFiles(sessionId, options = {}) {
2514
2450
  },
2515
2451
  [client, workspaceId, sessionId, tree, runOptimistic]
2516
2452
  );
2517
- const createFile = useCallback12(
2453
+ const createFile = useCallback14(
2518
2454
  async (path) => {
2519
2455
  if (!sessionId) throw new Error("no session");
2520
2456
  const parent = parentOf(path);
@@ -2537,7 +2473,7 @@ function useSandboxFiles(sessionId, options = {}) {
2537
2473
  },
2538
2474
  [client, workspaceId, sessionId, runOptimistic]
2539
2475
  );
2540
- const createDir = useCallback12(
2476
+ const createDir = useCallback14(
2541
2477
  async (path) => {
2542
2478
  if (!sessionId) throw new Error("no session");
2543
2479
  const parent = parentOf(path);
@@ -2556,7 +2492,7 @@ function useSandboxFiles(sessionId, options = {}) {
2556
2492
  },
2557
2493
  [client, workspaceId, sessionId, runOptimistic]
2558
2494
  );
2559
- const deleteEntry = useCallback12(
2495
+ const deleteEntry = useCallback14(
2560
2496
  async (path, recursive = false) => {
2561
2497
  if (!sessionId) throw new Error("no session");
2562
2498
  await runOptimistic(
@@ -2568,7 +2504,7 @@ function useSandboxFiles(sessionId, options = {}) {
2568
2504
  },
2569
2505
  [client, workspaceId, sessionId, runOptimistic]
2570
2506
  );
2571
- const moveEntry = useCallback12(
2507
+ const moveEntry = useCallback14(
2572
2508
  async (path, newPath, opts) => {
2573
2509
  if (!sessionId) throw new Error("no session");
2574
2510
  const from = parentOf(path);
@@ -2591,11 +2527,11 @@ function useSandboxFiles(sessionId, options = {}) {
2591
2527
  },
2592
2528
  [client, workspaceId, sessionId, runOptimistic]
2593
2529
  );
2594
- const captureRef = useRef7(capture);
2530
+ const captureRef = useRef5(capture);
2595
2531
  captureRef.current = capture;
2596
2532
  const captureRevision = capture?.revision ?? null;
2597
- const previousIdentityRef = useRef7(identityKey);
2598
- useEffect8(() => {
2533
+ const previousIdentityRef = useRef5(identityKey);
2534
+ useEffect6(() => {
2599
2535
  refreshAbortRef.current?.abort();
2600
2536
  refreshAbortRef.current = null;
2601
2537
  refreshGenerationRef.current += 1;
@@ -2663,7 +2599,7 @@ function useSandboxFiles(sessionId, options = {}) {
2663
2599
  refresh,
2664
2600
  seedFromCapture
2665
2601
  ]);
2666
- const refreshGitOverlay = useCallback12(
2602
+ const refreshGitOverlay = useCallback14(
2667
2603
  async (requestSignal) => {
2668
2604
  if (!sessionId) return;
2669
2605
  const identityGeneration = identityGenerationRef.current;
@@ -2701,7 +2637,7 @@ function useSandboxFiles(sessionId, options = {}) {
2701
2637
  [client, workspaceId, sessionId, repoPaths, applyStatus]
2702
2638
  );
2703
2639
  const events = options.events;
2704
- useEffect8(() => {
2640
+ useEffect6(() => {
2705
2641
  if (!enabled || !events) return;
2706
2642
  if (!active || !acceptsEventReads) {
2707
2643
  for (const event of events) {
@@ -2788,7 +2724,7 @@ function useSandboxFiles(sessionId, options = {}) {
2788
2724
  })();
2789
2725
  }, delay);
2790
2726
  }, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
2791
- useEffect8(() => {
2727
+ useEffect6(() => {
2792
2728
  if (active && acceptsEventReads) return;
2793
2729
  if (debounceRef.current) {
2794
2730
  clearTimeout(debounceRef.current);
@@ -2798,7 +2734,7 @@ function useSandboxFiles(sessionId, options = {}) {
2798
2734
  pendingGitRef.current = false;
2799
2735
  pendingAgentToolRef.current = false;
2800
2736
  }, [active, acceptsEventReads]);
2801
- useEffect8(
2737
+ useEffect6(
2802
2738
  () => () => {
2803
2739
  refreshAbortRef.current?.abort();
2804
2740
  identityAbortRef.current.abort();
@@ -2831,7 +2767,7 @@ function useSandboxFiles(sessionId, options = {}) {
2831
2767
  }
2832
2768
 
2833
2769
  // src/hooks/use-sandbox-git.ts
2834
- import { useCallback as useCallback13, useEffect as useEffect9, useMemo as useMemo6, useRef as useRef8, useState as useState7 } from "react";
2770
+ import { useCallback as useCallback15, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef6, useState as useState5 } from "react";
2835
2771
  function normalizeRoot2(root) {
2836
2772
  const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
2837
2773
  return normalized === "." ? "" : normalized;
@@ -2903,26 +2839,26 @@ function useSandboxGit(sessionId, options = {}) {
2903
2839
  const capture = comparison === "staged" ? null : options.capture ?? null;
2904
2840
  const isLive2 = sandboxAcceptsLiveIo(options.liveness);
2905
2841
  const acceptsEventReads = options.liveness === void 0 || isLive2;
2906
- const acceptsEventReadsRef = useRef8(acceptsEventReads);
2842
+ const acceptsEventReadsRef = useRef6(acceptsEventReads);
2907
2843
  acceptsEventReadsRef.current = acceptsEventReads;
2908
2844
  const hasLiveIoFence = options.liveness !== void 0;
2909
2845
  const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${repoPathsKey}\0${workspaceWide}\0${comparison}`;
2910
- const [diff, setDiff] = useState7([]);
2911
- const [branch, setBranch] = useState7(null);
2912
- const [isRepo, setIsRepo] = useState7(false);
2913
- const [ahead, setAhead] = useState7(0);
2914
- const [behind, setBehind] = useState7(0);
2915
- const [repoRoots, setRepoRoots] = useState7([]);
2916
- const [source, setSource] = useState7(null);
2917
- const sourceRef = useRef8(null);
2918
- const [loading, setLoading] = useState7(false);
2919
- const [error, setError] = useState7(null);
2920
- const [stateIdentity, setStateIdentity] = useState7(identityKey);
2921
- const refreshGenerationRef = useRef8(0);
2922
- const refreshAbortRef = useRef8(null);
2923
- const lastChangeRef = useRef8(0);
2924
- const commandRefreshTimerRef = useRef8(null);
2925
- const refresh = useCallback13(async () => {
2846
+ const [diff, setDiff] = useState5([]);
2847
+ const [branch, setBranch] = useState5(null);
2848
+ const [isRepo, setIsRepo] = useState5(false);
2849
+ const [ahead, setAhead] = useState5(0);
2850
+ const [behind, setBehind] = useState5(0);
2851
+ const [repoRoots, setRepoRoots] = useState5([]);
2852
+ const [source, setSource] = useState5(null);
2853
+ const sourceRef = useRef6(null);
2854
+ const [loading, setLoading] = useState5(false);
2855
+ const [error, setError] = useState5(null);
2856
+ const [stateIdentity, setStateIdentity] = useState5(identityKey);
2857
+ const refreshGenerationRef = useRef6(0);
2858
+ const refreshAbortRef = useRef6(null);
2859
+ const lastChangeRef = useRef6(0);
2860
+ const commandRefreshTimerRef = useRef6(null);
2861
+ const refresh = useCallback15(async () => {
2926
2862
  if (!sessionId) return;
2927
2863
  refreshAbortRef.current?.abort();
2928
2864
  const refreshAbort = new AbortController();
@@ -3035,7 +2971,7 @@ function useSandboxGit(sessionId, options = {}) {
3035
2971
  workspaceWide,
3036
2972
  comparison
3037
2973
  ]);
3038
- const seedFromCapture = useCallback13(
2974
+ const seedFromCapture = useCallback15(
3039
2975
  (manifest) => {
3040
2976
  const repositories = workspaceWide ? manifest.repos : [repoForPath(manifest, repoPath)].filter(Boolean);
3041
2977
  if (comparison === "branch" && repositories.some((repo) => repo.branchDiff === void 0)) {
@@ -3072,11 +3008,11 @@ function useSandboxGit(sessionId, options = {}) {
3072
3008
  },
3073
3009
  [comparison, repoPath, workspaceWide]
3074
3010
  );
3075
- const captureRef = useRef8(capture);
3011
+ const captureRef = useRef6(capture);
3076
3012
  captureRef.current = capture;
3077
3013
  const captureRevision = capture?.revision ?? null;
3078
- const previousIdentityRef = useRef8(identityKey);
3079
- useEffect9(() => {
3014
+ const previousIdentityRef = useRef6(identityKey);
3015
+ useEffect7(() => {
3080
3016
  refreshAbortRef.current?.abort();
3081
3017
  refreshAbortRef.current = null;
3082
3018
  refreshGenerationRef.current += 1;
@@ -3126,7 +3062,7 @@ function useSandboxGit(sessionId, options = {}) {
3126
3062
  seedFromCapture
3127
3063
  ]);
3128
3064
  const events = options.events;
3129
- useEffect9(() => {
3065
+ useEffect7(() => {
3130
3066
  if (!enabled || !events) {
3131
3067
  if (commandRefreshTimerRef.current) {
3132
3068
  clearTimeout(commandRefreshTimerRef.current);
@@ -3171,7 +3107,7 @@ function useSandboxGit(sessionId, options = {}) {
3171
3107
  }
3172
3108
  }
3173
3109
  }, [enabled, active, acceptsEventReads, events, refresh]);
3174
- useEffect9(
3110
+ useEffect7(
3175
3111
  () => () => {
3176
3112
  if (commandRefreshTimerRef.current) clearTimeout(commandRefreshTimerRef.current);
3177
3113
  },
@@ -3196,7 +3132,7 @@ function useSandboxGit(sessionId, options = {}) {
3196
3132
  }
3197
3133
 
3198
3134
  // src/hooks/use-workspace-capture.ts
3199
- import { useCallback as useCallback14, useEffect as useEffect10, useRef as useRef9, useState as useState8 } from "react";
3135
+ import { useCallback as useCallback16, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
3200
3136
  var REVISION_CAPTURED = "workspace.revision.captured";
3201
3137
  var REVISION_DEGRADED = "workspace.revision.degraded";
3202
3138
  var MANIFEST_FETCH_TIMEOUT_MS = 15e3;
@@ -3235,18 +3171,18 @@ function useWorkspaceCapture(sessionId, options = {}) {
3235
3171
  const { client, workspaceId } = useOpenGeni(options);
3236
3172
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
3237
3173
  const identityKey = `${workspaceId}\0${sessionId ?? ""}`;
3238
- const [capture, setCapture] = useState8(null);
3239
- const [available, setAvailable] = useState8(false);
3240
- const [degradedReason, setDegradedReason] = useState8(null);
3241
- const [fileCount, setFileCount] = useState8(null);
3242
- const [loading, setLoading] = useState8(false);
3243
- const [error, setError] = useState8(null);
3244
- const [announcedRevision, setAnnouncedRevision] = useState8(null);
3245
- const [stateIdentity, setStateIdentity] = useState8(identityKey);
3246
- const generationRef = useRef9(0);
3247
- const requestAbortRef = useRef9(null);
3248
- const lastSeqRef = useRef9(0);
3249
- const refresh = useCallback14(async () => {
3174
+ const [capture, setCapture] = useState6(null);
3175
+ const [available, setAvailable] = useState6(false);
3176
+ const [degradedReason, setDegradedReason] = useState6(null);
3177
+ const [fileCount, setFileCount] = useState6(null);
3178
+ const [loading, setLoading] = useState6(false);
3179
+ const [error, setError] = useState6(null);
3180
+ const [announcedRevision, setAnnouncedRevision] = useState6(null);
3181
+ const [stateIdentity, setStateIdentity] = useState6(identityKey);
3182
+ const generationRef = useRef7(0);
3183
+ const requestAbortRef = useRef7(null);
3184
+ const lastSeqRef = useRef7(0);
3185
+ const refresh = useCallback16(async () => {
3250
3186
  if (!sessionId) return;
3251
3187
  requestAbortRef.current?.abort();
3252
3188
  const requestAbort = new AbortController();
@@ -3319,7 +3255,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3319
3255
  if (requestAbortRef.current === requestAbort) requestAbortRef.current = null;
3320
3256
  }
3321
3257
  }, [client, workspaceId, sessionId, identityKey]);
3322
- useEffect10(() => {
3258
+ useEffect8(() => {
3323
3259
  requestAbortRef.current?.abort();
3324
3260
  requestAbortRef.current = null;
3325
3261
  generationRef.current += 1;
@@ -3342,7 +3278,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3342
3278
  };
3343
3279
  }, [enabled, refresh]);
3344
3280
  const events = options.events;
3345
- useEffect10(() => {
3281
+ useEffect8(() => {
3346
3282
  if (!enabled || !events) return;
3347
3283
  let newest = null;
3348
3284
  for (const event of events) {
@@ -3368,7 +3304,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3368
3304
  const visibleCapture = identityMatches ? capture : null;
3369
3305
  const visibleAnnouncedRevision = identityMatches ? announcedRevision : null;
3370
3306
  const loadedRevision = visibleCapture?.revision ?? null;
3371
- useEffect10(() => {
3307
+ useEffect8(() => {
3372
3308
  if (!enabled) return;
3373
3309
  if (visibleAnnouncedRevision === null) return;
3374
3310
  if (loadedRevision !== null && visibleAnnouncedRevision <= loadedRevision) return;
@@ -3391,7 +3327,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3391
3327
  }
3392
3328
 
3393
3329
  // src/hooks/use-workspace-edit.ts
3394
- import { useCallback as useCallback15, useEffect as useEffect11, useRef as useRef10, useState as useState9 } from "react";
3330
+ import { useCallback as useCallback17, useEffect as useEffect9, useRef as useRef8, useState as useState7 } from "react";
3395
3331
  function isLive(liveness) {
3396
3332
  return sandboxAcceptsLiveIo(liveness);
3397
3333
  }
@@ -3402,20 +3338,20 @@ function useWorkspaceEdit(sessionId, options = {}) {
3402
3338
  const live = isLive(options.liveness);
3403
3339
  const offline = options.offline === true;
3404
3340
  const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${path ?? ""}`;
3405
- const [buffer, setBuffer] = useState9(null);
3406
- const [wantsWarm, setWantsWarm] = useState9(false);
3407
- const [flush, setFlush] = useState9("idle");
3408
- const [conflict, setConflict] = useState9(null);
3409
- const [error, setError] = useState9(null);
3410
- const [stateIdentity, setStateIdentity] = useState9(identityKey);
3341
+ const [buffer, setBuffer] = useState7(null);
3342
+ const [wantsWarm, setWantsWarm] = useState7(false);
3343
+ const [flush, setFlush] = useState7("idle");
3344
+ const [conflict, setConflict] = useState7(null);
3345
+ const [error, setError] = useState7(null);
3346
+ const [stateIdentity, setStateIdentity] = useState7(identityKey);
3411
3347
  const onWarmRequested = options.onWarmRequested;
3412
- const bufferRef = useRef10(null);
3348
+ const bufferRef = useRef8(null);
3413
3349
  bufferRef.current = buffer;
3414
- const flushingRef = useRef10(false);
3415
- const identityGenerationRef = useRef10(0);
3416
- const flushAttemptRef = useRef10(0);
3417
- const readAbortRef = useRef10(null);
3418
- useEffect11(() => {
3350
+ const flushingRef = useRef8(false);
3351
+ const identityGenerationRef = useRef8(0);
3352
+ const flushAttemptRef = useRef8(0);
3353
+ const readAbortRef = useRef8(null);
3354
+ useEffect9(() => {
3419
3355
  identityGenerationRef.current += 1;
3420
3356
  flushAttemptRef.current += 1;
3421
3357
  readAbortRef.current?.abort();
@@ -3434,7 +3370,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3434
3370
  readAbortRef.current = null;
3435
3371
  };
3436
3372
  }, [identityKey]);
3437
- const edit = useCallback15(
3373
+ const edit = useCallback17(
3438
3374
  (content) => {
3439
3375
  if (stateIdentity !== identityKey) return;
3440
3376
  if (offline) return;
@@ -3450,7 +3386,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3450
3386
  },
3451
3387
  [offline, live, onWarmRequested, stateIdentity, identityKey]
3452
3388
  );
3453
- const doFlush = useCallback15(
3389
+ const doFlush = useCallback17(
3454
3390
  async (force) => {
3455
3391
  if (stateIdentity !== identityKey) return;
3456
3392
  if (!sessionId || !path) return;
@@ -3500,17 +3436,17 @@ function useWorkspaceEdit(sessionId, options = {}) {
3500
3436
  },
3501
3437
  [client, workspaceId, sessionId, path, baseContent, stateIdentity, identityKey]
3502
3438
  );
3503
- useEffect11(() => {
3439
+ useEffect9(() => {
3504
3440
  if (offline) return;
3505
3441
  if (!live) return;
3506
3442
  if (buffer === null) return;
3507
3443
  if (flush === "flushed" || flush === "conflict" || flush === "flushing") return;
3508
3444
  void doFlush(false);
3509
3445
  }, [offline, live, buffer, flush, doFlush]);
3510
- const overwrite = useCallback15(async () => {
3446
+ const overwrite = useCallback17(async () => {
3511
3447
  await doFlush(true);
3512
3448
  }, [doFlush]);
3513
- const discard = useCallback15(() => {
3449
+ const discard = useCallback17(() => {
3514
3450
  setBuffer(null);
3515
3451
  setWantsWarm(false);
3516
3452
  setFlush("idle");
@@ -3567,7 +3503,8 @@ function deriveMachineChip(input) {
3567
3503
  const now = input.now ?? Date.now();
3568
3504
  const asOf = input.capturedAt ?? null;
3569
3505
  const offlineLabel = asOf ? `Offline \u2014 as of ${formatAsOf(asOf, now)}` : "Offline";
3570
- if (sandboxAcceptsLiveIo(input.liveness)) {
3506
+ const selfhostedOnline = input.activeIsSelfhosted === true && input.activeMachineState != null && connectionStatusForState(input.activeMachineState) === "online";
3507
+ if (sandboxAcceptsLiveIo(input.liveness) || selfhostedOnline) {
3571
3508
  return { state: "live", label: "Live", asOf: null };
3572
3509
  }
3573
3510
  const selfhostedOffline = input.activeIsSelfhosted === true && input.activeMachineState === "offline";
@@ -3613,7 +3550,7 @@ function useMachineChip(input) {
3613
3550
 
3614
3551
  // src/components/approval-surface.tsx
3615
3552
  import { CheckIcon, ShieldCheckIcon, XIcon } from "lucide-react";
3616
- import { useEffect as useEffect12, useId, useRef as useRef11, useState as useState10 } from "react";
3553
+ import { useEffect as useEffect10, useId, useRef as useRef9, useState as useState8 } from "react";
3617
3554
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
3618
3555
  var defaultApprovalSurfaceMessages = {
3619
3556
  title: "Approval required",
@@ -3653,10 +3590,10 @@ function ApprovalSurface({
3653
3590
  }) {
3654
3591
  const titleId = useId();
3655
3592
  const messages = { ...defaultApprovalSurfaceMessages, ...overrides };
3656
- const [pending, setPending] = useState10(null);
3657
- const pendingRef = useRef11(null);
3658
- const [decisionError, setDecisionError] = useState10(null);
3659
- useEffect12(() => {
3593
+ const [pending, setPending] = useState8(null);
3594
+ const pendingRef = useRef9(null);
3595
+ const [decisionError, setDecisionError] = useState8(null);
3596
+ useEffect10(() => {
3660
3597
  if (pending && !approvals.some((approval) => approvalOwnershipKey(approval) === pending.approvalKey)) {
3661
3598
  if (pendingRef.current?.token === pending.token) {
3662
3599
  pendingRef.current = null;
@@ -3956,7 +3893,7 @@ function FleetTile({ session, title, subtitle, onOpen, className }) {
3956
3893
  }
3957
3894
 
3958
3895
  // src/components/sandbox-terminal.tsx
3959
- import { useEffect as useEffect13, useRef as useRef12, useState as useState11 } from "react";
3896
+ import { useEffect as useEffect11, useLayoutEffect, useRef as useRef10, useState as useState9 } from "react";
3960
3897
 
3961
3898
  // src/lib/xterm-theme.ts
3962
3899
  var DARK_ANSI = {
@@ -4148,19 +4085,20 @@ function SandboxTerminal({
4148
4085
  showHeader,
4149
4086
  shell,
4150
4087
  onActivate,
4088
+ onReconnectNeeded,
4151
4089
  className
4152
4090
  }) {
4153
- const containerRef = useRef12(null);
4154
- const termRef = useRef12(null);
4155
- const fitRef = useRef12(null);
4156
- const rendererRef = useRef12(null);
4157
- const writtenRef = useRef12(/* @__PURE__ */ new Set());
4158
- const wroteFirehoseRef = useRef12(false);
4159
- const bootActiveRef = useRef12(false);
4160
- const [ready, setReady] = useState11(false);
4161
- const [inputReady, setInputReady] = useState11(false);
4162
- const [activated, setActivated] = useState11(false);
4163
- const ptyDescriptor = terminalCapability?.transport === "pty-ws";
4091
+ const containerRef = useRef10(null);
4092
+ const termRef = useRef10(null);
4093
+ const fitRef = useRef10(null);
4094
+ const rendererRef = useRef10(null);
4095
+ const writtenRef = useRef10(/* @__PURE__ */ new Set());
4096
+ const wroteFirehoseRef = useRef10(false);
4097
+ const bootActiveRef = useRef10(false);
4098
+ const [ready, setReady] = useState9(false);
4099
+ const [inputReady, setInputReady] = useState9(false);
4100
+ const [activated, setActivated] = useState9(false);
4101
+ const ptyDescriptor = terminalCapability?.transport === "pty-ws" || terminalCapability?.transport === "relay-pty";
4164
4102
  const ptyAttachable = terminalCanAcquirePty(terminalCapability);
4165
4103
  const {
4166
4104
  status: ptyStatus,
@@ -4172,14 +4110,15 @@ function SandboxTerminal({
4172
4110
  // owner from the first websocket frame and removes an otherwise unbounded
4173
4111
  // pre-mount output queue during large command bursts.
4174
4112
  capability: ptyAttachable && ready ? {
4175
- transport: "pty-ws",
4113
+ transport: terminalCapability.transport,
4176
4114
  url: ptyDescriptor ? terminalCapability.url : null,
4177
4115
  token: ptyDescriptor ? terminalCapability.token : null,
4178
4116
  expiresAt: ptyDescriptor ? terminalCapability.expiresAt : null
4179
4117
  } : null,
4180
4118
  onOutput: (data) => {
4181
4119
  termRef.current?.write(data);
4182
- }
4120
+ },
4121
+ onReconnectNeeded
4183
4122
  });
4184
4123
  const ptyMode = ptyDescriptor;
4185
4124
  const ptyCapturesInput = ptyAttachable && ptyStatus !== "error";
@@ -4202,10 +4141,16 @@ function SandboxTerminal({
4202
4141
  booting
4203
4142
  });
4204
4143
  function handleActivate() {
4144
+ const term = termRef.current;
4145
+ if (term && !readOnly && (ptyAttachable || result.write !== null)) {
4146
+ term.options.disableStdin = false;
4147
+ term.options.cursorBlink = true;
4148
+ }
4149
+ term?.focus();
4205
4150
  if (!activated) setActivated(true);
4206
4151
  onActivate?.();
4207
4152
  }
4208
- useEffect13(() => {
4153
+ useEffect11(() => {
4209
4154
  if (typeof window === "undefined") return;
4210
4155
  const el = containerRef.current;
4211
4156
  if (!el) return;
@@ -4313,19 +4258,19 @@ function SandboxTerminal({
4313
4258
  setReady(false);
4314
4259
  };
4315
4260
  }, []);
4316
- useEffect13(() => {
4261
+ useLayoutEffect(() => {
4317
4262
  const term = termRef.current;
4318
4263
  if (!ready || !term) return;
4319
4264
  term.options.disableStdin = !interactive;
4320
4265
  term.options.cursorBlink = interactive;
4321
4266
  }, [ready, interactive]);
4322
- useEffect13(() => {
4267
+ useEffect11(() => {
4323
4268
  const term = termRef.current;
4324
4269
  if (!ready || !term) return;
4325
4270
  const next = theme ?? xtermThemeFromTokens(containerRef.current);
4326
4271
  if (next) term.options.theme = next;
4327
4272
  }, [ready, theme]);
4328
- useEffect13(() => {
4273
+ useEffect11(() => {
4329
4274
  const term = termRef.current;
4330
4275
  if (!ready || !term) return;
4331
4276
  if (!booting) {
@@ -4346,7 +4291,7 @@ function SandboxTerminal({
4346
4291
  const id = setInterval(paint, 1e3);
4347
4292
  return () => clearInterval(id);
4348
4293
  }, [ready, booting]);
4349
- useEffect13(() => {
4294
+ useEffect11(() => {
4350
4295
  const term = termRef.current;
4351
4296
  if (!ready || !term || ptyMode) return;
4352
4297
  for (const chunk of result.chunks) {
@@ -4356,7 +4301,7 @@ function SandboxTerminal({
4356
4301
  term.write(chunk.text);
4357
4302
  }
4358
4303
  }, [ready, result.chunks, ptyMode]);
4359
- useEffect13(() => {
4304
+ useLayoutEffect(() => {
4360
4305
  const term = termRef.current;
4361
4306
  if (!ready || !term || !interactive) {
4362
4307
  setInputReady(false);
@@ -4371,14 +4316,14 @@ function SandboxTerminal({
4371
4316
  setInputReady(sub !== null);
4372
4317
  return () => sub?.dispose();
4373
4318
  }, [ready, interactive, ptyAttachable, ptyWrite, result.write]);
4374
- useEffect13(() => {
4319
+ useEffect11(() => {
4375
4320
  const term = termRef.current;
4376
4321
  if (!ready || !term || !ptyMode) return;
4377
4322
  resizePty(term.cols, term.rows);
4378
4323
  const sub = term.onResize(({ cols, rows }) => resizePty(cols, rows));
4379
4324
  return () => sub.dispose();
4380
4325
  }, [ready, ptyMode, ptyConnected, resizePty]);
4381
- useEffect13(() => {
4326
+ useEffect11(() => {
4382
4327
  if (typeof window === "undefined") return;
4383
4328
  const refit = () => {
4384
4329
  try {
@@ -4523,24 +4468,24 @@ import {
4523
4468
  Trash2Icon
4524
4469
  } from "lucide-react";
4525
4470
  import {
4526
- useCallback as useCallback16,
4527
- useEffect as useEffect15,
4471
+ useCallback as useCallback18,
4472
+ useEffect as useEffect13,
4528
4473
  useId as useId2,
4529
- useLayoutEffect,
4474
+ useLayoutEffect as useLayoutEffect2,
4530
4475
  useMemo as useMemo9,
4531
- useRef as useRef13,
4532
- useState as useState12
4476
+ useRef as useRef11,
4477
+ useState as useState10
4533
4478
  } from "react";
4534
4479
  import { AlertDialog } from "radix-ui";
4535
4480
  import { VList } from "virtua";
4536
4481
 
4537
4482
  // src/lib/use-unicode-fonts.ts
4538
- import { useEffect as useEffect14, useMemo as useMemo8 } from "react";
4483
+ import { useEffect as useEffect12, useMemo as useMemo8 } from "react";
4539
4484
  var japaneseScript = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u;
4540
4485
  var arabicScript = /[\u0600-\u06ff\u0750-\u077f\u0870-\u08ff\ufb50-\ufdff\ufe70-\ufefc]/u;
4541
4486
  function useUnicodeFallbackFonts(values) {
4542
4487
  const content = useMemo8(() => collectUnicodeContent(values), [values]);
4543
- useEffect14(() => {
4488
+ useEffect12(() => {
4544
4489
  if (content.japanese) {
4545
4490
  void import("./noto-sans-jp-loader.generated-HFGLYBR3.js").then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese)).catch(() => void 0);
4546
4491
  }
@@ -4570,7 +4515,7 @@ var STATUS_TINT = {
4570
4515
  renamed: "text-og-accent",
4571
4516
  untracked: "text-og-fg-subtle"
4572
4517
  };
4573
- var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect15 : useLayoutEffect;
4518
+ var useBrowserLayoutEffect = typeof window === "undefined" ? useEffect13 : useLayoutEffect2;
4574
4519
  function parentOf2(path) {
4575
4520
  const i = path.lastIndexOf("/");
4576
4521
  return i <= 0 ? "" : path.slice(0, i);
@@ -4611,23 +4556,23 @@ function FileBrowser({
4611
4556
  confirmDelete,
4612
4557
  className
4613
4558
  }) {
4614
- const [expanded, setExpanded] = useState12(/* @__PURE__ */ new Set());
4615
- const [loadingPaths, setLoadingPaths] = useState12(/* @__PURE__ */ new Set());
4616
- const [active, setActive] = useState12(null);
4617
- const [draftCreate, setDraftCreate] = useState12(null);
4618
- const [draftRename, setDraftRename] = useState12(null);
4619
- const [dragOver, setDragOver] = useState12(null);
4620
- const [menu, setMenu] = useState12(null);
4621
- const [pendingDelete, setPendingDelete] = useState12(null);
4622
- const [busy, setBusy] = useState12(false);
4623
- const [coarsePointer, setCoarsePointer] = useState12(false);
4559
+ const [expanded, setExpanded] = useState10(/* @__PURE__ */ new Set());
4560
+ const [loadingPaths, setLoadingPaths] = useState10(/* @__PURE__ */ new Set());
4561
+ const [active, setActive] = useState10(null);
4562
+ const [draftCreate, setDraftCreate] = useState10(null);
4563
+ const [draftRename, setDraftRename] = useState10(null);
4564
+ const [dragOver, setDragOver] = useState10(null);
4565
+ const [menu, setMenu] = useState10(null);
4566
+ const [pendingDelete, setPendingDelete] = useState10(null);
4567
+ const [busy, setBusy] = useState10(false);
4568
+ const [coarsePointer, setCoarsePointer] = useState10(false);
4624
4569
  const treeId = useId2();
4625
4570
  const container = usePortalTokenSource();
4626
4571
  const containerRef = container.currentRef;
4627
- const deleteReturnFocusRef = useRef13(null);
4628
- const vlistRef = useRef13(null);
4572
+ const deleteReturnFocusRef = useRef11(null);
4573
+ const vlistRef = useRef11(null);
4629
4574
  const portalTokenStyle = usePortalTokenStyle(container.source);
4630
- const getTreeElement = useCallback16(
4575
+ const getTreeElement = useCallback18(
4631
4576
  () => typeof document === "undefined" ? null : document.getElementById(treeId),
4632
4577
  [treeId]
4633
4578
  );
@@ -4646,7 +4591,7 @@ function FileBrowser({
4646
4591
  }
4647
4592
  }, []);
4648
4593
  const cursor = active ?? selectedPath ?? null;
4649
- const expand = useCallback16(
4594
+ const expand = useCallback18(
4650
4595
  async (path) => {
4651
4596
  const node = findNode(result.tree, path);
4652
4597
  if (node && node.kind === "dir" && node.children === void 0) {
@@ -4664,14 +4609,14 @@ function FileBrowser({
4664
4609
  },
4665
4610
  [result]
4666
4611
  );
4667
- const open = useCallback16(
4612
+ const open = useCallback18(
4668
4613
  (path) => {
4669
4614
  setExpanded((prev) => new Set(prev).add(path));
4670
4615
  void expand(path);
4671
4616
  },
4672
4617
  [expand]
4673
4618
  );
4674
- const toggle = useCallback16(
4619
+ const toggle = useCallback18(
4675
4620
  async (node) => {
4676
4621
  if (node.kind !== "dir") return;
4677
4622
  const isOpen = expanded.has(node.path);
@@ -4746,7 +4691,7 @@ function FileBrowser({
4746
4691
  const cursorIndex = cursor ? rowIndexByPath.get(cursor) ?? -1 : -1;
4747
4692
  const activeDescendantId = cursorIndex >= 0 ? `${treeId}-item-${cursorIndex}` : void 0;
4748
4693
  const supportsMutation = editable && result.source === "live" && result.error === null && !result.loading;
4749
- const performDelete = useCallback16(
4694
+ const performDelete = useCallback18(
4750
4695
  async (node) => {
4751
4696
  if (!supportsMutation) return;
4752
4697
  const recursive = node.kind === "dir";
@@ -4760,7 +4705,7 @@ function FileBrowser({
4760
4705
  },
4761
4706
  [supportsMutation, result]
4762
4707
  );
4763
- const runDelete = useCallback16(
4708
+ const runDelete = useCallback18(
4764
4709
  async (node, returnFocus) => {
4765
4710
  if (!supportsMutation) return;
4766
4711
  setMenu(null);
@@ -4773,7 +4718,7 @@ function FileBrowser({
4773
4718
  },
4774
4719
  [supportsMutation, confirmDelete, performDelete]
4775
4720
  );
4776
- const closePendingDelete = useCallback16(
4721
+ const closePendingDelete = useCallback18(
4777
4722
  (restoreTrigger) => {
4778
4723
  const returnTarget = restoreTrigger ? deleteReturnFocusRef.current : getTreeElement();
4779
4724
  deleteReturnFocusRef.current = null;
@@ -4787,7 +4732,7 @@ function FileBrowser({
4787
4732
  },
4788
4733
  [getTreeElement]
4789
4734
  );
4790
- const commitCreate = useCallback16(
4735
+ const commitCreate = useCallback18(
4791
4736
  async (name) => {
4792
4737
  const draft = draftCreate;
4793
4738
  setDraftCreate(null);
@@ -4812,7 +4757,7 @@ function FileBrowser({
4812
4757
  },
4813
4758
  [draftCreate, supportsMutation, result, open, onSelectFile]
4814
4759
  );
4815
- const commitRename = useCallback16(
4760
+ const commitRename = useCallback18(
4816
4761
  async (name) => {
4817
4762
  const draft = draftRename;
4818
4763
  setDraftRename(null);
@@ -4835,7 +4780,7 @@ function FileBrowser({
4835
4780
  },
4836
4781
  [draftRename, supportsMutation, result, selectedPath, active, onSelectFile]
4837
4782
  );
4838
- const startCreate = useCallback16(
4783
+ const startCreate = useCallback18(
4839
4784
  (kind) => {
4840
4785
  if (!supportsMutation) return;
4841
4786
  const anchor = cursor ? findNode(result.tree, cursor) : void 0;
@@ -4847,7 +4792,7 @@ function FileBrowser({
4847
4792
  },
4848
4793
  [supportsMutation, cursor, result.tree, open]
4849
4794
  );
4850
- const startRename = useCallback16(
4795
+ const startRename = useCallback18(
4851
4796
  (node) => {
4852
4797
  if (!supportsMutation) return;
4853
4798
  setDraftCreate(null);
@@ -4856,7 +4801,7 @@ function FileBrowser({
4856
4801
  },
4857
4802
  [supportsMutation]
4858
4803
  );
4859
- const onDropOnto = useCallback16(
4804
+ const onDropOnto = useCallback18(
4860
4805
  async (targetDir, sourcePath) => {
4861
4806
  setDragOver(null);
4862
4807
  if (!supportsMutation || !sourcePath) return;
@@ -4880,7 +4825,7 @@ function FileBrowser({
4880
4825
  },
4881
4826
  [supportsMutation, result, selectedPath, active, onSelectFile]
4882
4827
  );
4883
- const onKeyDown = useCallback16(
4828
+ const onKeyDown = useCallback18(
4884
4829
  (e) => {
4885
4830
  if (draftCreate || draftRename) return;
4886
4831
  if (flatRows.length === 0) return;
@@ -4999,7 +4944,7 @@ function FileBrowser({
4999
4944
  treeId
5000
4945
  ]
5001
4946
  );
5002
- useEffect15(() => {
4947
+ useEffect13(() => {
5003
4948
  if (!menu) return;
5004
4949
  const close = () => setMenu(null);
5005
4950
  window.addEventListener("click", close);
@@ -5180,7 +5125,7 @@ function FileBrowser({
5180
5125
  );
5181
5126
  }
5182
5127
  };
5183
- useEffect15(() => {
5128
+ useEffect13(() => {
5184
5129
  if (!cursor) return;
5185
5130
  const index = renderItems.findIndex((it) => it.type === "node" && it.node.path === cursor);
5186
5131
  if (index >= 0) vlistRef.current?.scrollToIndex(index);
@@ -5387,11 +5332,11 @@ function DeleteDialog({
5387
5332
  onCancel,
5388
5333
  onConfirm
5389
5334
  }) {
5390
- const cancelRef = useRef13(null);
5391
- const [open, setOpen] = useState12(true);
5392
- const closeIntentRef = useRef13("cancel");
5393
- const settledRef = useRef13(false);
5394
- const settleTimerRef = useRef13(null);
5335
+ const cancelRef = useRef11(null);
5336
+ const [open, setOpen] = useState10(true);
5337
+ const closeIntentRef = useRef11("cancel");
5338
+ const settledRef = useRef11(false);
5339
+ const settleTimerRef = useRef11(null);
5395
5340
  const settle = () => {
5396
5341
  if (settledRef.current) return;
5397
5342
  settledRef.current = true;
@@ -5408,7 +5353,7 @@ function DeleteDialog({
5408
5353
  settle();
5409
5354
  }, 0);
5410
5355
  };
5411
- useEffect15(
5356
+ useEffect13(
5412
5357
  () => () => {
5413
5358
  if (settleTimerRef.current !== null) clearTimeout(settleTimerRef.current);
5414
5359
  },
@@ -5526,10 +5471,10 @@ function InlineInput({
5526
5471
  onCommit,
5527
5472
  onCancel
5528
5473
  }) {
5529
- const [value, setValue] = useState12(initialValue);
5530
- const ref = useRef13(null);
5531
- const doneRef = useRef13(false);
5532
- useEffect15(() => {
5474
+ const [value, setValue] = useState10(initialValue);
5475
+ const ref = useRef11(null);
5476
+ const doneRef = useRef11(false);
5477
+ useEffect13(() => {
5533
5478
  const el = ref.current;
5534
5479
  if (!el) return;
5535
5480
  el.focus();
@@ -5593,12 +5538,12 @@ function ContextMenu({
5593
5538
  onClose
5594
5539
  }) {
5595
5540
  const isDir = node.kind === "dir";
5596
- const menuRef = useRef13(null);
5541
+ const menuRef = useRef11(null);
5597
5542
  const vw = typeof window !== "undefined" ? window.innerWidth : 9999;
5598
5543
  const vh = typeof window !== "undefined" ? window.innerHeight : 9999;
5599
5544
  const left = Math.max(4, Math.min(x, vw - 180));
5600
5545
  const top = Math.max(4, Math.min(y, vh - 160));
5601
- useEffect15(() => {
5546
+ useEffect13(() => {
5602
5547
  menuRef.current?.querySelector('[role="menuitem"]')?.focus();
5603
5548
  }, []);
5604
5549
  const onKeyDown = (event) => {
@@ -5674,18 +5619,18 @@ function ContextMenu({
5674
5619
  import { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from "lucide-react";
5675
5620
  import {
5676
5621
  Component,
5677
- useCallback as useCallback18,
5678
- useEffect as useEffect17,
5622
+ useCallback as useCallback20,
5623
+ useEffect as useEffect15,
5679
5624
  useId as useId3,
5680
- useLayoutEffect as useLayoutEffect2,
5625
+ useLayoutEffect as useLayoutEffect3,
5681
5626
  useMemo as useMemo11,
5682
- useRef as useRef15,
5683
- useState as useState14
5627
+ useRef as useRef13,
5628
+ useState as useState12
5684
5629
  } from "react";
5685
5630
  import { VList as VList2 } from "virtua";
5686
5631
 
5687
5632
  // src/hooks/use-windowed-sections.ts
5688
- import { useCallback as useCallback17, useEffect as useEffect16, useMemo as useMemo10, useRef as useRef14, useState as useState13 } from "react";
5633
+ import { useCallback as useCallback19, useEffect as useEffect14, useMemo as useMemo10, useRef as useRef12, useState as useState11 } from "react";
5689
5634
  function computeWindowRange(heights, scrollTop, viewport, overscan) {
5690
5635
  const n = heights.length;
5691
5636
  if (n === 0) return { start: 0, end: 0 };
@@ -5716,29 +5661,29 @@ function computeWindowRange(heights, scrollTop, viewport, overscan) {
5716
5661
  }
5717
5662
  function useWindowedSections(opts) {
5718
5663
  const { count, estimateHeight, overscan = 3 } = opts;
5719
- const scrollRef = useRef14(null);
5720
- const [heights, setHeights] = useState13(
5664
+ const scrollRef = useRef12(null);
5665
+ const [heights, setHeights] = useState11(
5721
5666
  () => Array.from({ length: count }, (_, i) => estimateHeight(i))
5722
5667
  );
5723
- useEffect16(() => {
5668
+ useEffect14(() => {
5724
5669
  setHeights((prev) => {
5725
5670
  const next = Array.from({ length: count }, (_, i) => estimateHeight(i));
5726
5671
  return prev.length === next.length && prev.every((height, index) => height === next[index]) ? prev : next;
5727
5672
  });
5728
5673
  }, [count, estimateHeight]);
5729
- const heightsRef = useRef14(heights);
5674
+ const heightsRef = useRef12(heights);
5730
5675
  heightsRef.current = heights;
5731
- const [range, setRange] = useState13(() => ({
5676
+ const [range, setRange] = useState11(() => ({
5732
5677
  start: 0,
5733
5678
  end: Math.min(count, overscan + 1)
5734
5679
  }));
5735
- const recompute = useCallback17(() => {
5680
+ const recompute = useCallback19(() => {
5736
5681
  const el = scrollRef.current;
5737
5682
  if (!el) return;
5738
5683
  const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);
5739
5684
  setRange((prev) => prev.start === next.start && prev.end === next.end ? prev : next);
5740
5685
  }, [overscan]);
5741
- useEffect16(() => {
5686
+ useEffect14(() => {
5742
5687
  const el = scrollRef.current;
5743
5688
  if (!el) return;
5744
5689
  let raf = 0;
@@ -5759,7 +5704,7 @@ function useWindowedSections(opts) {
5759
5704
  if (raf && typeof cancelAnimationFrame === "function") cancelAnimationFrame(raf);
5760
5705
  };
5761
5706
  }, [recompute, count]);
5762
- const measure = useCallback17((index, height) => {
5707
+ const measure = useCallback19((index, height) => {
5763
5708
  if (height <= 0) return;
5764
5709
  setHeights((prev) => {
5765
5710
  if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;
@@ -5774,10 +5719,10 @@ function useWindowedSections(opts) {
5774
5719
  for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
5775
5720
  return out;
5776
5721
  }, [heights, count]);
5777
- useEffect16(() => {
5722
+ useEffect14(() => {
5778
5723
  recompute();
5779
5724
  }, [offsets, recompute]);
5780
- const scrollToIndex = useCallback17(
5725
+ const scrollToIndex = useCallback19(
5781
5726
  (index) => {
5782
5727
  const el = scrollRef.current;
5783
5728
  if (!el) return;
@@ -5804,7 +5749,7 @@ var HEADER_PX = 30;
5804
5749
  var LINE_PX = 18;
5805
5750
  var GUARD_BODY_PX = 52;
5806
5751
  var COMPACT_SURFACE_PX = 560;
5807
- var useChangesLayoutEffect = typeof window === "undefined" ? useEffect17 : useLayoutEffect2;
5752
+ var useChangesLayoutEffect = typeof window === "undefined" ? useEffect15 : useLayoutEffect3;
5808
5753
  var STATUS_TINT2 = {
5809
5754
  added: "text-og-status-idle",
5810
5755
  modified: "text-og-status-running",
@@ -5979,12 +5924,12 @@ function WorkbenchChanges({
5979
5924
  onOpenFile,
5980
5925
  className
5981
5926
  }) {
5982
- const rootRef = useRef15(null);
5983
- const [compact, setCompact] = useState14(false);
5984
- const [coarsePointer, setCoarsePointer] = useState14(false);
5927
+ const rootRef = useRef13(null);
5928
+ const [compact, setCompact] = useState12(false);
5929
+ const [coarsePointer, setCoarsePointer] = useState12(false);
5985
5930
  const resolvedTheme = useThemeType(themeType);
5986
- const [layout, setLayout] = useState14("unified");
5987
- const [activePath, setActivePath] = useState14(null);
5931
+ const [layout, setLayout] = useState12("unified");
5932
+ const [activePath, setActivePath] = useState12(null);
5988
5933
  const pickerId = useId3();
5989
5934
  const unicodeFontPaths = useMemo11(
5990
5935
  () => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
@@ -6023,7 +5968,7 @@ function WorkbenchChanges({
6023
5968
  }, []);
6024
5969
  const additions = useMemo11(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
6025
5970
  const deletions = useMemo11(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
6026
- const estimateHeight = useCallback18(
5971
+ const estimateHeight = useCallback20(
6027
5972
  (index) => {
6028
5973
  const file = orderedFiles[index];
6029
5974
  return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;
@@ -6035,14 +5980,14 @@ function WorkbenchChanges({
6035
5980
  estimateHeight,
6036
5981
  overscan: OVERSCAN
6037
5982
  });
6038
- const jumpTo = useCallback18(
5983
+ const jumpTo = useCallback20(
6039
5984
  (index) => {
6040
5985
  setActivePath(orderedFiles[index]?.path ?? null);
6041
5986
  windowed.scrollToIndex(index);
6042
5987
  },
6043
5988
  [orderedFiles, windowed]
6044
5989
  );
6045
- const onPaneScroll = useCallback18(() => {
5990
+ const onPaneScroll = useCallback20(() => {
6046
5991
  const el = windowed.scrollRef.current;
6047
5992
  if (!el) {
6048
5993
  return;
@@ -6057,7 +6002,7 @@ function WorkbenchChanges({
6057
6002
  const nextPath = orderedFiles[idx]?.path ?? null;
6058
6003
  setActivePath((previous) => previous === nextPath ? previous : nextPath);
6059
6004
  }, [windowed, orderedFiles]);
6060
- useEffect17(() => {
6005
+ useEffect15(() => {
6061
6006
  const el = windowed.scrollRef.current;
6062
6007
  if (!el) return;
6063
6008
  el.addEventListener("scroll", onPaneScroll, { passive: true });
@@ -6305,8 +6250,8 @@ function MeasuredSection({
6305
6250
  onMeasure,
6306
6251
  children
6307
6252
  }) {
6308
- const ref = useRef15(null);
6309
- useEffect17(() => {
6253
+ const ref = useRef13(null);
6254
+ useEffect15(() => {
6310
6255
  const el = ref.current;
6311
6256
  if (!el) return;
6312
6257
  const report = () => onMeasure(index, el.offsetHeight);
@@ -6427,8 +6372,8 @@ function DiffView({
6427
6372
  import {
6428
6373
  lazy,
6429
6374
  Suspense,
6430
- useEffect as useEffect18,
6431
- useState as useState15
6375
+ useEffect as useEffect16,
6376
+ useState as useState13
6432
6377
  } from "react";
6433
6378
  import { jsx as jsx8 } from "react/jsx-runtime";
6434
6379
  var LazyFile = lazy(async () => {
@@ -6445,8 +6390,8 @@ function PierreFile({
6445
6390
  fallback,
6446
6391
  className
6447
6392
  }) {
6448
- const [failed, setFailed] = useState15(false);
6449
- useEffect18(() => {
6393
+ const [failed, setFailed] = useState13(false);
6394
+ useEffect16(() => {
6450
6395
  let cancelled = false;
6451
6396
  void import("@pierre/diffs/react").catch(() => {
6452
6397
  if (!cancelled) setFailed(true);
@@ -6505,11 +6450,11 @@ function FileSkeleton() {
6505
6450
  // src/components/code-editor.tsx
6506
6451
  import { Loader2Icon, SaveIcon } from "lucide-react";
6507
6452
  import {
6508
- useCallback as useCallback19,
6509
- useEffect as useEffect19,
6453
+ useCallback as useCallback21,
6454
+ useEffect as useEffect17,
6510
6455
  useMemo as useMemo12,
6511
- useRef as useRef16,
6512
- useState as useState16
6456
+ useRef as useRef14,
6457
+ useState as useState14
6513
6458
  } from "react";
6514
6459
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
6515
6460
  var LANGUAGE_LOADERS = {
@@ -6581,30 +6526,30 @@ function CodeEditor({
6581
6526
  fallback,
6582
6527
  className
6583
6528
  }) {
6584
- const [failed, setFailed] = useState16(false);
6585
- const [bundle, setBundle] = useState16(null);
6586
- const [buffer, setBuffer] = useState16(() => ({
6529
+ const [failed, setFailed] = useState14(false);
6530
+ const [bundle, setBundle] = useState14(null);
6531
+ const [buffer, setBuffer] = useState14(() => ({
6587
6532
  path,
6588
6533
  value: initialContents,
6589
6534
  baseline: initialContents
6590
6535
  }));
6591
6536
  const { value, baseline } = buffer;
6592
- const [saving, setSaving] = useState16(false);
6593
- const [saveError, setSaveError] = useState16(null);
6594
- const [savedTick, setSavedTick] = useState16(false);
6595
- const saveInFlightRef = useRef16(false);
6537
+ const [saving, setSaving] = useState14(false);
6538
+ const [saveError, setSaveError] = useState14(null);
6539
+ const [savedTick, setSavedTick] = useState14(false);
6540
+ const saveInFlightRef = useRef14(false);
6596
6541
  const dirty = value !== baseline;
6597
6542
  const saveConflict = saveError !== null && saveError.code === "file_write_conflict";
6598
- const editIntentFiredRef = useRef16(false);
6599
- useEffect19(() => {
6543
+ const editIntentFiredRef = useRef14(false);
6544
+ useEffect17(() => {
6600
6545
  setBuffer((current) => reconcileEditorBuffer(current, { path, contents: initialContents }));
6601
6546
  }, [path, initialContents]);
6602
- useEffect19(() => {
6547
+ useEffect17(() => {
6603
6548
  editIntentFiredRef.current = false;
6604
6549
  setSaveError(null);
6605
6550
  setSavedTick(false);
6606
6551
  }, [path]);
6607
- const noteEdit = useCallback19(
6552
+ const noteEdit = useCallback21(
6608
6553
  (next) => {
6609
6554
  setBuffer((current) => ({ ...current, value: next }));
6610
6555
  setSavedTick(false);
@@ -6616,7 +6561,7 @@ function CodeEditor({
6616
6561
  [baseline, onEditIntent]
6617
6562
  );
6618
6563
  const langKey = useMemo12(() => languageForPath(path), [path]);
6619
- useEffect19(() => {
6564
+ useEffect17(() => {
6620
6565
  let cancelled = false;
6621
6566
  void (async () => {
6622
6567
  try {
@@ -6656,9 +6601,9 @@ function CodeEditor({
6656
6601
  cancelled = true;
6657
6602
  };
6658
6603
  }, [langKey]);
6659
- const saveRef = useRef16(() => {
6604
+ const saveRef = useRef14(() => {
6660
6605
  });
6661
- const save = useCallback19(async () => {
6606
+ const save = useCallback21(async () => {
6662
6607
  if (readOnly || saveInFlightRef.current) return;
6663
6608
  const snapshot = value;
6664
6609
  if (snapshot === baseline) return;
@@ -6679,7 +6624,7 @@ function CodeEditor({
6679
6624
  setSaving(false);
6680
6625
  }
6681
6626
  }, [readOnly, value, baseline, onSave, path]);
6682
- const overwrite = useCallback19(async () => {
6627
+ const overwrite = useCallback21(async () => {
6683
6628
  if (readOnly || !onOverwrite || saveInFlightRef.current) return;
6684
6629
  const snapshot = value;
6685
6630
  if (snapshot === baseline) return;
@@ -6703,7 +6648,7 @@ function CodeEditor({
6703
6648
  saveRef.current = () => {
6704
6649
  void save();
6705
6650
  };
6706
- useEffect19(() => {
6651
+ useEffect17(() => {
6707
6652
  if (!savedTick) return;
6708
6653
  const t = setTimeout(() => setSavedTick(false), 1800);
6709
6654
  return () => clearTimeout(t);
@@ -6716,7 +6661,7 @@ function CodeEditor({
6716
6661
  return exts;
6717
6662
  }, [bundle]);
6718
6663
  const Editor = bundle?.Editor;
6719
- const reload = useCallback19(() => {
6664
+ const reload = useCallback21(() => {
6720
6665
  setBuffer({ path, value: initialContents, baseline: initialContents });
6721
6666
  editIntentFiredRef.current = false;
6722
6667
  setSaveError(null);
@@ -6872,7 +6817,7 @@ function EditorSkeleton() {
6872
6817
 
6873
6818
  // src/components/sandbox-files.tsx
6874
6819
  import { FileCode2Icon, FileWarningIcon as FileWarningIcon2, LoaderCircleIcon } from "lucide-react";
6875
- import { useCallback as useCallback20, useEffect as useEffect20, useRef as useRef17, useState as useState17 } from "react";
6820
+ import { useCallback as useCallback22, useEffect as useEffect18, useRef as useRef15, useState as useState15 } from "react";
6876
6821
  import { Group, Panel, Separator } from "react-resizable-panels";
6877
6822
  import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
6878
6823
  function SandboxFiles({
@@ -6892,14 +6837,14 @@ function SandboxFiles({
6892
6837
  themeType,
6893
6838
  className
6894
6839
  }) {
6895
- const [selected, setSelected] = useState17(null);
6896
- const [editMode, setEditMode] = useState17(false);
6897
- const [liveRequestedPath, setLiveRequestedPath] = useState17(null);
6898
- const [viewReloadRevision, setViewReloadRevision] = useState17(0);
6899
- const pendingRequestRef = useRef17(null);
6900
- const handledRequestRef = useRef17(null);
6840
+ const [selected, setSelected] = useState15(null);
6841
+ const [editMode, setEditMode] = useState15(false);
6842
+ const [liveRequestedPath, setLiveRequestedPath] = useState15(null);
6843
+ const [viewReloadRevision, setViewReloadRevision] = useState15(0);
6844
+ const pendingRequestRef = useRef15(null);
6845
+ const handledRequestRef = useRef15(null);
6901
6846
  const requestKey = requestedPath ? requestedPathRequestId ?? requestedPath : null;
6902
- useEffect20(() => {
6847
+ useEffect18(() => {
6903
6848
  if (!requestedPath || requestKey === null) {
6904
6849
  pendingRequestRef.current = null;
6905
6850
  handledRequestRef.current = null;
@@ -6917,9 +6862,9 @@ function SandboxFiles({
6917
6862
  setSelected(requestedPath);
6918
6863
  setEditMode(false);
6919
6864
  }, [requestKey, requestedPath, requestedPathReady]);
6920
- const rootRef = useRef17(null);
6921
- const [wide, setWide] = useState17(false);
6922
- useEffect20(() => {
6865
+ const rootRef = useRef15(null);
6866
+ const [wide, setWide] = useState15(false);
6867
+ useEffect18(() => {
6923
6868
  const el = rootRef.current;
6924
6869
  if (!el) return;
6925
6870
  const update = () => setWide(el.getBoundingClientRect().width >= 720);
@@ -6945,7 +6890,7 @@ function SandboxFiles({
6945
6890
  files.readFile,
6946
6891
  (files.contentRevision ?? 0) + viewReloadRevision
6947
6892
  );
6948
- const selectFile = useCallback20((path) => {
6893
+ const selectFile = useCallback22((path) => {
6949
6894
  if (pendingRequestRef.current !== null) {
6950
6895
  handledRequestRef.current = pendingRequestRef.current;
6951
6896
  pendingRequestRef.current = null;
@@ -7271,8 +7216,8 @@ function Segmented({
7271
7216
  )) });
7272
7217
  }
7273
7218
  function useFileView(path, readFile, reloadRevision = 0) {
7274
- const previousPathRef = useRef17(null);
7275
- const [state, setState] = useState17({
7219
+ const previousPathRef = useRef15(null);
7220
+ const [state, setState] = useState15({
7276
7221
  content: null,
7277
7222
  isBinary: false,
7278
7223
  truncated: false,
@@ -7280,7 +7225,7 @@ function useFileView(path, readFile, reloadRevision = 0) {
7280
7225
  loading: false,
7281
7226
  error: null
7282
7227
  });
7283
- useEffect20(() => {
7228
+ useEffect18(() => {
7284
7229
  if (!path) {
7285
7230
  previousPathRef.current = null;
7286
7231
  setState({
@@ -7379,411 +7324,14 @@ function Notice({
7379
7324
  );
7380
7325
  }
7381
7326
 
7382
- // src/components/desktop-viewer.tsx
7383
- import { LoaderCircleIcon as LoaderCircleIcon2, MonitorIcon, MousePointerClickIcon, WifiOffIcon } from "lucide-react";
7384
- import { useEffect as useEffect21, useRef as useRef18, useState as useState18 } from "react";
7385
- import { jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
7386
- var DESKTOP_MEDIA_BACKGROUND = "#000";
7387
- function isHardUnavailable(reason) {
7388
- switch (reason) {
7389
- case "backend_unsupported":
7390
- case "os_unsupported":
7391
- case "not_provisioned":
7392
- case "disabled_by_policy":
7393
- case "tier_headless":
7394
- return true;
7395
- default:
7396
- return false;
7397
- }
7398
- }
7399
- function DesktopViewer({
7400
- capability,
7401
- interactive,
7402
- showControlToggle = true,
7403
- scaleViewport,
7404
- rfbFactory,
7405
- webSocketFactory,
7406
- onActivate,
7407
- onAcknowledge,
7408
- watching,
7409
- onWarm,
7410
- renderUnavailable,
7411
- renderWarming,
7412
- renderViewerCap,
7413
- viewerCapReached,
7414
- connectTimeoutMs = 13e3,
7415
- className
7416
- }) {
7417
- const containerRef = useRef18(null);
7418
- const onActivateRef = useRef18(onActivate);
7419
- const onAcknowledgeRef = useRef18(onAcknowledge);
7420
- onActivateRef.current = onActivate;
7421
- onAcknowledgeRef.current = onAcknowledge;
7422
- const acknowledgmentAttemptedRef = useRef18(false);
7423
- const [acknowledgmentError, setAcknowledgmentError] = useState18(null);
7424
- const [acknowledgmentRetryNonce, setAcknowledgmentRetryNonce] = useState18(0);
7425
- const [takeControl, setTakeControl] = useState18(interactive ?? false);
7426
- const externallyControlled = interactive !== void 0;
7427
- const serverAllowsControl = capability?.mode !== "read-only";
7428
- const wantControl = externallyControlled ? interactive : takeControl;
7429
- const inControl = Boolean(wantControl) && serverAllowsControl;
7430
- const isWatching = watching ?? true;
7431
- const transportNull = !capability || capability.transport === null;
7432
- const reason = capability?.reason ?? null;
7433
- const needsAcknowledgment = capability?.requiresAcknowledgment === true && capability.acknowledged !== true;
7434
- const noLiveAddress = Boolean(capability) && !transportNull && !capability.url;
7435
- const coldWarmable = transportNull && reason === "lease_cold" || !transportNull && noLiveAddress;
7436
- const hardUnavailable = transportNull && isHardUnavailable(reason);
7437
- useEffect21(() => {
7438
- onActivateRef.current?.();
7439
- }, []);
7440
- useEffect21(() => {
7441
- if (!needsAcknowledgment) {
7442
- acknowledgmentAttemptedRef.current = false;
7443
- setAcknowledgmentError(null);
7444
- return;
7445
- }
7446
- if (acknowledgmentAttemptedRef.current) return;
7447
- acknowledgmentAttemptedRef.current = true;
7448
- setAcknowledgmentError(null);
7449
- const acknowledge = onAcknowledgeRef.current;
7450
- if (!acknowledge) {
7451
- setAcknowledgmentError(new Error("Desktop acknowledgment is not configured."));
7452
- return;
7453
- }
7454
- let active = true;
7455
- void Promise.resolve().then(() => acknowledge()).catch((cause) => {
7456
- if (!active) return;
7457
- setAcknowledgmentError(cause instanceof Error ? cause : new Error(String(cause)));
7458
- });
7459
- return () => {
7460
- active = false;
7461
- };
7462
- }, [needsAcknowledgment, acknowledgmentRetryNonce]);
7463
- const retryAcknowledgment = () => {
7464
- acknowledgmentAttemptedRef.current = false;
7465
- setAcknowledgmentError(null);
7466
- setAcknowledgmentRetryNonce((nonce) => nonce + 1);
7467
- };
7468
- useEffect21(() => {
7469
- if (!inControl || externallyControlled) return;
7470
- const onKey = (event) => {
7471
- if (event.ctrlKey && event.altKey && event.shiftKey && (event.key === "Control" || event.key === "Alt" || event.key === "Shift")) {
7472
- event.preventDefault();
7473
- setTakeControl(false);
7474
- }
7475
- };
7476
- window.addEventListener("keydown", onKey);
7477
- return () => {
7478
- window.removeEventListener("keydown", onKey);
7479
- };
7480
- }, [inControl, externallyControlled]);
7481
- const connectCapability = !transportNull && !needsAcknowledgment && !viewerCapReached ? capability : null;
7482
- const stream = useDesktopStream({
7483
- capability: connectCapability,
7484
- containerRef,
7485
- interactive: inControl,
7486
- ...scaleViewport !== void 0 ? { scaleViewport } : {},
7487
- ...rfbFactory ? { rfbFactory } : {},
7488
- ...webSocketFactory ? { webSocketFactory } : {}
7489
- });
7490
- const connected = stream.state === "connected";
7491
- const hasLiveUrl = Boolean(connectCapability?.url);
7492
- const streamStateRef = useRef18(stream.state);
7493
- streamStateRef.current = stream.state;
7494
- const warmKeyRef = useRef18(null);
7495
- useEffect21(() => {
7496
- if (!isWatching || !coldWarmable || needsAcknowledgment || viewerCapReached) {
7497
- if (!coldWarmable) warmKeyRef.current = null;
7498
- return;
7499
- }
7500
- if (!onWarm) return;
7501
- const key = `${capability?.reason ?? ""}:${capability?.url ?? ""}:${capability?.expiresAt ?? ""}`;
7502
- if (warmKeyRef.current === key) return;
7503
- warmKeyRef.current = key;
7504
- onWarm();
7505
- }, [isWatching, coldWarmable, needsAcknowledgment, viewerCapReached, capability, onWarm]);
7506
- useEffect21(() => {
7507
- if (!hasLiveUrl || typeof document === "undefined") return;
7508
- const maybeReattach = () => {
7509
- if (document.visibilityState !== "visible") return;
7510
- if (streamStateRef.current === "idle") stream.reconnect();
7511
- };
7512
- document.addEventListener("visibilitychange", maybeReattach);
7513
- return () => document.removeEventListener("visibilitychange", maybeReattach);
7514
- }, [hasLiveUrl, connectCapability?.url]);
7515
- const [connectTimedOut, setConnectTimedOut] = useState18(false);
7516
- const [reconnectNonce, setReconnectNonce] = useState18(0);
7517
- useEffect21(() => {
7518
- setConnectTimedOut(false);
7519
- if (!hasLiveUrl || connected || stream.error || connectTimeoutMs <= 0) return;
7520
- const timer = setTimeout(() => setConnectTimedOut(true), connectTimeoutMs);
7521
- return () => clearTimeout(timer);
7522
- }, [
7523
- hasLiveUrl,
7524
- connected,
7525
- connectTimeoutMs,
7526
- connectCapability?.url,
7527
- stream.error,
7528
- reconnectNonce
7529
- ]);
7530
- const reconnect = () => {
7531
- setConnectTimedOut(false);
7532
- setReconnectNonce((n) => n + 1);
7533
- stream.reconnect();
7534
- };
7535
- let uiState;
7536
- if (viewerCapReached) uiState = "viewer_cap";
7537
- else if (hardUnavailable) uiState = "unavailable";
7538
- else if (acknowledgmentError) uiState = "acknowledgment_error";
7539
- else if (needsAcknowledgment) uiState = "opening";
7540
- else if (coldWarmable) uiState = "warming";
7541
- else if (connected) uiState = "connected";
7542
- else if (stream.error) uiState = "error";
7543
- else if (connectTimedOut) uiState = "connect_failed";
7544
- else uiState = "connecting";
7545
- const overlayShown = uiState !== "connected" && uiState !== "connecting" && uiState !== "opening";
7546
- const showToggle = showControlToggle && !overlayShown;
7547
- let overlay = null;
7548
- switch (uiState) {
7549
- case "viewer_cap":
7550
- overlay = renderViewerCap?.() ?? defaultNotice(
7551
- "Too many viewers",
7552
- "This session has reached its live-viewer limit. Try again shortly."
7553
- );
7554
- break;
7555
- case "unavailable":
7556
- overlay = renderUnavailable?.(reason) ?? defaultNotice("Desktop unavailable", unavailableCopy(reason));
7557
- break;
7558
- case "acknowledgment_error":
7559
- overlay = defaultNotice(
7560
- "Couldn\u2019t open desktop",
7561
- acknowledgmentError?.message ?? "The desktop acknowledgment failed.",
7562
- retryAcknowledgment
7563
- );
7564
- break;
7565
- case "warming":
7566
- overlay = renderWarming?.() ?? /* @__PURE__ */ jsx11(WarmingNotice, {});
7567
- break;
7568
- case "connect_failed":
7569
- overlay = defaultNotice(
7570
- "Couldn\u2019t connect",
7571
- "The desktop is warm but the live stream didn\u2019t come up. This usually clears on a retry.",
7572
- reconnect
7573
- );
7574
- break;
7575
- case "error":
7576
- overlay = defaultNotice(
7577
- "Desktop disconnected",
7578
- stream.error?.message ?? "The stream dropped.",
7579
- reconnect
7580
- );
7581
- break;
7582
- case "opening":
7583
- case "connecting":
7584
- case "connected":
7585
- overlay = null;
7586
- break;
7587
- }
7588
- return /* @__PURE__ */ jsxs9(
7589
- "div",
7590
- {
7591
- className: cn(
7592
- "relative h-full w-full overflow-hidden",
7593
- inControl && "ring-2 ring-inset ring-og-accent",
7594
- className
7595
- ),
7596
- style: { backgroundColor: DESKTOP_MEDIA_BACKGROUND },
7597
- "data-opengeni-desktop": true,
7598
- "data-state": stream.state,
7599
- "data-ui-state": uiState,
7600
- "data-in-control": inControl ? "true" : void 0,
7601
- children: [
7602
- /* @__PURE__ */ jsx11(
7603
- "div",
7604
- {
7605
- ref: containerRef,
7606
- className: "absolute inset-0 overflow-hidden",
7607
- "data-opengeni-desktop-canvas": true,
7608
- "data-state": stream.state
7609
- }
7610
- ),
7611
- (uiState === "opening" || uiState === "connecting") && /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-0 flex flex-col items-center justify-center gap-3 text-og-fg-subtle", children: [
7612
- /* @__PURE__ */ jsxs9("span", { className: "relative flex items-center justify-center", children: [
7613
- /* @__PURE__ */ jsx11(MonitorIcon, { className: "size-8 opacity-30", strokeWidth: 1.5 }),
7614
- /* @__PURE__ */ jsx11(
7615
- LoaderCircleIcon2,
7616
- {
7617
- className: "absolute size-12 animate-og-spin text-og-accent opacity-70",
7618
- strokeWidth: 1.25
7619
- }
7620
- )
7621
- ] }),
7622
- /* @__PURE__ */ jsx11("span", { className: "text-og-sm", children: uiState === "opening" ? "Opening the desktop\u2026" : "Connecting to the desktop\u2026" })
7623
- ] }),
7624
- showToggle && !externallyControlled && inControl && /* @__PURE__ */ jsx11(
7625
- InControlBar,
7626
- {
7627
- shared: capability?.shared ?? false,
7628
- onRelease: () => setTakeControl(false)
7629
- }
7630
- ),
7631
- showToggle && !externallyControlled && !inControl && connected && /* @__PURE__ */ jsx11(
7632
- TakeControlCallToAction,
7633
- {
7634
- disabled: !serverAllowsControl,
7635
- disabledReason: !serverAllowsControl ? capability?.client === "frames" ? "View-only \u2014 live control isn't available for this machine yet." : "This deployment streams the desktop read-only." : void 0,
7636
- onTakeControl: () => setTakeControl(true)
7637
- }
7638
- ),
7639
- overlay && /* @__PURE__ */ jsx11("div", { className: "absolute inset-0 flex items-center justify-center p-4", children: overlay })
7640
- ]
7641
- }
7642
- );
7643
- }
7644
- function TakeControlCallToAction({
7645
- disabled,
7646
- disabledReason,
7647
- onTakeControl
7648
- }) {
7649
- return (
7650
- // The wrapper spans the surface but is click-through (pointer-events-none); only
7651
- // the button itself is interactive, so watchers can still see the desktop.
7652
- /* @__PURE__ */ jsx11("div", { className: "pointer-events-none absolute inset-0 flex items-end justify-center pb-[8%]", children: /* @__PURE__ */ jsxs9(
7653
- "button",
7654
- {
7655
- type: "button",
7656
- disabled,
7657
- "aria-label": "Take control of the desktop",
7658
- title: disabled ? disabledReason : "Take control of the desktop",
7659
- onClick: onTakeControl,
7660
- className: cn(
7661
- "group pointer-events-auto flex items-center gap-3 rounded-og-lg border px-5 py-3",
7662
- "border-og-border",
7663
- "bg-og-bg/85 backdrop-blur-md",
7664
- "shadow-og-lg",
7665
- "outline-hidden transition-all duration-150 ease-out",
7666
- "focus-visible:ring-2 focus-visible:ring-og-accent focus-visible:ring-offset-2 focus-visible:ring-offset-og-bg",
7667
- disabled ? "cursor-not-allowed opacity-60" : cn(
7668
- "cursor-pointer opacity-90 hover:-translate-y-0.5 hover:opacity-100",
7669
- "hover:border-og-accent",
7670
- "hover:bg-og-accent/10"
7671
- )
7672
- ),
7673
- children: [
7674
- /* @__PURE__ */ jsx11(
7675
- "span",
7676
- {
7677
- className: cn(
7678
- "flex size-9 shrink-0 items-center justify-center rounded-full transition-colors",
7679
- "bg-og-accent",
7680
- "text-og-accent-fg",
7681
- disabled ? "" : "group-hover:scale-105"
7682
- ),
7683
- children: /* @__PURE__ */ jsx11(MousePointerClickIcon, { className: "size-5", strokeWidth: 2 })
7684
- }
7685
- ),
7686
- /* @__PURE__ */ jsxs9("span", { className: "flex flex-col items-start leading-tight", children: [
7687
- /* @__PURE__ */ jsx11("span", { className: "text-og-base font-semibold text-og-fg", children: "Take control" }),
7688
- /* @__PURE__ */ jsx11("span", { className: "text-og-xs text-og-fg-subtle", children: disabled && disabledReason ? disabledReason : "Drive the mouse & keyboard" })
7689
- ] })
7690
- ]
7691
- }
7692
- ) })
7693
- );
7694
- }
7695
- function InControlBar({ shared, onRelease }) {
7696
- return /* @__PURE__ */ jsxs9("div", { className: "pointer-events-none absolute inset-x-0 top-0 flex items-center justify-between gap-2 p-2", children: [
7697
- /* @__PURE__ */ jsxs9("span", { className: "pointer-events-auto inline-flex items-center gap-2 rounded-og-sm bg-og-accent px-2.5 py-1 text-og-xs font-medium text-og-accent-fg shadow-og-md", children: [
7698
- /* @__PURE__ */ jsx11("span", { className: "size-1.5 animate-pulse rounded-full bg-current", "aria-hidden": true }),
7699
- "You're in control",
7700
- /* @__PURE__ */ jsx11("span", { className: "opacity-75", children: "\xB7 click Return control (or Ctrl+Alt+Shift)" })
7701
- ] }),
7702
- /* @__PURE__ */ jsxs9("div", { className: "pointer-events-auto flex items-center gap-1.5", children: [
7703
- shared && /* @__PURE__ */ jsx11("span", { className: "rounded-og-sm bg-og-status-failed/85 px-2 py-0.5 text-og-xs text-og-accent-fg", children: "Shared sandbox \u2014 others are watching" }),
7704
- /* @__PURE__ */ jsxs9(
7705
- "button",
7706
- {
7707
- type: "button",
7708
- onClick: onRelease,
7709
- title: "Return control (or press Ctrl+Alt+Shift)",
7710
- className: cn(
7711
- "inline-flex items-center gap-1.5 rounded-og-sm border px-2.5 py-1 text-og-xs font-semibold shadow-og-md transition-colors",
7712
- "border-og-accent bg-og-bg/90 text-og-fg backdrop-blur-sm",
7713
- "outline-hidden hover:bg-og-accent hover:text-og-accent-fg focus-visible:ring-2 focus-visible:ring-og-accent"
7714
- ),
7715
- children: [
7716
- /* @__PURE__ */ jsx11(MonitorIcon, { className: "size-3.5", strokeWidth: 2, "aria-hidden": true }),
7717
- "Return control"
7718
- ]
7719
- }
7720
- )
7721
- ] })
7722
- ] });
7723
- }
7724
- function unavailableCopy(reason) {
7725
- switch (reason) {
7726
- case "backend_unsupported":
7727
- return "This sandbox backend cannot stream a desktop.";
7728
- case "tier_headless":
7729
- return "This deployment is headless \u2014 terminal, files, and diff only.";
7730
- case "os_unsupported":
7731
- return "The sandbox OS does not support a desktop stream.";
7732
- case "not_provisioned":
7733
- return "This sandbox doesn't have a desktop yet.";
7734
- case "disabled_by_policy":
7735
- return "Desktop streaming is disabled on this deployment.";
7736
- case "lease_cold":
7737
- return "Waiting for the sandbox to start\u2026";
7738
- default:
7739
- return "The desktop isn\u2019t available for this sandbox.";
7740
- }
7741
- }
7742
- function WarmingNotice({ onRetry }) {
7743
- return /* @__PURE__ */ jsxs9("div", { className: "flex max-w-sm flex-col items-center gap-3 rounded-og-lg border border-og-border bg-og-bg/90 p-5 text-center text-og-base text-og-fg backdrop-blur-sm", children: [
7744
- /* @__PURE__ */ jsx11(LoaderCircleIcon2, { className: "size-7 animate-og-spin text-og-accent", strokeWidth: 1.5 }),
7745
- /* @__PURE__ */ jsxs9("div", { className: "space-y-1", children: [
7746
- /* @__PURE__ */ jsx11("div", { className: "font-medium", children: "Warming the sandbox\u2026" }),
7747
- /* @__PURE__ */ jsx11("p", { className: "text-og-sm text-og-fg-subtle", children: "Spinning up the desktop \u2014 this takes a few seconds." })
7748
- ] }),
7749
- onRetry && /* @__PURE__ */ jsx11(
7750
- "button",
7751
- {
7752
- type: "button",
7753
- onClick: onRetry,
7754
- className: "rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm text-og-fg-muted transition-colors hover:text-og-fg pointer-coarse:min-h-11",
7755
- children: "Taking too long? Retry"
7756
- }
7757
- )
7758
- ] });
7759
- }
7760
- function defaultNotice(title, body, onRetry) {
7761
- return /* @__PURE__ */ jsxs9("div", { className: "max-w-sm rounded-og-lg border border-og-border bg-og-bg p-4 text-center text-og-base text-og-fg", children: [
7762
- /* @__PURE__ */ jsxs9("div", { className: "mb-1 flex items-center justify-center gap-1.5 font-medium", children: [
7763
- onRetry && /* @__PURE__ */ jsx11(WifiOffIcon, { className: "size-4 opacity-70", strokeWidth: 1.75 }),
7764
- title
7765
- ] }),
7766
- /* @__PURE__ */ jsx11("p", { className: "text-og-sm text-og-fg-subtle", children: body }),
7767
- onRetry && /* @__PURE__ */ jsx11(
7768
- "button",
7769
- {
7770
- type: "button",
7771
- onClick: onRetry,
7772
- className: "mt-3 rounded-og-sm border border-og-border px-3 py-1.5 text-og-sm transition-colors hover:border-og-accent pointer-coarse:min-h-11",
7773
- children: "Reconnect"
7774
- }
7775
- )
7776
- ] });
7777
- }
7778
-
7779
7327
  // src/components/workspace-dock.tsx
7780
7328
  import {
7781
- useCallback as useCallback21,
7782
- useEffect as useEffect22,
7329
+ useCallback as useCallback23,
7330
+ useEffect as useEffect19,
7783
7331
  useId as useId4,
7784
- useLayoutEffect as useLayoutEffect3,
7785
- useRef as useRef19,
7786
- useState as useState19
7332
+ useLayoutEffect as useLayoutEffect4,
7333
+ useRef as useRef16,
7334
+ useState as useState16
7787
7335
  } from "react";
7788
7336
  import {
7789
7337
  ChevronsLeftRightIcon,
@@ -7799,7 +7347,7 @@ import {
7799
7347
  useDefaultLayout,
7800
7348
  usePanelRef
7801
7349
  } from "react-resizable-panels";
7802
- import { Fragment as Fragment4, jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
7350
+ import { Fragment as Fragment4, jsx as jsx11, jsxs as jsxs9 } from "react/jsx-runtime";
7803
7351
  var SERVER_LAYOUT_STORAGE = {
7804
7352
  getItem: () => null,
7805
7353
  setItem: () => {
@@ -7813,10 +7361,10 @@ function getLayoutStorage() {
7813
7361
  return SERVER_LAYOUT_STORAGE;
7814
7362
  }
7815
7363
  }
7816
- var useDockLayoutEffect = typeof window === "undefined" ? useEffect22 : useLayoutEffect3;
7364
+ var useDockLayoutEffect = typeof window === "undefined" ? useEffect19 : useLayoutEffect4;
7817
7365
  var DOCK_OVERLAY_BREAKPOINT = 1024;
7818
7366
  function useIsNarrow(maxWidth) {
7819
- const [narrow, setNarrow] = useState19(false);
7367
+ const [narrow, setNarrow] = useState16(false);
7820
7368
  useDockLayoutEffect(() => {
7821
7369
  if (typeof window === "undefined" || typeof window.matchMedia !== "function") {
7822
7370
  return;
@@ -7850,17 +7398,17 @@ function WorkspaceDock({
7850
7398
  className
7851
7399
  }) {
7852
7400
  const narrow = useIsNarrow(DOCK_OVERLAY_BREAKPOINT);
7853
- const rootRef = useRef19(null);
7854
- const reopenRef = useRef19(null);
7855
- const returnFocusRef = useRef19(null);
7856
- const initialNarrowFocusRef = useRef19(false);
7401
+ const rootRef = useRef16(null);
7402
+ const reopenRef = useRef16(null);
7403
+ const returnFocusRef = useRef16(null);
7404
+ const initialNarrowFocusRef = useRef16(false);
7857
7405
  const dockPanelRef = usePanelRef();
7858
- const [internalCollapsed, setInternalCollapsed] = useState19(false);
7859
- const [maximized, setMaximized] = useState19(false);
7860
- const [internalTab, setInternalTab] = useState19(tabs[0]?.id ?? "");
7406
+ const [internalCollapsed, setInternalCollapsed] = useState16(false);
7407
+ const [maximized, setMaximized] = useState16(false);
7408
+ const [internalTab, setInternalTab] = useState16(tabs[0]?.id ?? "");
7861
7409
  const collapsed = collapsedProp ?? internalCollapsed;
7862
7410
  const hostControlled = collapsedProp !== void 0;
7863
- const previousCollapsedRef = useRef19(collapsed);
7411
+ const previousCollapsedRef = useRef16(collapsed);
7864
7412
  const { defaultLayout, onLayoutChanged } = useDefaultLayout({
7865
7413
  panelIds: ["primary", "dock"],
7866
7414
  storage: getLayoutStorage(),
@@ -7871,14 +7419,14 @@ function WorkspaceDock({
7871
7419
  const firstTabId = tabs[0]?.id ?? "";
7872
7420
  const requestedTabIsValid = tabIds.includes(requestedTab);
7873
7421
  const current = requestedTabIsValid ? requestedTab : firstTabId;
7874
- const setTab = useCallback21(
7422
+ const setTab = useCallback23(
7875
7423
  (id) => {
7876
7424
  setInternalTab(id);
7877
7425
  onActiveTabChange?.(id);
7878
7426
  },
7879
7427
  [onActiveTabChange]
7880
7428
  );
7881
- const setCollapsed = useCallback21(
7429
+ const setCollapsed = useCallback23(
7882
7430
  (next) => {
7883
7431
  setInternalCollapsed((previous) => previous === next ? previous : next);
7884
7432
  onCollapsedChange?.(next);
@@ -7896,7 +7444,7 @@ function WorkspaceDock({
7896
7444
  dockPanelRef.current?.expand();
7897
7445
  }
7898
7446
  }, [collapsedProp, dockPanelRef]);
7899
- useEffect22(() => {
7447
+ useEffect19(() => {
7900
7448
  const previous = previousCollapsedRef.current;
7901
7449
  previousCollapsedRef.current = collapsed;
7902
7450
  if (previous === collapsed) return;
@@ -7926,7 +7474,7 @@ function WorkspaceDock({
7926
7474
  });
7927
7475
  return () => cancelAnimationFrame(frame);
7928
7476
  }, [collapsed, hostControlled, narrow]);
7929
- useEffect22(() => {
7477
+ useEffect19(() => {
7930
7478
  if (!narrow || collapsed || initialNarrowFocusRef.current) return;
7931
7479
  initialNarrowFocusRef.current = true;
7932
7480
  const active = document.activeElement;
@@ -7939,20 +7487,20 @@ function WorkspaceDock({
7939
7487
  });
7940
7488
  return () => cancelAnimationFrame(frame);
7941
7489
  }, [collapsed, narrow]);
7942
- useEffect22(() => {
7490
+ useEffect19(() => {
7943
7491
  if (firstTabId && !requestedTabIsValid) {
7944
7492
  setTab(firstTabId);
7945
7493
  }
7946
7494
  }, [firstTabId, requestedTabIsValid, setTab]);
7947
- const collapse = useCallback21(() => {
7495
+ const collapse = useCallback23(() => {
7948
7496
  dockPanelRef.current?.collapse();
7949
7497
  setCollapsed(true);
7950
7498
  }, [dockPanelRef, setCollapsed]);
7951
- const expand = useCallback21(() => {
7499
+ const expand = useCallback23(() => {
7952
7500
  dockPanelRef.current?.expand();
7953
7501
  setCollapsed(false);
7954
7502
  }, [dockPanelRef, setCollapsed]);
7955
- useEffect22(() => {
7503
+ useEffect19(() => {
7956
7504
  const overlayOpen = maximized || narrow && !collapsed;
7957
7505
  if (!overlayOpen) return;
7958
7506
  const onKey = (event) => {
@@ -7996,8 +7544,8 @@ function WorkspaceDock({
7996
7544
  return () => window.removeEventListener("keydown", onKey);
7997
7545
  }, [maximized, narrow, collapsed, collapse]);
7998
7546
  if (narrow) {
7999
- return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
8000
- /* @__PURE__ */ jsx12(
7547
+ return /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
7548
+ /* @__PURE__ */ jsx11(
8001
7549
  "div",
8002
7550
  {
8003
7551
  "data-workspace-primary": true,
@@ -8007,7 +7555,7 @@ function WorkspaceDock({
8007
7555
  children: primary
8008
7556
  }
8009
7557
  ),
8010
- /* @__PURE__ */ jsx12(
7558
+ /* @__PURE__ */ jsx11(
8011
7559
  "div",
8012
7560
  {
8013
7561
  "data-workspace-surface": true,
@@ -8025,7 +7573,7 @@ function WorkspaceDock({
8025
7573
  paddingTop: "env(safe-area-inset-top)",
8026
7574
  paddingBottom: "env(safe-area-inset-bottom)"
8027
7575
  },
8028
- children: /* @__PURE__ */ jsx12(
7576
+ children: /* @__PURE__ */ jsx11(
8029
7577
  DockChrome,
8030
7578
  {
8031
7579
  tabs,
@@ -8033,12 +7581,12 @@ function WorkspaceDock({
8033
7581
  onTab: setTab,
8034
7582
  leading: mobileLeadingControl,
8035
7583
  accessory: headerAccessory,
8036
- controls: /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Close workspace", label: "Close workspace", children: /* @__PURE__ */ jsx12(XIcon2, { className: "size-4" }) })
7584
+ controls: /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Close workspace", label: "Close workspace", children: /* @__PURE__ */ jsx11(XIcon2, { className: "size-4" }) })
8037
7585
  }
8038
7586
  )
8039
7587
  }
8040
7588
  ),
8041
- collapsed && !hostControlled ? /* @__PURE__ */ jsx12(
7589
+ collapsed && !hostControlled ? /* @__PURE__ */ jsx11(
8042
7590
  "button",
8043
7591
  {
8044
7592
  ref: reopenRef,
@@ -8048,34 +7596,34 @@ function WorkspaceDock({
8048
7596
  "aria-label": "Open workspace",
8049
7597
  className: "absolute right-3 top-3 z-30 inline-flex size-11 items-center justify-center rounded-og-md border border-og-border bg-og-surface-1 text-og-fg-muted shadow-lg transition-colors hover:border-og-border-strong hover:text-og-fg focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent",
8050
7598
  style: { marginTop: "env(safe-area-inset-top)" },
8051
- children: /* @__PURE__ */ jsx12(ChevronsLeftRightIcon, { className: "size-4", "aria-hidden": true })
7599
+ children: /* @__PURE__ */ jsx11(ChevronsLeftRightIcon, { className: "size-4", "aria-hidden": true })
8052
7600
  }
8053
7601
  ) : null
8054
7602
  ] });
8055
7603
  }
8056
- const dockChrome = /* @__PURE__ */ jsx12(
7604
+ const dockChrome = /* @__PURE__ */ jsx11(
8057
7605
  DockChrome,
8058
7606
  {
8059
7607
  tabs,
8060
7608
  current,
8061
7609
  onTab: setTab,
8062
7610
  accessory: headerAccessory,
8063
- controls: /* @__PURE__ */ jsxs10(Fragment4, { children: [
8064
- /* @__PURE__ */ jsx12(
7611
+ controls: /* @__PURE__ */ jsxs9(Fragment4, { children: [
7612
+ /* @__PURE__ */ jsx11(
8065
7613
  ChromeButton,
8066
7614
  {
8067
7615
  onClick: () => setMaximized((m) => !m),
8068
7616
  title: maximized ? "Restore (Esc)" : "Maximize",
8069
7617
  label: maximized ? "Restore dock" : "Maximize dock",
8070
- children: maximized ? /* @__PURE__ */ jsx12(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx12(Maximize2Icon, { className: "size-3.5" })
7618
+ children: maximized ? /* @__PURE__ */ jsx11(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx11(Maximize2Icon, { className: "size-3.5" })
8071
7619
  }
8072
7620
  ),
8073
- hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx12(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx12(PanelRightCloseIcon, { className: "size-3.5" }) })
7621
+ hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx11(PanelRightCloseIcon, { className: "size-3.5" }) })
8074
7622
  ] })
8075
7623
  }
8076
7624
  );
8077
- return /* @__PURE__ */ jsxs10("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
8078
- /* @__PURE__ */ jsxs10(
7625
+ return /* @__PURE__ */ jsxs9("div", { ref: rootRef, className: cn("relative flex h-full min-h-0 w-full min-w-0", className), children: [
7626
+ /* @__PURE__ */ jsxs9(
8079
7627
  Group2,
8080
7628
  {
8081
7629
  orientation: "horizontal",
@@ -8083,7 +7631,7 @@ function WorkspaceDock({
8083
7631
  ...defaultLayout ? { defaultLayout } : {},
8084
7632
  onLayoutChanged,
8085
7633
  children: [
8086
- /* @__PURE__ */ jsx12(Panel2, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx12(
7634
+ /* @__PURE__ */ jsx11(Panel2, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx11(
8087
7635
  "div",
8088
7636
  {
8089
7637
  "data-workspace-primary": true,
@@ -8093,8 +7641,8 @@ function WorkspaceDock({
8093
7641
  children: primary
8094
7642
  }
8095
7643
  ) }),
8096
- !collapsed && /* @__PURE__ */ jsx12(Separator2, { className: "group relative z-10 w-1.5 shrink-0 outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11", children: /* @__PURE__ */ jsx12("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent" }) }),
8097
- /* @__PURE__ */ jsx12(
7644
+ !collapsed && /* @__PURE__ */ jsx11(Separator2, { className: "group relative z-10 w-1.5 shrink-0 outline-hidden focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-og-accent max-[1023px]:-mx-[19px] max-[1023px]:w-11 pointer-coarse:-mx-[19px] pointer-coarse:w-11", children: /* @__PURE__ */ jsx11("span", { className: "absolute inset-y-0 left-1/2 w-px -translate-x-1/2 bg-og-border transition-colors group-hover:bg-og-accent group-focus-visible:bg-og-accent group-data-[separator-state=dragging]:bg-og-accent" }) }),
7645
+ /* @__PURE__ */ jsx11(
8098
7646
  Panel2,
8099
7647
  {
8100
7648
  id: "dock",
@@ -8112,7 +7660,7 @@ function WorkspaceDock({
8112
7660
  }
8113
7661
  },
8114
7662
  className: "min-h-0 min-w-0",
8115
- children: /* @__PURE__ */ jsx12(
7663
+ children: /* @__PURE__ */ jsx11(
8116
7664
  "div",
8117
7665
  {
8118
7666
  "data-workspace-surface": true,
@@ -8133,7 +7681,7 @@ function WorkspaceDock({
8133
7681
  ]
8134
7682
  }
8135
7683
  ),
8136
- collapsed && !maximized && !hostControlled && /* @__PURE__ */ jsx12(
7684
+ collapsed && !maximized && !hostControlled && /* @__PURE__ */ jsx11(
8137
7685
  "button",
8138
7686
  {
8139
7687
  ref: reopenRef,
@@ -8141,7 +7689,7 @@ function WorkspaceDock({
8141
7689
  onClick: expand,
8142
7690
  title: "Open workspace",
8143
7691
  className: "absolute inset-y-0 right-0 flex w-6 shrink-0 items-center justify-center border-l border-og-border bg-og-surface-1 text-og-fg-subtle hover:text-og-fg",
8144
- children: /* @__PURE__ */ jsx12(ChevronsLeftRightIcon, { className: "size-3.5" })
7692
+ children: /* @__PURE__ */ jsx11(ChevronsLeftRightIcon, { className: "size-3.5" })
8145
7693
  }
8146
7694
  )
8147
7695
  ] });
@@ -8152,7 +7700,7 @@ function ChromeButton({
8152
7700
  label,
8153
7701
  children
8154
7702
  }) {
8155
- return /* @__PURE__ */ jsx12(
7703
+ return /* @__PURE__ */ jsx11(
8156
7704
  "button",
8157
7705
  {
8158
7706
  type: "button",
@@ -8172,19 +7720,38 @@ function DockChrome({
8172
7720
  accessory,
8173
7721
  controls
8174
7722
  }) {
7723
+ const chromeRef = useRef16(null);
7724
+ const [compact, setCompact] = useState16(false);
8175
7725
  const active = tabs.find((t) => t.id === current) ?? tabs[0];
8176
7726
  const tabsetId = useId4();
8177
- const tabRefs = useRef19([]);
8178
- const tabListRef = useRef19(null);
8179
- const [visitedTabs, setVisitedTabs] = useState19(() => /* @__PURE__ */ new Set());
8180
- const [tabOverflow, setTabOverflow] = useState19({ start: false, end: false });
7727
+ const tabRefs = useRef16([]);
7728
+ const tabListRef = useRef16(null);
7729
+ const [visitedTabs, setVisitedTabs] = useState16(() => /* @__PURE__ */ new Set());
7730
+ const [tabOverflow, setTabOverflow] = useState16({ start: false, end: false });
8181
7731
  const activeId = active?.id ?? "";
8182
7732
  const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);
8183
- const keepActiveTabVisible = useCallback21(() => {
7733
+ useDockLayoutEffect(() => {
7734
+ const node = chromeRef.current;
7735
+ if (!node) return;
7736
+ const update = () => {
7737
+ const desktop = typeof window.matchMedia !== "function" || window.matchMedia("(min-width: 1024px)").matches;
7738
+ const next = desktop && node.getBoundingClientRect().width < 420;
7739
+ setCompact((previous) => previous === next ? previous : next);
7740
+ };
7741
+ update();
7742
+ if (typeof ResizeObserver === "undefined") {
7743
+ window.addEventListener("resize", update);
7744
+ return () => window.removeEventListener("resize", update);
7745
+ }
7746
+ const observer = new ResizeObserver(update);
7747
+ observer.observe(node);
7748
+ return () => observer.disconnect();
7749
+ }, []);
7750
+ const keepActiveTabVisible = useCallback23(() => {
8184
7751
  const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;
8185
7752
  selected?.scrollIntoView({ block: "nearest", inline: "nearest" });
8186
7753
  }, [activeTabIndex]);
8187
- useEffect22(() => {
7754
+ useEffect19(() => {
8188
7755
  if (!activeId) return;
8189
7756
  setVisitedTabs((previous) => {
8190
7757
  if (previous.has(activeId)) return previous;
@@ -8241,55 +7808,98 @@ function DockChrome({
8241
7808
  event.preventDefault();
8242
7809
  activateTab(nextIndex);
8243
7810
  };
8244
- return /* @__PURE__ */ jsxs10(Fragment4, { children: [
8245
- /* @__PURE__ */ jsxs10("div", { className: "grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]", children: [
8246
- /* @__PURE__ */ jsx12("div", { className: "flex min-w-0 shrink-0 items-center max-[1023px]:min-h-11 min-[640px]:max-[1023px]:col-start-1 min-[640px]:max-[1023px]:row-start-1", children: leading ?? /* @__PURE__ */ jsx12("span", { className: "hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" }) }),
8247
- /* @__PURE__ */ jsx12(
8248
- "div",
8249
- {
8250
- ref: tabListRef,
8251
- className: "flex min-w-0 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-[1023px]:col-span-3 max-[1023px]:row-start-2 max-[1023px]:w-full min-[640px]:max-[1023px]:col-span-1 min-[640px]:max-[1023px]:col-start-2 min-[640px]:max-[1023px]:row-start-1",
8252
- role: "tablist",
8253
- "aria-orientation": "horizontal",
8254
- style: {
8255
- maskImage: tabMask,
8256
- WebkitMaskImage: tabMask,
8257
- scrollPaddingInline: "16px"
8258
- },
8259
- children: tabs.map((tab, index) => /* @__PURE__ */ jsxs10(
8260
- "button",
7811
+ return /* @__PURE__ */ jsxs9(Fragment4, { children: [
7812
+ /* @__PURE__ */ jsxs9(
7813
+ "div",
7814
+ {
7815
+ ref: chromeRef,
7816
+ "data-dock-chrome": true,
7817
+ "data-compact": compact ? "true" : void 0,
7818
+ className: cn(
7819
+ "grid shrink-0 grid-cols-[auto_minmax(0,1fr)_auto_auto] items-center gap-x-2 border-b border-og-border px-1.5 py-1 max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto] max-[1023px]:gap-y-0 max-[1023px]:px-2 max-[1023px]:pb-1 max-[1023px]:pt-0 min-[640px]:max-[1023px]:grid-cols-[auto_minmax(0,1fr)_auto_auto]",
7820
+ compact && "gap-y-1"
7821
+ ),
7822
+ style: compact ? { gridTemplateColumns: "minmax(0, 1fr) auto" } : void 0,
7823
+ children: [
7824
+ /* @__PURE__ */ jsx11(
7825
+ "div",
7826
+ {
7827
+ className: cn(
7828
+ "flex min-w-0 shrink-0 items-center max-[1023px]:min-h-11 min-[640px]:max-[1023px]:col-start-1 min-[640px]:max-[1023px]:row-start-1",
7829
+ compact && "hidden"
7830
+ ),
7831
+ children: leading ?? /* @__PURE__ */ jsx11("span", { className: "hidden truncate px-1 text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" })
7832
+ }
7833
+ ),
7834
+ /* @__PURE__ */ jsx11(
7835
+ "div",
8261
7836
  {
8262
- ref: (node) => {
8263
- tabRefs.current[index] = node;
7837
+ ref: tabListRef,
7838
+ className: cn(
7839
+ "flex min-w-0 items-center gap-0.5 overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden max-[1023px]:col-span-3 max-[1023px]:row-start-2 max-[1023px]:w-full min-[640px]:max-[1023px]:col-span-1 min-[640px]:max-[1023px]:col-start-2 min-[640px]:max-[1023px]:row-start-1",
7840
+ compact && "col-span-2 col-start-1 row-start-2 w-full"
7841
+ ),
7842
+ role: "tablist",
7843
+ "aria-orientation": "horizontal",
7844
+ style: {
7845
+ maskImage: tabMask,
7846
+ WebkitMaskImage: tabMask,
7847
+ scrollPaddingInline: "16px"
8264
7848
  },
8265
- id: `${tabsetId}-tab-${index}`,
8266
- type: "button",
8267
- role: "tab",
8268
- "aria-selected": tab.id === current,
8269
- "aria-controls": `${tabsetId}-panel-${index}`,
8270
- tabIndex: tab.id === current ? 0 : -1,
8271
- onClick: () => onTab(tab.id),
8272
- onKeyDown: (event) => onTabKeyDown(event, index),
7849
+ children: tabs.map((tab, index) => /* @__PURE__ */ jsxs9(
7850
+ "button",
7851
+ {
7852
+ ref: (node) => {
7853
+ tabRefs.current[index] = node;
7854
+ },
7855
+ id: `${tabsetId}-tab-${index}`,
7856
+ type: "button",
7857
+ role: "tab",
7858
+ "aria-selected": tab.id === current,
7859
+ "aria-controls": `${tabsetId}-panel-${index}`,
7860
+ tabIndex: tab.id === current ? 0 : -1,
7861
+ onClick: () => onTab(tab.id),
7862
+ onKeyDown: (event) => onTabKeyDown(event, index),
7863
+ className: cn(
7864
+ "flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
7865
+ tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
7866
+ ),
7867
+ children: [
7868
+ /* @__PURE__ */ jsx11("span", { "data-contrast-audited": true, children: tab.label }),
7869
+ tab.badge
7870
+ ]
7871
+ },
7872
+ tab.id
7873
+ ))
7874
+ }
7875
+ ),
7876
+ /* @__PURE__ */ jsx11(
7877
+ "div",
7878
+ {
8273
7879
  className: cn(
8274
- "flex min-h-7 shrink-0 items-center justify-center gap-1 rounded-og-sm px-2 py-1 text-og-xs font-medium transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent max-[1023px]:min-h-11 max-[1023px]:min-w-11 pointer-coarse:min-h-11 pointer-coarse:min-w-11",
8275
- tab.id === current ? "bg-og-accent-soft text-og-fg" : "text-og-fg-subtle hover:text-og-fg"
7880
+ "flex min-w-0 shrink-0 items-center justify-self-end min-[640px]:max-[1023px]:col-start-3 min-[640px]:max-[1023px]:row-start-1",
7881
+ compact && "col-start-1 row-start-1 justify-self-start"
8276
7882
  ),
8277
- children: [
8278
- /* @__PURE__ */ jsx12("span", { "data-contrast-audited": true, children: tab.label }),
8279
- tab.badge
8280
- ]
8281
- },
8282
- tab.id
8283
- ))
8284
- }
8285
- ),
8286
- /* @__PURE__ */ jsx12("div", { className: "flex min-w-0 shrink-0 items-center justify-self-end min-[640px]:max-[1023px]:col-start-3 min-[640px]:max-[1023px]:row-start-1", children: accessory }),
8287
- /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center gap-0.5 text-og-fg-subtle min-[640px]:max-[1023px]:col-start-4 min-[640px]:max-[1023px]:row-start-1", children: controls })
8288
- ] }),
8289
- /* @__PURE__ */ jsx12("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children: tabs.length > 0 ? tabs.map((tab, index) => {
7883
+ children: accessory
7884
+ }
7885
+ ),
7886
+ /* @__PURE__ */ jsx11(
7887
+ "div",
7888
+ {
7889
+ className: cn(
7890
+ "flex shrink-0 items-center gap-0.5 text-og-fg-subtle min-[640px]:max-[1023px]:col-start-4 min-[640px]:max-[1023px]:row-start-1",
7891
+ compact && "col-start-2 row-start-1"
7892
+ ),
7893
+ children: controls
7894
+ }
7895
+ )
7896
+ ]
7897
+ }
7898
+ ),
7899
+ /* @__PURE__ */ jsx11("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children: tabs.length > 0 ? tabs.map((tab, index) => {
8290
7900
  const selected = tab.id === activeId;
8291
7901
  const shouldMount = selected || visitedTabs.has(tab.id);
8292
- return /* @__PURE__ */ jsx12(
7902
+ return /* @__PURE__ */ jsx11(
8293
7903
  "div",
8294
7904
  {
8295
7905
  id: `${tabsetId}-panel-${index}`,
@@ -8301,7 +7911,7 @@ function DockChrome({
8301
7911
  },
8302
7912
  tab.id
8303
7913
  );
8304
- }) : /* @__PURE__ */ jsx12("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
7914
+ }) : /* @__PURE__ */ jsx11("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
8305
7915
  ] });
8306
7916
  }
8307
7917
 
@@ -8309,11 +7919,11 @@ function DockChrome({
8309
7919
  import {
8310
7920
  lazy as lazy2,
8311
7921
  Suspense as Suspense2,
8312
- useCallback as useCallback22,
8313
- useEffect as useEffect23,
7922
+ useCallback as useCallback24,
7923
+ useEffect as useEffect20,
8314
7924
  useMemo as useMemo13,
8315
- useRef as useRef20,
8316
- useState as useState20
7925
+ useRef as useRef17,
7926
+ useState as useState17
8317
7927
  } from "react";
8318
7928
  import {
8319
7929
  CircleCheckIcon,
@@ -8321,19 +7931,19 @@ import {
8321
7931
  FileCode2Icon as FileCode2Icon2,
8322
7932
  GitCompareArrowsIcon,
8323
7933
  Globe2Icon,
8324
- LoaderCircleIcon as LoaderCircleIcon3,
8325
- MonitorIcon as MonitorIcon2,
7934
+ LoaderCircleIcon as LoaderCircleIcon2,
7935
+ MonitorIcon,
8326
7936
  RefreshCwIcon as RefreshCwIcon2,
8327
7937
  SquareTerminalIcon,
8328
7938
  TriangleAlertIcon as TriangleAlertIcon2
8329
7939
  } from "lucide-react";
8330
- import { jsx as jsx13, jsxs as jsxs11 } from "react/jsx-runtime";
7940
+ import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
8331
7941
  var LazyBrowserViewer = lazy2(async () => {
8332
- const viewers = await import("./interactive-workbench-viewers-KHNOHWQE.js");
7942
+ const viewers = await import("./interactive-workbench-viewers-TOHBXJEE.js");
8333
7943
  return { default: viewers.BrowserViewer };
8334
7944
  });
8335
7945
  var LazyComputerViewer = lazy2(async () => {
8336
- const viewers = await import("./interactive-workbench-viewers-KHNOHWQE.js");
7946
+ const viewers = await import("./interactive-workbench-viewers-TOHBXJEE.js");
8337
7947
  return { default: viewers.ComputerViewer };
8338
7948
  });
8339
7949
  var WORKBENCH_TAB_CHANGES = "changes";
@@ -8370,17 +7980,17 @@ function captureDegradedMessage(reason) {
8370
7980
  }
8371
7981
  }
8372
7982
  function WorkbenchTabLabel({ icon, children }) {
8373
- return /* @__PURE__ */ jsxs11("span", { className: "inline-flex items-center gap-1.5", children: [
8374
- /* @__PURE__ */ jsx13("span", { className: "[&>svg]:size-3.5", "aria-hidden": true, children: icon }),
8375
- /* @__PURE__ */ jsx13("span", { children })
7983
+ return /* @__PURE__ */ jsxs10("span", { className: "inline-flex items-center gap-1.5", children: [
7984
+ /* @__PURE__ */ jsx12("span", { className: "[&>svg]:size-3.5", "aria-hidden": true, children: icon }),
7985
+ /* @__PURE__ */ jsx12("span", { children })
8376
7986
  ] });
8377
7987
  }
8378
7988
  function WorkbenchSurfaceLoading({ name }) {
8379
- return /* @__PURE__ */ jsx13(
7989
+ return /* @__PURE__ */ jsx12(
8380
7990
  CenteredState,
8381
7991
  {
8382
- icon: /* @__PURE__ */ jsx13(LoaderCircleIcon3, { className: "size-5 animate-spin motion-reduce:animate-none", "aria-hidden": true }),
8383
- children: /* @__PURE__ */ jsxs11("p", { className: "text-og-sm font-medium text-og-fg", children: [
7992
+ icon: /* @__PURE__ */ jsx12(LoaderCircleIcon2, { className: "size-5 animate-spin motion-reduce:animate-none", "aria-hidden": true }),
7993
+ children: /* @__PURE__ */ jsxs10("p", { className: "text-og-sm font-medium text-og-fg", children: [
8384
7994
  "Opening ",
8385
7995
  name
8386
7996
  ] })
@@ -8435,7 +8045,7 @@ function emptyWarmIntents(sessionId) {
8435
8045
  };
8436
8046
  }
8437
8047
  function useSandboxWorkspaceTabs(options) {
8438
- const { client, workspaceId } = useOpenGeni(options);
8048
+ const { client, workspaceId, workspaceInteractionEvent } = useOpenGeni(options);
8439
8049
  const {
8440
8050
  sessionId,
8441
8051
  events,
@@ -8451,6 +8061,7 @@ function useSandboxWorkspaceTabs(options) {
8451
8061
  } = options;
8452
8062
  const initialTab = options.initialTab ?? null;
8453
8063
  const activeTab = options.activeTab ?? initialTab;
8064
+ const workspaceVisible = options.workspaceVisible ?? true;
8454
8065
  const requestedSurfaces = options.surfaces ?? WORKBENCH_SURFACES;
8455
8066
  const surfaceSet = new Set(requestedSurfaces);
8456
8067
  const changesEnabled = surfaceSet.has(WORKBENCH_TAB_CHANGES);
@@ -8458,13 +8069,17 @@ function useSandboxWorkspaceTabs(options) {
8458
8069
  const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);
8459
8070
  const browserEnabled = surfaceSet.has(WORKBENCH_TAB_BROWSER);
8460
8071
  const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);
8461
- const createLinkedComputer = useCallback22(
8462
- async (name) => {
8072
+ const createLinkedComputer = useCallback24(
8073
+ async (name, placement) => {
8463
8074
  const response = await client.createComputerSession(workspaceId, {
8464
8075
  operationId: crypto.randomUUID(),
8465
8076
  sessionId,
8466
- name
8077
+ name,
8078
+ ...placement ? { placement } : {}
8467
8079
  });
8080
+ if (response.operation.state !== "completed" || response.session.lifecycle !== "active") {
8081
+ throw new Error(response.operation.error?.message ?? "The computer could not be opened.");
8082
+ }
8468
8083
  return {
8469
8084
  id: response.session.id,
8470
8085
  placement: response.session.placement
@@ -8478,12 +8093,12 @@ function useSandboxWorkspaceTabs(options) {
8478
8093
  const changesActive = resolvedActiveTab === WORKBENCH_TAB_CHANGES;
8479
8094
  const filesActive = resolvedActiveTab === WORKBENCH_TAB_FILES;
8480
8095
  const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(",");
8481
- const [storedWarmIntents, setStoredWarmIntents] = useState20(
8096
+ const [storedWarmIntents, setStoredWarmIntents] = useState17(
8482
8097
  () => emptyWarmIntents(sessionId)
8483
8098
  );
8484
8099
  const warmIntents = storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
8485
8100
  const { warmTerminal, warmFiles } = warmIntents;
8486
- const requestWarmIntent = useCallback22(
8101
+ const requestWarmIntent = useCallback24(
8487
8102
  (intent) => {
8488
8103
  setStoredWarmIntents((previous) => {
8489
8104
  const current = previous.sessionId === sessionId ? previous : emptyWarmIntents(sessionId);
@@ -8524,14 +8139,18 @@ function useSandboxWorkspaceTabs(options) {
8524
8139
  });
8525
8140
  const provisioning = sandboxProvisionInFlight(events);
8526
8141
  const renegotiate = caps.renegotiate;
8527
- const provisioningRef = useRef20({ sessionId, provisioning });
8528
- useEffect23(() => {
8142
+ const provisioningRef = useRef17({ sessionId, provisioning });
8143
+ useEffect20(() => {
8529
8144
  const previous = provisioningRef.current;
8530
8145
  if (previous.sessionId === sessionId && previous.provisioning && !provisioning) {
8531
8146
  renegotiate();
8532
8147
  }
8533
8148
  provisioningRef.current = { sessionId, provisioning };
8534
8149
  }, [sessionId, provisioning, renegotiate]);
8150
+ useEffect20(() => {
8151
+ if (workspaceInteractionEvent?.workspaceId !== workspaceId) return;
8152
+ renegotiate();
8153
+ }, [workspaceId, workspaceInteractionEvent, renegotiate]);
8535
8154
  const captureState = useWorkspaceCapture(sessionId, {
8536
8155
  events,
8537
8156
  enabled: workspaceDataEnabled
@@ -8544,14 +8163,14 @@ function useSandboxWorkspaceTabs(options) {
8544
8163
  if (advertised.length > 0) return advertised;
8545
8164
  return captureState.capture?.repos.map((repo) => repo.root) ?? [];
8546
8165
  }, [capabilities?.Git.repos, captureState.capture]);
8547
- const notifiedCaptureDegradedReason = useRef20({
8166
+ const notifiedCaptureDegradedReason = useRef17({
8548
8167
  sessionId,
8549
8168
  reason: null
8550
8169
  });
8551
8170
  if (notifiedCaptureDegradedReason.current.sessionId !== sessionId) {
8552
8171
  notifiedCaptureDegradedReason.current = { sessionId, reason: null };
8553
8172
  }
8554
- useEffect23(() => {
8173
+ useEffect20(() => {
8555
8174
  const reason = captureState.degradedReason;
8556
8175
  if (!reason || notifiedCaptureDegradedReason.current.reason === reason) return;
8557
8176
  notifiedCaptureDegradedReason.current = { sessionId, reason };
@@ -8573,10 +8192,10 @@ function useSandboxWorkspaceTabs(options) {
8573
8192
  message: `Could not ${op}: ${error.message}`
8574
8193
  })
8575
8194
  });
8576
- const [storedChangesComparison, setStoredChangesComparison] = useState20(null);
8195
+ const [storedChangesComparison, setStoredChangesComparison] = useState17(null);
8577
8196
  const captureSupportsBranch = captureState.capture !== null && captureState.capture.repos.length > 0 && captureState.capture.repos.every((repo) => repo.branchDiff !== void 0);
8578
8197
  const changesComparison = storedChangesComparison?.sessionId === sessionId ? storedChangesComparison.value : liveWorkspaceExpected || captureSupportsBranch ? "branch" : "working";
8579
- const setChangesComparison = useCallback22(
8198
+ const setChangesComparison = useCallback24(
8580
8199
  (value) => setStoredChangesComparison({ sessionId, value }),
8581
8200
  [sessionId]
8582
8201
  );
@@ -8592,13 +8211,13 @@ function useSandboxWorkspaceTabs(options) {
8592
8211
  comparison: changesComparison,
8593
8212
  capture: changesComparison === "staged" ? null : captureState.capture
8594
8213
  });
8595
- useEffect23(() => {
8214
+ useEffect20(() => {
8596
8215
  if (changesComparison === "branch" && git.error && storedChangesComparison?.sessionId !== sessionId) {
8597
8216
  setStoredChangesComparison({ sessionId, value: "working" });
8598
8217
  }
8599
8218
  }, [changesComparison, git.error, sessionId, storedChangesComparison?.sessionId]);
8600
- const [xtermTheme, setXtermTheme] = useState20(void 0);
8601
- useEffect23(() => {
8219
+ const [xtermTheme, setXtermTheme] = useState17(void 0);
8220
+ useEffect20(() => {
8602
8221
  if (!terminalEnabled || typeof document === "undefined") return;
8603
8222
  const derive = () => setXtermTheme(xtermThemeFromTokens());
8604
8223
  derive();
@@ -8620,7 +8239,7 @@ function useSandboxWorkspaceTabs(options) {
8620
8239
  });
8621
8240
  const workspaceWaking = chip.state === "waking";
8622
8241
  const defaultIdentity = `${sessionId}\0${surfaceIdentity}`;
8623
- const defaultTabRef = useRef20({
8242
+ const defaultTabRef = useRef17({
8624
8243
  identity: defaultIdentity,
8625
8244
  value: null
8626
8245
  });
@@ -8668,9 +8287,9 @@ function useSandboxWorkspaceTabs(options) {
8668
8287
  if (changesEnabled)
8669
8288
  list.push({
8670
8289
  id: WORKBENCH_TAB_CHANGES,
8671
- label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(GitCompareArrowsIcon, {}), children: "Changes" }),
8672
- badge: dirtyCount > 0 ? /* @__PURE__ */ jsx13(DirtyBadge, { count: dirtyCount }) : void 0,
8673
- content: /* @__PURE__ */ jsx13(
8290
+ label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(GitCompareArrowsIcon, {}), children: "Changes" }),
8291
+ badge: dirtyCount > 0 ? /* @__PURE__ */ jsx12(DirtyBadge, { count: dirtyCount }) : void 0,
8292
+ content: /* @__PURE__ */ jsx12(
8674
8293
  ChangesTabBody,
8675
8294
  {
8676
8295
  git,
@@ -8697,8 +8316,8 @@ function useSandboxWorkspaceTabs(options) {
8697
8316
  if (filesEnabled)
8698
8317
  list.push({
8699
8318
  id: WORKBENCH_TAB_FILES,
8700
- label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(FileCode2Icon2, {}), children: "Files" }),
8701
- content: /* @__PURE__ */ jsx13(
8319
+ label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(FileCode2Icon2, {}), children: "Files" }),
8320
+ content: /* @__PURE__ */ jsx12(
8702
8321
  SandboxFiles,
8703
8322
  {
8704
8323
  files,
@@ -8723,13 +8342,14 @@ function useSandboxWorkspaceTabs(options) {
8723
8342
  if (terminalOn) {
8724
8343
  list.push({
8725
8344
  id: WORKBENCH_TAB_TERMINAL,
8726
- label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(SquareTerminalIcon, {}), children: "Terminal" }),
8727
- content: /* @__PURE__ */ jsx13("div", { className: "h-full bg-og-bg p-1", children: /* @__PURE__ */ jsx13(
8345
+ label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(SquareTerminalIcon, {}), children: "Terminal" }),
8346
+ content: /* @__PURE__ */ jsx12("div", { className: "h-full bg-og-bg p-1", children: /* @__PURE__ */ jsx12(
8728
8347
  SandboxTerminal,
8729
8348
  {
8730
8349
  result: terminal,
8731
8350
  terminalCapability: capabilities?.Terminal ?? null,
8732
8351
  onActivate: () => requestWarmIntent("warmTerminal"),
8352
+ onReconnectNeeded: caps.renegotiate,
8733
8353
  showHeader: true,
8734
8354
  shell: capabilities?.Terminal.shell ?? void 0,
8735
8355
  liveness,
@@ -8742,13 +8362,14 @@ function useSandboxWorkspaceTabs(options) {
8742
8362
  if (browserEnabled) {
8743
8363
  list.push({
8744
8364
  id: WORKBENCH_TAB_BROWSER,
8745
- label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(Globe2Icon, {}), children: "Browser" }),
8746
- content: /* @__PURE__ */ jsx13(Suspense2, { fallback: /* @__PURE__ */ jsx13(WorkbenchSurfaceLoading, { name: "Browser" }), children: /* @__PURE__ */ jsx13(
8365
+ label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(Globe2Icon, {}), children: "Browser" }),
8366
+ content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Browser" }), children: /* @__PURE__ */ jsx12(
8747
8367
  LazyBrowserViewer,
8748
8368
  {
8749
8369
  client,
8750
8370
  workspaceId,
8751
8371
  sessionId,
8372
+ enabled: workspaceVisible,
8752
8373
  onNotify,
8753
8374
  ...desktopEnabled ? { createLinkedComputer } : {},
8754
8375
  ...onOpenComputerSession ? { onOpenComputer: onOpenComputerSession } : {},
@@ -8762,13 +8383,14 @@ function useSandboxWorkspaceTabs(options) {
8762
8383
  if (desktopEnabled) {
8763
8384
  list.push({
8764
8385
  id: WORKBENCH_TAB_DESKTOP,
8765
- label: /* @__PURE__ */ jsx13(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx13(MonitorIcon2, {}), children: "Computer" }),
8766
- content: /* @__PURE__ */ jsx13(Suspense2, { fallback: /* @__PURE__ */ jsx13(WorkbenchSurfaceLoading, { name: "Computer" }), children: /* @__PURE__ */ jsx13(
8386
+ label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(MonitorIcon, {}), children: "Computer" }),
8387
+ content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Computer" }), children: /* @__PURE__ */ jsx12(
8767
8388
  LazyComputerViewer,
8768
8389
  {
8769
8390
  client,
8770
8391
  workspaceId,
8771
8392
  sessionId,
8393
+ enabled: workspaceVisible,
8772
8394
  onNotify,
8773
8395
  requestedComputerSessionId,
8774
8396
  requestedComputerRequestId,
@@ -8799,6 +8421,8 @@ function useSandboxWorkspaceTabs(options) {
8799
8421
  onNotify,
8800
8422
  browserWebSocketFactory,
8801
8423
  computerWebSocketFactory,
8424
+ workspaceVisible,
8425
+ resolvedActiveTab,
8802
8426
  client,
8803
8427
  workspaceId,
8804
8428
  liveness,
@@ -8851,14 +8475,14 @@ function SandboxWorkspace(props) {
8851
8475
  maxSize,
8852
8476
  className
8853
8477
  } = props;
8854
- const [storedSelection, setStoredSelection] = useState20(null);
8855
- const [requestedFile, setRequestedFile] = useState20(null);
8856
- const [requestedComputer, setRequestedComputer] = useState20(null);
8857
- const nextFileRequestId = useRef20(0);
8858
- const nextComputerRequestId = useRef20(0);
8478
+ const [storedSelection, setStoredSelection] = useState17(null);
8479
+ const [requestedFile, setRequestedFile] = useState17(null);
8480
+ const [requestedComputer, setRequestedComputer] = useState17(null);
8481
+ const nextFileRequestId = useRef17(0);
8482
+ const nextComputerRequestId = useRef17(0);
8859
8483
  const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
8860
8484
  const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
8861
- const openFile = useCallback22(
8485
+ const openFile = useCallback24(
8862
8486
  (path) => {
8863
8487
  nextFileRequestId.current += 1;
8864
8488
  setRequestedFile({ sessionId, path, requestId: nextFileRequestId.current });
@@ -8866,7 +8490,7 @@ function SandboxWorkspace(props) {
8866
8490
  },
8867
8491
  [sessionId]
8868
8492
  );
8869
- const openComputer = useCallback22(
8493
+ const openComputer = useCallback24(
8870
8494
  (computerSessionId) => {
8871
8495
  nextComputerRequestId.current += 1;
8872
8496
  setRequestedComputer({
@@ -8890,6 +8514,7 @@ function SandboxWorkspace(props) {
8890
8514
  ...surfaces ? { surfaces } : {},
8891
8515
  ...initialTab ? { initialTab } : {},
8892
8516
  activeTab: activeTabHint,
8517
+ workspaceVisible: collapsed !== true,
8893
8518
  ...onNotify ? { onNotify } : {},
8894
8519
  ...browserWebSocketFactory ? { browserWebSocketFactory } : {},
8895
8520
  ...computerWebSocketFactory ? { computerWebSocketFactory } : {},
@@ -8903,11 +8528,11 @@ function SandboxWorkspace(props) {
8903
8528
  const tabs = [...leadingTabs ?? [], ...workbenchTabs, ...trailingTabs ?? []];
8904
8529
  const preferredTab = selectedTab ?? defaultTab;
8905
8530
  const activeTab = preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;
8906
- const selectTab = useCallback22(
8531
+ const selectTab = useCallback24(
8907
8532
  (tab) => setStoredSelection({ sessionId, tab }),
8908
8533
  [sessionId]
8909
8534
  );
8910
- return /* @__PURE__ */ jsx13(
8535
+ return /* @__PURE__ */ jsx12(
8911
8536
  WorkspaceDock,
8912
8537
  {
8913
8538
  primary,
@@ -8915,7 +8540,7 @@ function SandboxWorkspace(props) {
8915
8540
  ...activeTab !== void 0 ? { activeTab } : {},
8916
8541
  onActiveTabChange: selectTab,
8917
8542
  ...machine.enabled ? {
8918
- headerAccessory: /* @__PURE__ */ jsx13(MachineStateChip, { chip: machine.chip })
8543
+ headerAccessory: /* @__PURE__ */ jsx12(MachineStateChip, { chip: machine.chip })
8919
8544
  } : {},
8920
8545
  ...mobileLeadingControl !== void 0 ? { mobileLeadingControl } : {},
8921
8546
  ...collapsed !== void 0 ? { collapsed } : {},
@@ -8930,7 +8555,7 @@ function SandboxWorkspace(props) {
8930
8555
  );
8931
8556
  }
8932
8557
  function DirtyBadge({ count }) {
8933
- return /* @__PURE__ */ jsx13("span", { className: "rounded-og-xs bg-og-accent-soft px-1 text-og-xs text-og-fg-muted", children: count });
8558
+ return /* @__PURE__ */ jsx12("span", { className: "rounded-og-xs bg-og-accent-soft px-1 text-og-xs text-og-fg-muted", children: count });
8934
8559
  }
8935
8560
  function chipDotClass(state) {
8936
8561
  if (state === "live") return "bg-og-status-running";
@@ -8938,27 +8563,27 @@ function chipDotClass(state) {
8938
8563
  return "bg-og-fg-subtle";
8939
8564
  }
8940
8565
  function MachineStateChip({ chip }) {
8941
- return /* @__PURE__ */ jsxs11(
8566
+ return /* @__PURE__ */ jsxs10(
8942
8567
  "div",
8943
8568
  {
8944
8569
  role: "status",
8945
8570
  "aria-label": `Machine: ${chip.label}`,
8946
- className: "inline-flex min-h-7 items-center gap-1.5 px-2 py-1 text-og-xs font-medium text-og-fg-muted max-[1023px]:min-h-11 pointer-coarse:min-h-11",
8571
+ className: "inline-flex min-h-7 min-w-0 items-center gap-1.5 px-2 py-1 text-og-xs font-medium text-og-fg-muted max-[1023px]:min-h-11 pointer-coarse:min-h-11",
8947
8572
  children: [
8948
- /* @__PURE__ */ jsx13(
8573
+ /* @__PURE__ */ jsx12(
8949
8574
  "span",
8950
8575
  {
8951
8576
  className: cn("size-1.5 shrink-0 rounded-full", chipDotClass(chip.state)),
8952
8577
  "aria-hidden": true
8953
8578
  }
8954
8579
  ),
8955
- /* @__PURE__ */ jsx13("span", { className: "max-w-[11rem] truncate", children: chip.label })
8580
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 max-w-[11rem] truncate", children: chip.label })
8956
8581
  ]
8957
8582
  }
8958
8583
  );
8959
8584
  }
8960
8585
  function DockActionButton({ onClick, children }) {
8961
- return /* @__PURE__ */ jsx13(
8586
+ return /* @__PURE__ */ jsx12(
8962
8587
  "button",
8963
8588
  {
8964
8589
  type: "button",
@@ -8973,12 +8598,12 @@ function ChangesTabBody({
8973
8598
  onComparisonChange,
8974
8599
  ...props
8975
8600
  }) {
8976
- return /* @__PURE__ */ jsxs11("div", { className: "flex h-full min-h-0 flex-col", children: [
8977
- /* @__PURE__ */ jsx13("div", { className: "flex shrink-0 items-center gap-1 border-b border-og-border bg-og-surface-1 px-2 py-1.5", children: [
8601
+ return /* @__PURE__ */ jsxs10("div", { className: "flex h-full min-h-0 flex-col", children: [
8602
+ /* @__PURE__ */ jsx12("div", { className: "flex shrink-0 items-center gap-1 border-b border-og-border bg-og-surface-1 px-2 py-1.5", children: [
8978
8603
  ["branch", "Branch"],
8979
8604
  ["working", "Uncommitted"],
8980
8605
  ["staged", "Staged"]
8981
- ].map(([value, label]) => /* @__PURE__ */ jsx13(
8606
+ ].map(([value, label]) => /* @__PURE__ */ jsx12(
8982
8607
  "button",
8983
8608
  {
8984
8609
  type: "button",
@@ -8992,7 +8617,7 @@ function ChangesTabBody({
8992
8617
  },
8993
8618
  value
8994
8619
  )) }),
8995
- /* @__PURE__ */ jsx13("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx13(ChangesTabContent, { comparison, ...props }) })
8620
+ /* @__PURE__ */ jsx12("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx12(ChangesTabContent, { comparison, ...props }) })
8996
8621
  ] });
8997
8622
  }
8998
8623
  function ChangesTabContent({
@@ -9011,8 +8636,8 @@ function ChangesTabContent({
9011
8636
  }) {
9012
8637
  const diff = git.diff;
9013
8638
  if (diff.length > 0) {
9014
- return /* @__PURE__ */ jsxs11("div", { className: "flex h-full min-h-0 flex-col", children: [
9015
- git.error ? /* @__PURE__ */ jsxs11(
8639
+ return /* @__PURE__ */ jsxs10("div", { className: "flex h-full min-h-0 flex-col", children: [
8640
+ git.error ? /* @__PURE__ */ jsxs10(
9016
8641
  "div",
9017
8642
  {
9018
8643
  role: "status",
@@ -9020,9 +8645,9 @@ function ChangesTabContent({
9020
8645
  "data-opengeni-changes-degraded": true,
9021
8646
  className: "flex shrink-0 items-center gap-2 border-b border-og-status-running/30 bg-og-status-running/10 px-2 py-1.5 text-og-xs text-og-fg-muted",
9022
8647
  children: [
9023
- /* @__PURE__ */ jsx13(TriangleAlertIcon2, { className: "size-3.5 shrink-0 text-og-status-running", "aria-hidden": true }),
9024
- /* @__PURE__ */ jsx13("span", { className: "min-w-0 flex-1", children: git.source === "capture" ? "Live changes are temporarily unavailable. Showing the latest captured revision." : "Live refresh failed. Showing the last loaded changes." }),
9025
- /* @__PURE__ */ jsxs11(
8648
+ /* @__PURE__ */ jsx12(TriangleAlertIcon2, { className: "size-3.5 shrink-0 text-og-status-running", "aria-hidden": true }),
8649
+ /* @__PURE__ */ jsx12("span", { className: "min-w-0 flex-1", children: git.source === "capture" ? "Live changes are temporarily unavailable. Showing the latest captured revision." : "Live refresh failed. Showing the last loaded changes." }),
8650
+ /* @__PURE__ */ jsxs10(
9026
8651
  "button",
9027
8652
  {
9028
8653
  type: "button",
@@ -9030,7 +8655,7 @@ function ChangesTabContent({
9030
8655
  disabled: git.loading,
9031
8656
  className: "inline-flex min-h-7 shrink-0 items-center gap-1 rounded-og-sm border border-og-border bg-og-surface-1 px-2 font-medium text-og-fg transition-colors hover:border-og-border-strong focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-og-accent disabled:cursor-not-allowed disabled:opacity-50 pointer-coarse:min-h-11",
9032
8657
  children: [
9033
- /* @__PURE__ */ jsx13(
8658
+ /* @__PURE__ */ jsx12(
9034
8659
  RefreshCwIcon2,
9035
8660
  {
9036
8661
  className: cn("size-3", git.loading && "animate-spin motion-reduce:animate-none"),
@@ -9044,7 +8669,7 @@ function ChangesTabContent({
9044
8669
  ]
9045
8670
  }
9046
8671
  ) : null,
9047
- /* @__PURE__ */ jsx13("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx13(
8672
+ /* @__PURE__ */ jsx12("div", { className: "min-h-0 flex-1", children: /* @__PURE__ */ jsx12(
9048
8673
  WorkbenchChanges,
9049
8674
  {
9050
8675
  diff,
@@ -9057,49 +8682,49 @@ function ChangesTabContent({
9057
8682
  ] });
9058
8683
  }
9059
8684
  if (capabilitiesError && !captureAvailable) {
9060
- return /* @__PURE__ */ jsxs11(CenteredState, { icon: /* @__PURE__ */ jsx13(TriangleAlertIcon2, { className: "size-5", "aria-hidden": true }), tone: "danger", children: [
9061
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: "Sandbox unavailable" }),
9062
- /* @__PURE__ */ jsx13("p", { "data-contrast-audited": true, className: "text-og-sm leading-5 text-og-fg-muted", children: capabilitiesError.message || "Couldn't reach the sandbox for this session." }),
9063
- /* @__PURE__ */ jsxs11(DockActionButton, { onClick: onRetry, children: [
9064
- /* @__PURE__ */ jsx13(RefreshCwIcon2, { className: "size-3" }),
8685
+ return /* @__PURE__ */ jsxs10(CenteredState, { icon: /* @__PURE__ */ jsx12(TriangleAlertIcon2, { className: "size-5", "aria-hidden": true }), tone: "danger", children: [
8686
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm font-medium text-og-fg", children: "Sandbox unavailable" }),
8687
+ /* @__PURE__ */ jsx12("p", { "data-contrast-audited": true, className: "text-og-sm leading-5 text-og-fg-muted", children: capabilitiesError.message || "Couldn't reach the sandbox for this session." }),
8688
+ /* @__PURE__ */ jsxs10(DockActionButton, { onClick: onRetry, children: [
8689
+ /* @__PURE__ */ jsx12(RefreshCwIcon2, { className: "size-3" }),
9065
8690
  "Retry"
9066
8691
  ] })
9067
8692
  ] });
9068
8693
  }
9069
8694
  if ((capturePending || capabilitiesState === "negotiating") && !captureAvailable && !workspaceWaking) {
9070
- return /* @__PURE__ */ jsxs11(
8695
+ return /* @__PURE__ */ jsxs10(
9071
8696
  CenteredState,
9072
8697
  {
9073
- icon: /* @__PURE__ */ jsx13(
9074
- LoaderCircleIcon3,
8698
+ icon: /* @__PURE__ */ jsx12(
8699
+ LoaderCircleIcon2,
9075
8700
  {
9076
8701
  className: "size-5 animate-spin motion-reduce:animate-none",
9077
8702
  "aria-hidden": true
9078
8703
  }
9079
8704
  ),
9080
8705
  children: [
9081
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: "Connecting workspace" }),
9082
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: "Looking for the latest files and changes\u2026" })
8706
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm font-medium text-og-fg", children: "Connecting workspace" }),
8707
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: "Looking for the latest files and changes\u2026" })
9083
8708
  ]
9084
8709
  }
9085
8710
  );
9086
8711
  }
9087
8712
  if (!liveWorkspaceExpected && (comparison !== "working" || !captureAvailable)) {
9088
- return /* @__PURE__ */ jsxs11(
8713
+ return /* @__PURE__ */ jsxs10(
9089
8714
  CenteredState,
9090
8715
  {
9091
- icon: workspaceWaking ? /* @__PURE__ */ jsx13(
9092
- LoaderCircleIcon3,
8716
+ icon: workspaceWaking ? /* @__PURE__ */ jsx12(
8717
+ LoaderCircleIcon2,
9093
8718
  {
9094
8719
  className: "size-5 animate-spin motion-reduce:animate-none",
9095
8720
  "aria-hidden": true
9096
8721
  }
9097
- ) : /* @__PURE__ */ jsx13(CpuIcon, { className: "size-5", "aria-hidden": true }),
8722
+ ) : /* @__PURE__ */ jsx12(CpuIcon, { className: "size-5", "aria-hidden": true }),
9098
8723
  children: [
9099
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: workspaceWaking ? "Waking workspace" : "Workspace is resting" }),
9100
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: workspaceWaking ? "Connecting to the live working tree\u2026" : comparison === "branch" ? "Wake the sandbox to compare this branch with the remote default branch." : comparison === "staged" ? "Wake the sandbox to inspect staged changes." : "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes." }),
9101
- !workspaceWaking ? /* @__PURE__ */ jsxs11(DockActionButton, { onClick: onWake, children: [
9102
- /* @__PURE__ */ jsx13(CpuIcon, { className: "size-3" }),
8724
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm font-medium text-og-fg", children: workspaceWaking ? "Waking workspace" : "Workspace is resting" }),
8725
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: workspaceWaking ? "Connecting to the live working tree\u2026" : comparison === "branch" ? "Wake the sandbox to compare this branch with the remote default branch." : comparison === "staged" ? "Wake the sandbox to inspect staged changes." : "No captured revision is available yet. Wake the sandbox to inspect uncommitted changes." }),
8726
+ !workspaceWaking ? /* @__PURE__ */ jsxs10(DockActionButton, { onClick: onWake, children: [
8727
+ /* @__PURE__ */ jsx12(CpuIcon, { className: "size-3" }),
9103
8728
  "Open live workspace"
9104
8729
  ] }) : null
9105
8730
  ]
@@ -9107,26 +8732,26 @@ function ChangesTabContent({
9107
8732
  );
9108
8733
  }
9109
8734
  if (git.loading && git.source === null) {
9110
- return /* @__PURE__ */ jsxs11(
8735
+ return /* @__PURE__ */ jsxs10(
9111
8736
  CenteredState,
9112
8737
  {
9113
- icon: /* @__PURE__ */ jsx13(
9114
- LoaderCircleIcon3,
8738
+ icon: /* @__PURE__ */ jsx12(
8739
+ LoaderCircleIcon2,
9115
8740
  {
9116
8741
  className: "size-5 animate-spin motion-reduce:animate-none",
9117
8742
  "aria-hidden": true
9118
8743
  }
9119
8744
  ),
9120
8745
  children: [
9121
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: "Loading workspace" }),
9122
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: "Reading the current working tree\u2026" })
8746
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm font-medium text-og-fg", children: "Loading workspace" }),
8747
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: "Reading the current working tree\u2026" })
9123
8748
  ]
9124
8749
  }
9125
8750
  );
9126
8751
  }
9127
- return /* @__PURE__ */ jsxs11(CenteredState, { icon: /* @__PURE__ */ jsx13(CircleCheckIcon, { className: "size-5", "aria-hidden": true }), tone: "success", children: [
9128
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm font-medium text-og-fg", children: comparison === "branch" ? "No branch changes" : comparison === "staged" ? "No staged changes" : "No uncommitted changes" }),
9129
- /* @__PURE__ */ jsx13("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: comparison === "branch" ? "This branch matches the remote default branch." : comparison === "staged" ? "Stage files to review the next commit here." : "Local edits not yet committed will appear here." })
8752
+ return /* @__PURE__ */ jsxs10(CenteredState, { icon: /* @__PURE__ */ jsx12(CircleCheckIcon, { className: "size-5", "aria-hidden": true }), tone: "success", children: [
8753
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm font-medium text-og-fg", children: comparison === "branch" ? "No branch changes" : comparison === "staged" ? "No staged changes" : "No uncommitted changes" }),
8754
+ /* @__PURE__ */ jsx12("p", { className: "text-og-sm leading-5 text-og-fg-subtle", children: comparison === "branch" ? "This branch matches the remote default branch." : comparison === "staged" ? "Stage files to review the next commit here." : "Local edits not yet committed will appear here." })
9130
8755
  ] });
9131
8756
  }
9132
8757
  function CenteredState({
@@ -9134,15 +8759,15 @@ function CenteredState({
9134
8759
  icon,
9135
8760
  tone = "neutral"
9136
8761
  }) {
9137
- return /* @__PURE__ */ jsx13(
8762
+ return /* @__PURE__ */ jsx12(
9138
8763
  "div",
9139
8764
  {
9140
8765
  role: tone === "danger" ? "alert" : "status",
9141
8766
  "aria-atomic": "true",
9142
8767
  "aria-live": tone === "danger" ? "assertive" : "polite",
9143
8768
  className: "grid h-full place-items-center p-6 text-center",
9144
- children: /* @__PURE__ */ jsxs11("div", { className: "flex max-w-sm flex-col items-center gap-2.5", children: [
9145
- icon ? /* @__PURE__ */ jsx13(
8769
+ children: /* @__PURE__ */ jsxs10("div", { className: "flex max-w-sm flex-col items-center gap-2.5", children: [
8770
+ icon ? /* @__PURE__ */ jsx12(
9146
8771
  "span",
9147
8772
  {
9148
8773
  className: cn(
@@ -9161,7 +8786,7 @@ function CenteredState({
9161
8786
  }
9162
8787
 
9163
8788
  // src/hooks/use-codex-accounts.ts
9164
- import { useCallback as useCallback23, useState as useState21 } from "react";
8789
+ import { useCallback as useCallback25, useState as useState18 } from "react";
9165
8790
  function isCodexAccountEvent(event) {
9166
8791
  return event.type === "codex.account.switched";
9167
8792
  }
@@ -9182,7 +8807,7 @@ function useCodexAccounts(options = {}) {
9182
8807
  const codexClient = options.codexClient ?? client;
9183
8808
  const sessionId = options.sessionId;
9184
8809
  const sharedEvents = options.events;
9185
- const load = useCallback23(async () => {
8810
+ const load = useCallback25(async () => {
9186
8811
  const accountsP = codexClient.listCodexAccounts(workspaceId);
9187
8812
  const sessionP = sessionId && codexClient.getSession ? codexClient.getSession(workspaceId, sessionId).catch(() => null) : Promise.resolve(null);
9188
8813
  const [acc, session] = await Promise.all([accountsP, sessionP]);
@@ -9204,7 +8829,7 @@ function useCodexAccounts(options = {}) {
9204
8829
  });
9205
8830
  const { run: runMutation, mutating: pinning, mutationError } = useMutationRunner();
9206
8831
  const { run: runUsageMutation, mutating: refreshingUsage } = useMutationRunner();
9207
- const [pinningTarget, setPinningTarget] = useState21(null);
8832
+ const [pinningTarget, setPinningTarget] = useState18(null);
9208
8833
  useSessionEventTrigger(
9209
8834
  client,
9210
8835
  workspaceId,
@@ -9216,7 +8841,7 @@ function useCodexAccounts(options = {}) {
9216
8841
  ...sharedEvents !== void 0 ? { events: sharedEvents } : {}
9217
8842
  }
9218
8843
  );
9219
- const pin = useCallback23(
8844
+ const pin = useCallback25(
9220
8845
  async (target) => {
9221
8846
  if (!sessionId || !codexClient.pinSessionCodexAccount) {
9222
8847
  return false;
@@ -9232,7 +8857,7 @@ function useCodexAccounts(options = {}) {
9232
8857
  },
9233
8858
  [codexClient, workspaceId, sessionId, runMutation, refresh]
9234
8859
  );
9235
- const refreshUsage = useCallback23(async () => {
8860
+ const refreshUsage = useCallback25(async () => {
9236
8861
  if (!codexClient.refreshCodexUsage) {
9237
8862
  return false;
9238
8863
  }
@@ -9262,6 +8887,54 @@ function useCodexAccounts(options = {}) {
9262
8887
  mutationError
9263
8888
  };
9264
8889
  }
8890
+
8891
+ // src/hooks/use-supergrok-accounts.ts
8892
+ import { useCallback as useCallback26 } from "react";
8893
+ var EMPTY = {
8894
+ accounts: [],
8895
+ activeAccountId: null,
8896
+ settings: {
8897
+ rotationEnabled: false,
8898
+ rotationStrategy: "sharded",
8899
+ activeCredentialId: null
8900
+ }
8901
+ };
8902
+ function useSuperGrokAccounts(options = {}) {
8903
+ const { client, workspaceId } = useOpenGeni(options);
8904
+ const supergrokClient = options.supergrokClient ?? client;
8905
+ const load = useCallback26(
8906
+ async () => await supergrokClient.listSuperGrokAccounts(workspaceId),
8907
+ [supergrokClient, workspaceId]
8908
+ );
8909
+ const { data, loading, refresh } = usePolledValue(load, {
8910
+ enabled: options.enabled,
8911
+ pollIntervalMs: options.pollIntervalMs
8912
+ });
8913
+ const { run, mutating: activating, mutationError } = useMutationRunner();
8914
+ const activate = useCallback26(
8915
+ async (accountId) => {
8916
+ if (!supergrokClient.activateSuperGrokAccount) return false;
8917
+ const result = await run(async () => {
8918
+ await supergrokClient.activateSuperGrokAccount(workspaceId, accountId);
8919
+ return true;
8920
+ });
8921
+ if (result) await refresh();
8922
+ return result === true;
8923
+ },
8924
+ [refresh, run, supergrokClient, workspaceId]
8925
+ );
8926
+ const state = data ?? EMPTY;
8927
+ return {
8928
+ accounts: state.accounts,
8929
+ activeAccountId: state.activeAccountId,
8930
+ settings: state.settings,
8931
+ loading,
8932
+ refresh,
8933
+ activate,
8934
+ activating,
8935
+ mutationError
8936
+ };
8937
+ }
9265
8938
  export {
9266
8939
  ActivityDisclosure,
9267
8940
  ActivityRail,
@@ -9464,6 +9137,7 @@ export {
9464
9137
  unwrapMcpOutput,
9465
9138
  useAvailableModels,
9466
9139
  useBillingUsage,
9140
+ useChannels,
9467
9141
  useCodexAccounts,
9468
9142
  useComposer,
9469
9143
  useDesktopStream,
@@ -9497,6 +9171,7 @@ export {
9497
9171
  useSessionLineage,
9498
9172
  useSessionMcpApprovalPolicy,
9499
9173
  useSlashCommands,
9174
+ useSuperGrokAccounts,
9500
9175
  useTerminalStream,
9501
9176
  useTranscription,
9502
9177
  useTurnQueue,