@opengeni/react 1.0.1 → 2.2.0-canary.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 (174) hide show
  1. package/README.md +33 -10
  2. package/dist/artifacts-spreadsheet.js +1 -1
  3. package/dist/artifacts.js +1 -1
  4. package/dist/browser-viewer-GXUICIAG.js +14 -0
  5. package/dist/{chunk-R3AZOAB5.js → chunk-4MDLEHXL.js} +9 -115
  6. package/dist/chunk-4MDLEHXL.js.map +1 -0
  7. package/dist/{chunk-Y7OEDUJ5.js → chunk-5RZN3DD5.js} +1014 -420
  8. package/dist/chunk-5RZN3DD5.js.map +1 -0
  9. package/dist/chunk-5YK2ZDIG.js +116 -0
  10. package/dist/chunk-5YK2ZDIG.js.map +1 -0
  11. package/dist/chunk-H5YGYNXS.js +329 -0
  12. package/dist/chunk-H5YGYNXS.js.map +1 -0
  13. package/dist/{chunk-7WCLAEFV.js → chunk-ICA6R6TW.js} +2 -2
  14. package/dist/{chunk-7WCLAEFV.js.map → chunk-ICA6R6TW.js.map} +1 -1
  15. package/dist/{chunk-IGC6A2ZI.js → chunk-JOAVA2TW.js} +589 -217
  16. package/dist/chunk-JOAVA2TW.js.map +1 -0
  17. package/dist/{chunk-5UQPNLIO.js → chunk-LTCHJ4MS.js} +9 -9
  18. package/dist/chunk-LTCHJ4MS.js.map +1 -0
  19. package/dist/{chunk-C5ZPSSS5.js → chunk-NEXLLTP4.js} +2 -1
  20. package/dist/{chunk-C5ZPSSS5.js.map → chunk-NEXLLTP4.js.map} +1 -1
  21. package/dist/chunk-QGBJ6GHG.js +2300 -0
  22. package/dist/chunk-QGBJ6GHG.js.map +1 -0
  23. package/dist/{chunk-B6WPZSTT.js → chunk-UOLJWT5Y.js} +78 -16
  24. package/dist/chunk-UOLJWT5Y.js.map +1 -0
  25. package/dist/{chunk-MKPIAGVL.js → chunk-W6NFJXSA.js} +8 -19
  26. package/dist/chunk-W6NFJXSA.js.map +1 -0
  27. package/dist/{chunk-XBZWOM2K.js → chunk-X4A66EWZ.js} +8 -3
  28. package/dist/chunk-X4A66EWZ.js.map +1 -0
  29. package/dist/chunk-X4JAFD5R.js +3907 -0
  30. package/dist/chunk-X4JAFD5R.js.map +1 -0
  31. package/dist/{chunk-6KLX2RH5.js → chunk-XSA2QAKP.js} +334 -19
  32. package/dist/chunk-XSA2QAKP.js.map +1 -0
  33. package/dist/{chunk-HA46CYQK.js → chunk-XXWH2JLC.js} +792 -206
  34. package/dist/chunk-XXWH2JLC.js.map +1 -0
  35. package/dist/client.d.ts +2 -2
  36. package/dist/components/browser-viewer.d.ts +7 -1
  37. package/dist/components/computer-viewer.d.ts +4 -1
  38. package/dist/components/enrollment-consent.d.ts +6 -3
  39. package/dist/components/file-browser.d.ts +7 -1
  40. package/dist/components/human-input-form.d.ts +1 -1
  41. package/dist/components/human-input-surface.d.ts +1 -1
  42. package/dist/components/machine-card.d.ts +4 -1
  43. package/dist/components/machines/machine-detail.d.ts +4 -1
  44. package/dist/components/machines/machine-operation-policy.d.ts +11 -0
  45. package/dist/components/machines-dashboard.d.ts +3 -1
  46. package/dist/components/markdown.d.ts +20 -1
  47. package/dist/components/message-timeline.d.ts +1 -1
  48. package/dist/components/model-policy-picker.d.ts +2 -0
  49. package/dist/components/pierre-file.d.ts +1 -1
  50. package/dist/components/sandbox-files.d.ts +8 -2
  51. package/dist/components/sandbox-workspace.d.ts +33 -3
  52. package/dist/components/session-chrome.d.ts +15 -3
  53. package/dist/components/workspace-dock.d.ts +6 -2
  54. package/dist/composer.js +5 -5
  55. package/dist/computer-viewer-ZOP4Q7A7.js +16 -0
  56. package/dist/computer-viewer-ZOP4Q7A7.js.map +1 -0
  57. package/dist/hooks/channel-cache.d.ts +23 -0
  58. package/dist/hooks/shared-machines-list.d.ts +14 -0
  59. package/dist/hooks/use-attached-browsers.d.ts +2 -1
  60. package/dist/hooks/use-channels.d.ts +1 -0
  61. package/dist/hooks/use-composer.d.ts +37 -9
  62. package/dist/hooks/use-machine-chip.d.ts +3 -3
  63. package/dist/hooks/use-machines.d.ts +19 -1
  64. package/dist/hooks/use-sandbox-files.d.ts +1 -0
  65. package/dist/hooks/use-session-background-commands.d.ts +14 -0
  66. package/dist/hooks/use-turn-queue.d.ts +12 -0
  67. package/dist/hooks/use-workspace-sessions.d.ts +1 -0
  68. package/dist/index.d.ts +7 -5
  69. package/dist/index.js +1096 -623
  70. package/dist/index.js.map +1 -1
  71. package/dist/interaction.js +29 -181
  72. package/dist/interaction.js.map +1 -1
  73. package/dist/lib/interaction-errors.d.ts +3 -0
  74. package/dist/machines.d.ts +4 -2
  75. package/dist/machines.js +10 -2
  76. package/dist/model-policy.d.ts +1 -1
  77. package/dist/model-policy.js +1 -1
  78. package/dist/platform-activity-row-BJHUHXAT.js +169 -0
  79. package/dist/platform-activity-row-BJHUHXAT.js.map +1 -0
  80. package/dist/{queue-surface-implementation-2KVJTYXH.js → queue-surface-implementation-BSKUWHRI.js} +76 -16
  81. package/dist/queue-surface-implementation-BSKUWHRI.js.map +1 -0
  82. package/dist/realtime/realtime-control.d.ts +6 -3
  83. package/dist/realtime.js +36 -14
  84. package/dist/realtime.js.map +1 -1
  85. package/dist/session-ui.d.ts +1 -1
  86. package/dist/session-ui.js +6 -2
  87. package/dist/session.d.ts +2 -2
  88. package/dist/session.js +4 -4
  89. package/dist/timeline/index.d.ts +1 -1
  90. package/dist/timeline/platform-activity-row.d.ts +15 -0
  91. package/dist/timeline/screenshot-lightbox.d.ts +1 -1
  92. package/dist/timeline/shared.d.ts +4 -2
  93. package/dist/timeline/types.d.ts +48 -5
  94. package/dist/types/machines.d.ts +2 -2
  95. package/package.json +3 -3
  96. package/src/client.ts +12 -1
  97. package/src/components/artifacts/editable-spreadsheet.tsx +1 -1
  98. package/src/components/browser-viewer.tsx +361 -58
  99. package/src/components/chat-composer.tsx +1 -1
  100. package/src/components/composer.tsx +6 -6
  101. package/src/components/computer-viewer.tsx +368 -115
  102. package/src/components/enrollment-consent.tsx +64 -6
  103. package/src/components/file-browser.tsx +117 -2
  104. package/src/components/human-input-form.tsx +91 -55
  105. package/src/components/human-input-surface.tsx +1 -1
  106. package/src/components/machine-card.tsx +111 -0
  107. package/src/components/machine-input-display.ts +15 -0
  108. package/src/components/machines/machine-detail.tsx +47 -0
  109. package/src/components/machines/machine-operation-policy.tsx +179 -0
  110. package/src/components/machines-dashboard.tsx +6 -0
  111. package/src/components/markdown.tsx +174 -20
  112. package/src/components/message-timeline.tsx +162 -17
  113. package/src/components/model-policy-picker.tsx +9 -17
  114. package/src/components/pierre-file.tsx +1 -1
  115. package/src/components/queue-surface-implementation.tsx +82 -16
  116. package/src/components/sandbox-files.tsx +135 -19
  117. package/src/components/sandbox-workspace.tsx +204 -92
  118. package/src/components/session-chrome.tsx +415 -208
  119. package/src/components/session-status.tsx +1 -1
  120. package/src/components/user-message-body.tsx +168 -48
  121. package/src/components/workspace-dock.tsx +141 -175
  122. package/src/hooks/channel-cache.ts +104 -0
  123. package/src/hooks/shared-machines-list.ts +239 -0
  124. package/src/hooks/use-attached-browsers.ts +5 -1
  125. package/src/hooks/use-browser-frame-stream.ts +6 -1
  126. package/src/hooks/use-browser-session.ts +6 -4
  127. package/src/hooks/use-browser-sessions.ts +7 -1
  128. package/src/hooks/use-channels.ts +32 -10
  129. package/src/hooks/use-composer.ts +760 -291
  130. package/src/hooks/use-computer-frame-stream.ts +14 -9
  131. package/src/hooks/use-computer-session.ts +16 -14
  132. package/src/hooks/use-computer-sessions.ts +8 -1
  133. package/src/hooks/use-last-started-turn-policy.ts +1 -1
  134. package/src/hooks/use-machine-chip.ts +23 -6
  135. package/src/hooks/use-machines.ts +104 -6
  136. package/src/hooks/use-sandbox-files.ts +195 -140
  137. package/src/hooks/use-sandbox-git.ts +58 -29
  138. package/src/hooks/use-session-background-commands.ts +56 -0
  139. package/src/hooks/use-session-capabilities.ts +18 -0
  140. package/src/hooks/use-turn-queue.ts +111 -12
  141. package/src/hooks/use-workspace-sessions.ts +5 -1
  142. package/src/index.ts +26 -4
  143. package/src/lib/interaction-errors.ts +18 -0
  144. package/src/machines.ts +9 -1
  145. package/src/model-policy.ts +10 -2
  146. package/src/realtime/realtime-control.tsx +53 -17
  147. package/src/session-context.ts +1 -0
  148. package/src/session-ui.ts +6 -1
  149. package/src/session.ts +9 -1
  150. package/src/timeline/activity-rail.tsx +20 -75
  151. package/src/timeline/index.ts +4 -0
  152. package/src/timeline/platform-activity-row.tsx +201 -0
  153. package/src/timeline/projection.ts +476 -23
  154. package/src/timeline/screenshot-lightbox.tsx +48 -14
  155. package/src/timeline/shared.tsx +8 -2
  156. package/src/timeline/tool-renderers.tsx +104 -6
  157. package/src/timeline/turn-summary.tsx +6 -1
  158. package/src/timeline/types.ts +75 -2
  159. package/src/types/machines.ts +10 -0
  160. package/styles/compiled.css +115 -104
  161. package/dist/chunk-5UQPNLIO.js.map +0 -1
  162. package/dist/chunk-6KLX2RH5.js.map +0 -1
  163. package/dist/chunk-B6WPZSTT.js.map +0 -1
  164. package/dist/chunk-HA46CYQK.js.map +0 -1
  165. package/dist/chunk-IGC6A2ZI.js.map +0 -1
  166. package/dist/chunk-LLOLUHVG.js +0 -5937
  167. package/dist/chunk-LLOLUHVG.js.map +0 -1
  168. package/dist/chunk-MKPIAGVL.js.map +0 -1
  169. package/dist/chunk-R3AZOAB5.js.map +0 -1
  170. package/dist/chunk-XBZWOM2K.js.map +0 -1
  171. package/dist/chunk-Y7OEDUJ5.js.map +0 -1
  172. package/dist/interactive-workbench-viewers-TOHBXJEE.js +0 -15
  173. package/dist/queue-surface-implementation-2KVJTYXH.js.map +0 -1
  174. /package/dist/{interactive-workbench-viewers-TOHBXJEE.js.map → browser-viewer-GXUICIAG.js.map} +0 -0
package/dist/index.js CHANGED
@@ -24,7 +24,7 @@ import {
24
24
  useSessionLineage,
25
25
  useSessionMcpApprovalPolicy,
26
26
  useTurnQueue
27
- } from "./chunk-B6WPZSTT.js";
27
+ } from "./chunk-UOLJWT5Y.js";
28
28
  import {
29
29
  CONNECTION_STATUS_META,
30
30
  ConnectionDot,
@@ -32,6 +32,9 @@ import {
32
32
  EnrollmentConsent,
33
33
  EnrollmentDeviceFlow,
34
34
  HEALTH_TOKEN,
35
+ MACHINES_COMPOSER_POLL_MS,
36
+ MACHINES_DASHBOARD_POLL_MS,
37
+ MACHINES_SESSION_POLL_MS,
35
38
  MACHINE_STATE_BADGE_META,
36
39
  METRICS,
37
40
  METRIC_WINDOWS,
@@ -40,6 +43,7 @@ import {
40
43
  MachineDockBar,
41
44
  MachineHealthPill,
42
45
  MachineMetrics,
46
+ MachineOperationPolicyEditor,
43
47
  MachineStatusPill,
44
48
  MachinesDashboard,
45
49
  MetricHistoryChart,
@@ -52,7 +56,7 @@ import {
52
56
  healthPulses,
53
57
  pointsFor,
54
58
  useMachines
55
- } from "./chunk-HA46CYQK.js";
59
+ } from "./chunk-XXWH2JLC.js";
56
60
  import {
57
61
  Actions,
58
62
  AttachButton,
@@ -105,14 +109,14 @@ import {
105
109
  useSlashCommands,
106
110
  useTranscription,
107
111
  useVoiceInput
108
- } from "./chunk-5UQPNLIO.js";
112
+ } from "./chunk-LTCHJ4MS.js";
109
113
  import {
110
114
  FILE_ONLY_MESSAGE_TEXT,
111
115
  composeSendInput,
112
116
  shouldSteerOnKey,
113
117
  shouldSubmitOnKey,
114
118
  useComposer
115
- } from "./chunk-IGC6A2ZI.js";
119
+ } from "./chunk-JOAVA2TW.js";
116
120
  import {
117
121
  BillingClassMark,
118
122
  ModelPolicyPicker,
@@ -121,7 +125,7 @@ import {
121
125
  PickerBackHeader,
122
126
  PickerNavRow,
123
127
  defaultModelPolicyPickerMessages
124
- } from "./chunk-MKPIAGVL.js";
128
+ } from "./chunk-W6NFJXSA.js";
125
129
  import {
126
130
  advancedSourceSummary,
127
131
  availabilityReasonLabel,
@@ -139,7 +143,7 @@ import {
139
143
  projectPickerRows,
140
144
  runnableLatencyModesForModel,
141
145
  sortPickerRows
142
- } from "./chunk-XBZWOM2K.js";
146
+ } from "./chunk-X4A66EWZ.js";
143
147
  import {
144
148
  ActivityDisclosure,
145
149
  ActivityRail,
@@ -185,8 +189,13 @@ import {
185
189
  parseExecBannerSessionId,
186
190
  parseFreeformApplyPatch,
187
191
  parseToolArgs,
192
+ prefersReducedMotion,
188
193
  rawTypeOf,
189
194
  sandboxCommandExitCode,
195
+ sandboxFileLocationFromHref,
196
+ sandboxFilePathFromHref,
197
+ sessionChromeGoalPillExplanation,
198
+ sessionChromeGoalPillLabel,
190
199
  sessionChromeGoalPillState,
191
200
  stripExecBanner,
192
201
  tailPeek,
@@ -196,7 +205,7 @@ import {
196
205
  useThemeType,
197
206
  userMessageLikelyNeedsDisclosure,
198
207
  v4aToGitFileDiff
199
- } from "./chunk-Y7OEDUJ5.js";
208
+ } from "./chunk-5RZN3DD5.js";
200
209
  import {
201
210
  buildTimeline,
202
211
  creditExhaustedFromEvents,
@@ -204,7 +213,7 @@ import {
204
213
  groupTimeline,
205
214
  sessionStatusFromEvents,
206
215
  toolDisplayName
207
- } from "./chunk-6KLX2RH5.js";
216
+ } from "./chunk-XSA2QAKP.js";
208
217
  import {
209
218
  TimelineAnnotationsChip
210
219
  } from "./chunk-WSK7G5LE.js";
@@ -220,15 +229,17 @@ import {
220
229
  } from "./chunk-VZTQ73XT.js";
221
230
  import {
222
231
  DesktopViewer,
232
+ useDesktopStream,
233
+ useRelayFrameStream
234
+ } from "./chunk-4MDLEHXL.js";
235
+ import {
223
236
  STREAM_KIND_PTY,
224
237
  STREAM_ROLE_CLIENT,
225
238
  decodeStreamFrame,
226
239
  decodeStreamOpenAck,
227
240
  encodeStreamFrame,
228
- encodeStreamOpen,
229
- useDesktopStream,
230
- useRelayFrameStream
231
- } from "./chunk-R3AZOAB5.js";
241
+ encodeStreamOpen
242
+ } from "./chunk-5YK2ZDIG.js";
232
243
  import {
233
244
  useMutationRunner,
234
245
  usePageLiveActivity,
@@ -238,7 +249,7 @@ import {
238
249
  import {
239
250
  useOpenGeni,
240
251
  useOpenGeniClient
241
- } from "./chunk-C5ZPSSS5.js";
252
+ } from "./chunk-NEXLLTP4.js";
242
253
  import {
243
254
  COMPOSER_PAYMENT_REQUIRED_MESSAGE,
244
255
  CREDIT_EXHAUSTION_MESSAGE,
@@ -289,7 +300,7 @@ function policyFromTurn(turn) {
289
300
  return {
290
301
  model: turn.model,
291
302
  reasoningEffort: turn.reasoningEffort,
292
- latencyMode: turn.latencyMode ?? "standard",
303
+ latencyMode: turn.latencyMode,
293
304
  turnId: turn.id
294
305
  };
295
306
  }
@@ -361,6 +372,7 @@ function useWorkspaceSessions(options = {}) {
361
372
  const cursor = options.cursor;
362
373
  const search = options.search;
363
374
  const pinsOnly = options.pinsOnly;
375
+ const archivedOnly = options.archivedOnly;
364
376
  const enabled = options.enabled ?? true;
365
377
  const queryKey = [
366
378
  workspaceId,
@@ -368,7 +380,8 @@ function useWorkspaceSessions(options = {}) {
368
380
  parentSessionId === null ? "null" : parentSessionId ?? "",
369
381
  cursor ?? "",
370
382
  search ?? "",
371
- pinsOnly ? "1" : ""
383
+ pinsOnly ? "1" : "",
384
+ archivedOnly ? "archived" : "active"
372
385
  ].join("\0");
373
386
  const previousQueryKey = useRef(queryKey);
374
387
  const queryKeyTransition = previousQueryKey.current !== queryKey;
@@ -383,10 +396,11 @@ function useWorkspaceSessions(options = {}) {
383
396
  ...parentSessionId !== void 0 ? { parentSessionId } : {},
384
397
  ...cursor !== void 0 ? { cursor } : {},
385
398
  ...search !== void 0 ? { search } : {},
386
- ...pinsOnly ? { pinsOnly: true } : {}
399
+ ...pinsOnly ? { pinsOnly: true } : {},
400
+ ...archivedOnly ? { archivedOnly: true } : {}
387
401
  })
388
402
  }),
389
- [client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, queryKey]
403
+ [client, workspaceId, limit, parentSessionId, cursor, search, pinsOnly, archivedOnly, queryKey]
390
404
  );
391
405
  const state = usePolledValue(load, {
392
406
  pollIntervalMs: options.pollIntervalMs,
@@ -413,11 +427,48 @@ function useWorkspaceSessions(options = {}) {
413
427
  };
414
428
  }
415
429
 
430
+ // src/hooks/use-session-background-commands.ts
431
+ import { useCallback as useCallback4 } from "react";
432
+ function useSessionBackgroundCommands(sessionId, options = {}) {
433
+ const { client, workspaceId } = useOpenGeni(options);
434
+ const enabled = (options.enabled ?? true) && Boolean(sessionId);
435
+ const load = useCallback4(async () => {
436
+ if (!sessionId) return { commands: [] };
437
+ if (!client.listSessionBackgroundCommands) {
438
+ throw new Error("The configured OpenGeni client does not support background commands");
439
+ }
440
+ return await client.listSessionBackgroundCommands(workspaceId, sessionId);
441
+ }, [client, workspaceId, sessionId]);
442
+ const state = usePolledValue(load, {
443
+ enabled,
444
+ pollIntervalMs: options.pollIntervalMs
445
+ });
446
+ const refresh = state.refresh;
447
+ const cancel = useCallback4(
448
+ async (commandId) => {
449
+ if (!sessionId) return;
450
+ if (!client.cancelSessionBackgroundCommand) {
451
+ throw new Error("The configured OpenGeni client does not support background commands");
452
+ }
453
+ await client.cancelSessionBackgroundCommand(workspaceId, sessionId, commandId);
454
+ await refresh();
455
+ },
456
+ [client, workspaceId, sessionId, refresh]
457
+ );
458
+ return {
459
+ commands: state.data?.commands ?? [],
460
+ loading: state.loading,
461
+ error: state.error,
462
+ refresh,
463
+ cancel
464
+ };
465
+ }
466
+
416
467
  // src/hooks/use-environments.ts
417
- import { useCallback as useCallback4, useMemo as useMemo2 } from "react";
468
+ import { useCallback as useCallback5, useMemo as useMemo2 } from "react";
418
469
  function useVariableSets(options = {}) {
419
470
  const { client, workspaceId } = useOpenGeni(options);
420
- const load = useCallback4(
471
+ const load = useCallback5(
421
472
  async () => await client.listVariableSets(workspaceId),
422
473
  [client, workspaceId]
423
474
  );
@@ -426,7 +477,7 @@ function useVariableSets(options = {}) {
426
477
  enabled: options.enabled
427
478
  });
428
479
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
429
- const create = useCallback4(
480
+ const create = useCallback5(
430
481
  async (request) => {
431
482
  const result = await run(() => client.createVariableSet(workspaceId, request));
432
483
  if (result) {
@@ -436,7 +487,7 @@ function useVariableSets(options = {}) {
436
487
  },
437
488
  [client, workspaceId, run, refresh]
438
489
  );
439
- const update = useCallback4(
490
+ const update = useCallback5(
440
491
  async (variableSetId, request) => {
441
492
  const result = await run(() => client.updateVariableSet(workspaceId, variableSetId, request));
442
493
  if (result) {
@@ -446,7 +497,7 @@ function useVariableSets(options = {}) {
446
497
  },
447
498
  [client, workspaceId, run, refresh]
448
499
  );
449
- const remove = useCallback4(
500
+ const remove = useCallback5(
450
501
  async (variableSetId) => {
451
502
  const result = await run(async () => {
452
503
  await client.deleteVariableSet(workspaceId, variableSetId);
@@ -459,11 +510,11 @@ function useVariableSets(options = {}) {
459
510
  },
460
511
  [client, workspaceId, run, refresh]
461
512
  );
462
- const readVariable = useCallback4(
513
+ const readVariable = useCallback5(
463
514
  async (variableSetId, name) => await run(() => client.getVariableSetVariable(workspaceId, variableSetId, name)),
464
515
  [client, workspaceId, run]
465
516
  );
466
- const setVariable = useCallback4(
517
+ const setVariable = useCallback5(
467
518
  async (variableSetId, name, value) => {
468
519
  const result = await run(
469
520
  () => client.setVariableSetVariable(workspaceId, variableSetId, name, value)
@@ -475,7 +526,7 @@ function useVariableSets(options = {}) {
475
526
  },
476
527
  [client, workspaceId, run, refresh]
477
528
  );
478
- const deleteVariable = useCallback4(
529
+ const deleteVariable = useCallback5(
479
530
  async (variableSetId, name) => {
480
531
  const result = await run(async () => {
481
532
  await client.deleteVariableSetVariable(workspaceId, variableSetId, name);
@@ -527,29 +578,105 @@ function useEnvironments(options = {}) {
527
578
  }
528
579
 
529
580
  // src/hooks/use-channels.ts
530
- import { useCallback as useCallback5 } from "react";
581
+ import { useCallback as useCallback6, useMemo as useMemo3, useSyncExternalStore } from "react";
582
+
583
+ // src/hooks/channel-cache.ts
584
+ var EMPTY_CHANNELS = [];
585
+ var EMPTY_SNAPSHOT = {
586
+ channels: EMPTY_CHANNELS,
587
+ ready: false
588
+ };
589
+ var channelCaches = /* @__PURE__ */ new WeakMap();
590
+ function compareChannels(left, right) {
591
+ return Number(right.pinned) - Number(left.pinned) || left.sortOrder - right.sortOrder || left.id.localeCompare(right.id);
592
+ }
593
+ function orderedChannels(channels) {
594
+ return [...channels].sort(compareChannels);
595
+ }
596
+ function upsertChannel(channels, channel) {
597
+ const next = channels.filter((candidate) => candidate.id !== channel.id);
598
+ next.push(channel);
599
+ return orderedChannels(next);
600
+ }
601
+ function createChannelCacheStore() {
602
+ let base = null;
603
+ let snapshot = EMPTY_SNAPSHOT;
604
+ let nextLoadTicket = 0;
605
+ let acceptedLoadTicket = 0;
606
+ const pending = /* @__PURE__ */ new Map();
607
+ const listeners = /* @__PURE__ */ new Set();
608
+ const publish = () => {
609
+ let channels = base ?? EMPTY_CHANNELS;
610
+ for (const { channel } of pending.values()) {
611
+ channels = upsertChannel(channels, channel);
612
+ }
613
+ snapshot = { channels, ready: base !== null || pending.size > 0 };
614
+ for (const listener of listeners) listener();
615
+ };
616
+ return {
617
+ getSnapshot: () => snapshot,
618
+ subscribe: (listener) => {
619
+ listeners.add(listener);
620
+ return () => listeners.delete(listener);
621
+ },
622
+ beginLoad: () => ++nextLoadTicket,
623
+ acceptLoad: (ticket, channels) => {
624
+ if (ticket < acceptedLoadTicket) return;
625
+ acceptedLoadTicket = ticket;
626
+ base = orderedChannels(channels);
627
+ for (const [id, created] of pending) {
628
+ if (ticket > created.committedAfterLoad) pending.delete(id);
629
+ }
630
+ publish();
631
+ },
632
+ publishCreated: (channel) => {
633
+ pending.set(channel.id, { channel, committedAfterLoad: nextLoadTicket });
634
+ publish();
635
+ }
636
+ };
637
+ }
638
+ function channelCacheFor(client, workspaceId) {
639
+ let byWorkspace = channelCaches.get(client);
640
+ if (!byWorkspace) {
641
+ byWorkspace = /* @__PURE__ */ new Map();
642
+ channelCaches.set(client, byWorkspace);
643
+ }
644
+ let cache = byWorkspace.get(workspaceId);
645
+ if (!cache) {
646
+ cache = createChannelCacheStore();
647
+ byWorkspace.set(workspaceId, cache);
648
+ }
649
+ return cache;
650
+ }
651
+
652
+ // src/hooks/use-channels.ts
531
653
  function useChannels(options = {}) {
532
654
  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, {
655
+ const cache = useMemo3(() => channelCacheFor(client, workspaceId), [client, workspaceId]);
656
+ const snapshot = useSyncExternalStore(cache.subscribe, cache.getSnapshot, cache.getSnapshot);
657
+ const load = useCallback6(async () => {
658
+ const ticket = cache.beginLoad();
659
+ const channels = await client.listChannels(workspaceId);
660
+ cache.acceptLoad(ticket, channels);
661
+ return channels;
662
+ }, [cache, client, workspaceId]);
663
+ const { loading, error, refresh } = usePolledValue(load, {
538
664
  pollIntervalMs: options.pollIntervalMs,
539
665
  enabled: options.enabled
540
666
  });
541
- const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
542
- const create = useCallback5(
667
+ const { run, mutating, mutationError, clearMutationError } = useMutationRunner(cache);
668
+ const create = useCallback6(
543
669
  async (request) => {
544
670
  const result = await run(() => client.createChannel(workspaceId, request));
545
671
  if (result) {
672
+ cache.publishCreated(result);
546
673
  await refresh();
547
674
  }
548
675
  return result;
549
676
  },
550
- [client, workspaceId, run, refresh]
677
+ [cache, client, workspaceId, run, refresh]
551
678
  );
552
- const update = useCallback5(
679
+ const update = useCallback6(
553
680
  async (channelId, request) => {
554
681
  const result = await run(() => client.updateChannel(workspaceId, channelId, request));
555
682
  if (result) {
@@ -559,7 +686,7 @@ function useChannels(options = {}) {
559
686
  },
560
687
  [client, workspaceId, run, refresh]
561
688
  );
562
- const remove = useCallback5(
689
+ const remove = useCallback6(
563
690
  async (channelId) => {
564
691
  const result = await run(async () => {
565
692
  await client.deleteChannel(workspaceId, channelId);
@@ -572,19 +699,30 @@ function useChannels(options = {}) {
572
699
  },
573
700
  [client, workspaceId, run, refresh]
574
701
  );
575
- const moveSession = useCallback5(
702
+ const reorder = useCallback6(
703
+ async (channelIds) => {
704
+ const result = await run(() => client.reorderChannels(workspaceId, { channelIds }));
705
+ if (result) await refresh();
706
+ return result;
707
+ },
708
+ [client, workspaceId, run, refresh]
709
+ );
710
+ const moveSession = useCallback6(
576
711
  async (sessionId, channelId) => {
577
712
  return await run(() => client.updateSessionChannel(workspaceId, sessionId, { channelId }));
578
713
  },
579
714
  [client, workspaceId, run]
580
715
  );
581
716
  return {
582
- channels: data ?? [],
583
- loading,
717
+ // Cache snapshots replace arrays instead of mutating them, so preserve the
718
+ // stable reference expected from the previous polled-value implementation.
719
+ channels: snapshot.channels,
720
+ loading: loading && !snapshot.ready,
584
721
  error,
585
722
  refresh,
586
723
  create,
587
724
  update,
725
+ reorder,
588
726
  remove,
589
727
  moveSession,
590
728
  mutating,
@@ -594,16 +732,16 @@ function useChannels(options = {}) {
594
732
  }
595
733
 
596
734
  // src/hooks/use-rigs.ts
597
- import { useCallback as useCallback6 } from "react";
735
+ import { useCallback as useCallback7 } from "react";
598
736
  function useRigs(options = {}) {
599
737
  const { client, workspaceId } = useOpenGeni(options);
600
- const load = useCallback6(async () => await client.listRigs(workspaceId), [client, workspaceId]);
738
+ const load = useCallback7(async () => await client.listRigs(workspaceId), [client, workspaceId]);
601
739
  const { data, loading, error, refresh } = usePolledValue(load, {
602
740
  pollIntervalMs: options.pollIntervalMs,
603
741
  enabled: options.enabled
604
742
  });
605
743
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
606
- const create = useCallback6(
744
+ const create = useCallback7(
607
745
  async (request) => {
608
746
  const result = await run(() => client.createRig(workspaceId, request));
609
747
  if (result) {
@@ -613,7 +751,7 @@ function useRigs(options = {}) {
613
751
  },
614
752
  [client, workspaceId, run, refresh]
615
753
  );
616
- const update = useCallback6(
754
+ const update = useCallback7(
617
755
  async (rigId, request) => {
618
756
  const result = await run(() => client.updateRig(workspaceId, rigId, request));
619
757
  if (result) {
@@ -623,7 +761,7 @@ function useRigs(options = {}) {
623
761
  },
624
762
  [client, workspaceId, run, refresh]
625
763
  );
626
- const remove = useCallback6(
764
+ const remove = useCallback7(
627
765
  async (rigId) => {
628
766
  const result = await run(async () => {
629
767
  await client.deleteRig(workspaceId, rigId);
@@ -636,13 +774,13 @@ function useRigs(options = {}) {
636
774
  },
637
775
  [client, workspaceId, run, refresh]
638
776
  );
639
- const listVersions = useCallback6(
777
+ const listVersions = useCallback7(
640
778
  async (rigId) => {
641
779
  return await run(() => client.listRigVersions(workspaceId, rigId));
642
780
  },
643
781
  [client, workspaceId, run]
644
782
  );
645
- const activateVersion = useCallback6(
783
+ const activateVersion = useCallback7(
646
784
  async (rigId, versionId) => {
647
785
  const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
648
786
  if (result) {
@@ -652,13 +790,13 @@ function useRigs(options = {}) {
652
790
  },
653
791
  [client, workspaceId, run, refresh]
654
792
  );
655
- const listChanges = useCallback6(
793
+ const listChanges = useCallback7(
656
794
  async (rigId) => {
657
795
  return await run(() => client.listRigChanges(workspaceId, rigId));
658
796
  },
659
797
  [client, workspaceId, run]
660
798
  );
661
- const proposeChange = useCallback6(
799
+ const proposeChange = useCallback7(
662
800
  async (rigId, request) => {
663
801
  const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
664
802
  if (result) {
@@ -687,7 +825,7 @@ function useRigs(options = {}) {
687
825
  }
688
826
  function useRig(rigId, options = {}) {
689
827
  const { client, workspaceId } = useOpenGeni(options);
690
- const load = useCallback6(
828
+ const load = useCallback7(
691
829
  async () => await client.getRig(workspaceId, rigId),
692
830
  [client, workspaceId, rigId]
693
831
  );
@@ -696,7 +834,7 @@ function useRig(rigId, options = {}) {
696
834
  enabled: options.enabled
697
835
  });
698
836
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
699
- const update = useCallback6(
837
+ const update = useCallback7(
700
838
  async (request) => {
701
839
  const result = await run(() => client.updateRig(workspaceId, rigId, request));
702
840
  if (result) {
@@ -706,14 +844,14 @@ function useRig(rigId, options = {}) {
706
844
  },
707
845
  [client, workspaceId, rigId, run, refresh]
708
846
  );
709
- const remove = useCallback6(async () => {
847
+ const remove = useCallback7(async () => {
710
848
  const result = await run(async () => {
711
849
  await client.deleteRig(workspaceId, rigId);
712
850
  return true;
713
851
  });
714
852
  return result === true;
715
853
  }, [client, workspaceId, rigId, run]);
716
- const activateVersion = useCallback6(
854
+ const activateVersion = useCallback7(
717
855
  async (versionId) => {
718
856
  const result = await run(() => client.activateRigVersion(workspaceId, rigId, versionId));
719
857
  if (result) {
@@ -723,7 +861,7 @@ function useRig(rigId, options = {}) {
723
861
  },
724
862
  [client, workspaceId, rigId, run, refresh]
725
863
  );
726
- const proposeChange = useCallback6(
864
+ const proposeChange = useCallback7(
727
865
  async (request) => {
728
866
  const result = await run(() => client.proposeRigChange(workspaceId, rigId, request));
729
867
  if (result) {
@@ -733,7 +871,7 @@ function useRig(rigId, options = {}) {
733
871
  },
734
872
  [client, workspaceId, rigId, run, refresh]
735
873
  );
736
- const verifyChange = useCallback6(
874
+ const verifyChange = useCallback7(
737
875
  async (changeId) => {
738
876
  const result = await run(() => client.verifyRigChange(workspaceId, rigId, changeId));
739
877
  if (result) {
@@ -743,7 +881,7 @@ function useRig(rigId, options = {}) {
743
881
  },
744
882
  [client, workspaceId, rigId, run, refresh]
745
883
  );
746
- const promoteChange = useCallback6(
884
+ const promoteChange = useCallback7(
747
885
  async (changeId) => {
748
886
  const result = await run(() => client.promoteRigChange(workspaceId, rigId, changeId));
749
887
  if (result) {
@@ -753,7 +891,7 @@ function useRig(rigId, options = {}) {
753
891
  },
754
892
  [client, workspaceId, rigId, run, refresh]
755
893
  );
756
- const verify = useCallback6(async () => {
894
+ const verify = useCallback7(async () => {
757
895
  return await run(() => client.verifyRig(workspaceId, rigId));
758
896
  }, [client, workspaceId, rigId, run]);
759
897
  return {
@@ -775,7 +913,7 @@ function useRig(rigId, options = {}) {
775
913
  }
776
914
  function useRigVersions(rigId, options = {}) {
777
915
  const { client, workspaceId } = useOpenGeni(options);
778
- const load = useCallback6(
916
+ const load = useCallback7(
779
917
  async () => await client.listRigVersions(workspaceId, rigId),
780
918
  [client, workspaceId, rigId]
781
919
  );
@@ -792,7 +930,7 @@ function useRigVersions(rigId, options = {}) {
792
930
  }
793
931
  function useRigChanges(rigId, options = {}) {
794
932
  const { client, workspaceId } = useOpenGeni(options);
795
- const load = useCallback6(
933
+ const load = useCallback7(
796
934
  async () => await client.listRigChanges(workspaceId, rigId),
797
935
  [client, workspaceId, rigId]
798
936
  );
@@ -809,17 +947,17 @@ function useRigChanges(rigId, options = {}) {
809
947
  }
810
948
 
811
949
  // src/hooks/use-packs.ts
812
- import { useCallback as useCallback7 } from "react";
950
+ import { useCallback as useCallback8 } from "react";
813
951
  var EMPTY_INSTALLATIONS = [];
814
952
  function usePacks(options = {}) {
815
953
  const { client, workspaceId } = useOpenGeni(options);
816
- const load = useCallback7(async () => await client.listPacks(workspaceId), [client, workspaceId]);
954
+ const load = useCallback8(async () => await client.listPacks(workspaceId), [client, workspaceId]);
817
955
  const { data, loading, error, refresh } = usePolledValue(load, {
818
956
  pollIntervalMs: options.pollIntervalMs,
819
957
  enabled: options.enabled
820
958
  });
821
959
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
822
- const register = useCallback7(
960
+ const register = useCallback8(
823
961
  async (manifest) => {
824
962
  const result = await run(() => client.registerPack(workspaceId, manifest));
825
963
  if (result) {
@@ -829,7 +967,7 @@ function usePacks(options = {}) {
829
967
  },
830
968
  [client, workspaceId, run, refresh]
831
969
  );
832
- const enable = useCallback7(
970
+ const enable = useCallback8(
833
971
  async (packId, request = {}) => {
834
972
  const result = await run(() => client.enablePack(workspaceId, packId, request));
835
973
  if (result) {
@@ -839,11 +977,11 @@ function usePacks(options = {}) {
839
977
  },
840
978
  [client, workspaceId, run, refresh]
841
979
  );
842
- const previewInstallation = useCallback7(
980
+ const previewInstallation = useCallback8(
843
981
  async (packId, request = {}) => await run(() => client.previewPackInstallation(workspaceId, packId, request)),
844
982
  [client, workspaceId, run]
845
983
  );
846
- const install = useCallback7(
984
+ const install = useCallback8(
847
985
  async (packId, request) => {
848
986
  const result = await run(() => client.installPack(workspaceId, packId, request));
849
987
  if (result) await refresh();
@@ -851,11 +989,11 @@ function usePacks(options = {}) {
851
989
  },
852
990
  [client, workspaceId, run, refresh]
853
991
  );
854
- const previewUninstall = useCallback7(
992
+ const previewUninstall = useCallback8(
855
993
  async (packId) => await run(() => client.previewPackUninstall(workspaceId, packId)),
856
994
  [client, workspaceId, run]
857
995
  );
858
- const uninstall = useCallback7(
996
+ const uninstall = useCallback8(
859
997
  async (packId, request) => {
860
998
  const result = await run(() => client.uninstallPack(workspaceId, packId, request));
861
999
  if (result) await refresh();
@@ -863,7 +1001,7 @@ function usePacks(options = {}) {
863
1001
  },
864
1002
  [client, workspaceId, run, refresh]
865
1003
  );
866
- const remove = useCallback7(
1004
+ const remove = useCallback8(
867
1005
  async (packId) => {
868
1006
  const result = await run(async () => {
869
1007
  await client.deletePack(workspaceId, packId);
@@ -877,7 +1015,7 @@ function usePacks(options = {}) {
877
1015
  [client, workspaceId, run, refresh]
878
1016
  );
879
1017
  const installations = data?.installations ?? EMPTY_INSTALLATIONS;
880
- const installationFor = useCallback7(
1018
+ const installationFor = useCallback8(
881
1019
  (packId) => installations.find((installation) => installation.packId === packId) ?? null,
882
1020
  [installations]
883
1021
  );
@@ -902,16 +1040,16 @@ function usePacks(options = {}) {
902
1040
  }
903
1041
 
904
1042
  // src/hooks/use-workspaces.ts
905
- import { useCallback as useCallback8 } from "react";
1043
+ import { useCallback as useCallback9 } from "react";
906
1044
  function useWorkspaces(options = {}) {
907
1045
  const client = useOpenGeniClient(options);
908
- const load = useCallback8(async () => await client.listWorkspaces(), [client]);
1046
+ const load = useCallback9(async () => await client.listWorkspaces(), [client]);
909
1047
  const { data, loading, error, refresh } = usePolledValue(load, {
910
1048
  pollIntervalMs: options.pollIntervalMs,
911
1049
  enabled: options.enabled
912
1050
  });
913
1051
  const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
914
- const create = useCallback8(
1052
+ const create = useCallback9(
915
1053
  async (request) => {
916
1054
  const result = await run(() => client.createWorkspace(request));
917
1055
  if (result) {
@@ -921,7 +1059,7 @@ function useWorkspaces(options = {}) {
921
1059
  },
922
1060
  [client, run, refresh]
923
1061
  );
924
- const update = useCallback8(
1062
+ const update = useCallback9(
925
1063
  async (workspaceId, request) => {
926
1064
  const result = await run(() => client.updateWorkspace(workspaceId, request));
927
1065
  if (result) {
@@ -945,12 +1083,12 @@ function useWorkspaces(options = {}) {
945
1083
  }
946
1084
 
947
1085
  // src/hooks/use-billing-usage.ts
948
- import { useCallback as useCallback9 } from "react";
1086
+ import { useCallback as useCallback10 } from "react";
949
1087
  function useBillingUsage(options = {}) {
950
1088
  const client = useOpenGeniClient(options);
951
1089
  const accountId = options.accountId;
952
1090
  const workspaceId = options.workspaceId;
953
- const load = useCallback9(
1091
+ const load = useCallback10(
954
1092
  async () => await client.getBillingUsage({
955
1093
  ...accountId !== void 0 ? { accountId } : {},
956
1094
  ...workspaceId !== void 0 ? { workspaceId } : {}
@@ -971,10 +1109,10 @@ function useBillingUsage(options = {}) {
971
1109
  }
972
1110
 
973
1111
  // src/hooks/use-available-models.ts
974
- import { useCallback as useCallback10 } from "react";
1112
+ import { useCallback as useCallback11 } from "react";
975
1113
  function useAvailableModels(options = {}) {
976
1114
  const client = useOpenGeniClient(options);
977
- const load = useCallback10(async () => await client.getClientConfig(), [client]);
1115
+ const load = useCallback11(async () => await client.getClientConfig(), [client]);
978
1116
  const state = usePolledValue(load, {
979
1117
  pollIntervalMs: options.pollIntervalMs,
980
1118
  enabled: options.enabled
@@ -989,7 +1127,7 @@ function useAvailableModels(options = {}) {
989
1127
  }
990
1128
  function useWorkspaceModelCatalog(options) {
991
1129
  const client = useOpenGeniClient(options);
992
- const load = useCallback10(async () => {
1130
+ const load = useCallback11(async () => {
993
1131
  if (!options.workspaceId) {
994
1132
  return { models: [], defaultModel: null };
995
1133
  }
@@ -1022,7 +1160,7 @@ import {
1022
1160
  OpenGeniApiError,
1023
1161
  applyUrlRotation
1024
1162
  } from "@opengeni/sdk";
1025
- import { useCallback as useCallback11, useEffect as useEffect3, useRef as useRef2, useState } from "react";
1163
+ import { useCallback as useCallback12, useEffect as useEffect3, useRef as useRef2, useState } from "react";
1026
1164
 
1027
1165
  // src/lib/sandbox-liveness.ts
1028
1166
  function sandboxAcceptsLiveIo(liveness) {
@@ -1072,7 +1210,7 @@ function useSessionCapabilities(sessionId, options = {}) {
1072
1210
  const epochRef = useRef2(0);
1073
1211
  const viewerIdRef = useRef2(null);
1074
1212
  const previousIdentityRef = useRef2(identityKey);
1075
- const renegotiate = useCallback11(() => {
1213
+ const renegotiate = useCallback12(() => {
1076
1214
  setNonce((n) => n + 1);
1077
1215
  }, []);
1078
1216
  useEffect3(() => {
@@ -1158,6 +1296,11 @@ function useSessionCapabilities(sessionId, options = {}) {
1158
1296
  pollUntilWarm();
1159
1297
  }).catch((cause) => {
1160
1298
  if (!cancelled) {
1299
+ if (isSandboxOwnershipDisabledError(cause)) {
1300
+ setState("on-demand");
1301
+ setError(null);
1302
+ return;
1303
+ }
1161
1304
  setState("error");
1162
1305
  setError(cause instanceof Error ? cause : new Error(String(cause)));
1163
1306
  }
@@ -1260,6 +1403,11 @@ function useSessionCapabilities(sessionId, options = {}) {
1260
1403
  }
1261
1404
  } catch (cause) {
1262
1405
  if (cancelled) return;
1406
+ if (isSandboxOwnershipDisabledError(cause)) {
1407
+ setState("on-demand");
1408
+ setError(null);
1409
+ return;
1410
+ }
1263
1411
  if (cause instanceof OpenGeniApiError && cause.status === 403) {
1264
1412
  setState("error");
1265
1413
  setError(new Error("not permitted to view this session's sandbox"));
@@ -1324,6 +1472,9 @@ function useSessionCapabilities(sessionId, options = {}) {
1324
1472
  renegotiate
1325
1473
  };
1326
1474
  }
1475
+ function isSandboxOwnershipDisabledError(error) {
1476
+ return error instanceof OpenGeniApiError && error.status === 404 && error.message.includes("sandbox ownership is not enabled");
1477
+ }
1327
1478
 
1328
1479
  // src/hooks/use-terminal-stream.ts
1329
1480
  import {
@@ -1334,7 +1485,7 @@ import {
1334
1485
  ttydResizeFrame,
1335
1486
  TtydServerCommand
1336
1487
  } from "@opengeni/sdk";
1337
- import { useCallback as useCallback12, useEffect as useEffect4, useMemo as useMemo3, useRef as useRef3, useState as useState2 } from "react";
1488
+ import { useCallback as useCallback13, useEffect as useEffect4, useMemo as useMemo4, useRef as useRef3, useState as useState2 } from "react";
1338
1489
  var MAX_PENDING_TERMINAL_INPUT_CODE_UNITS = 64 * 1024;
1339
1490
  var TERMINAL_CONNECT_EXPIRY_SKEW_MS = 5e3;
1340
1491
  var TERMINAL_RECONNECT_INITIAL_MS = 100;
@@ -1647,7 +1798,7 @@ function useTerminalStream(options) {
1647
1798
  closeSocket(socket);
1648
1799
  };
1649
1800
  }, [transport, url, token, expiresAt, reconnectGeneration]);
1650
- const write = useCallback12((data) => {
1801
+ const write = useCallback13((data) => {
1651
1802
  const currentTransport = transportRef.current;
1652
1803
  const ws = wsRef.current;
1653
1804
  if (ws && ws.readyState === WebSocket.OPEN && socketAuthenticatedRef.current) {
@@ -1675,7 +1826,7 @@ function useTerminalStream(options) {
1675
1826
  pendingInputRef.current += data;
1676
1827
  }
1677
1828
  }, []);
1678
- const resize = useCallback12((cols, rows) => {
1829
+ const resize = useCallback13((cols, rows) => {
1679
1830
  if (cols <= 0 || rows <= 0) return;
1680
1831
  sizeRef.current = { cols, rows };
1681
1832
  const ws = wsRef.current;
@@ -1686,7 +1837,7 @@ function useTerminalStream(options) {
1686
1837
  }
1687
1838
  }
1688
1839
  }, []);
1689
- const disconnect = useCallback12(() => {
1840
+ const disconnect = useCallback13(() => {
1690
1841
  const ws = wsRef.current;
1691
1842
  wsRef.current = null;
1692
1843
  pendingInputRef.current = "";
@@ -1697,14 +1848,14 @@ function useTerminalStream(options) {
1697
1848
  if (ws) intentionalCloseRef.current.add(ws);
1698
1849
  closeSocket(ws);
1699
1850
  }, []);
1700
- return useMemo3(() => {
1851
+ return useMemo4(() => {
1701
1852
  return { connected: status === "open", status, write, resize, disconnect };
1702
1853
  }, [status, write, resize, disconnect]);
1703
1854
  }
1704
1855
 
1705
1856
  // src/hooks/use-sandbox-terminal.ts
1706
1857
  import { OpenGeniApiError as OpenGeniApiError2 } from "@opengeni/sdk";
1707
- import { useCallback as useCallback13, useEffect as useEffect5, useMemo as useMemo4, useRef as useRef4, useState as useState3 } from "react";
1858
+ import { useCallback as useCallback14, useEffect as useEffect5, useMemo as useMemo5, useRef as useRef4, useState as useState3 } from "react";
1708
1859
  function useSandboxTerminal(sessionId, options) {
1709
1860
  const { client, workspaceId } = useOpenGeni(options);
1710
1861
  const includeAgentFirehose = options.includeAgentFirehose ?? true;
@@ -1755,7 +1906,7 @@ function useSandboxTerminal(sessionId, options) {
1755
1906
  });
1756
1907
  };
1757
1908
  }, [interactive, client, workspaceId, sessionId, ptyFilter, boxWarm, reopenNonce]);
1758
- const { chunks, openPty, supportsInput } = useMemo4(() => {
1909
+ const { chunks, openPty, supportsInput } = useMemo5(() => {
1759
1910
  const out = [];
1760
1911
  const open = /* @__PURE__ */ new Map();
1761
1912
  let lastOpened = null;
@@ -1808,7 +1959,7 @@ function useSandboxTerminal(sessionId, options) {
1808
1959
  const identityMatches = stateIdentity === identityKey;
1809
1960
  const activePtyId = identityMatches ? openedPtyId ?? openPty : null;
1810
1961
  const canWrite = openedPtyId !== null || supportsInput;
1811
- const write = useMemo4(() => {
1962
+ const write = useMemo5(() => {
1812
1963
  if (!activePtyId || !canWrite || !sessionId) return null;
1813
1964
  const writeIdentity = identityKey;
1814
1965
  return (data) => {
@@ -1820,7 +1971,7 @@ function useSandboxTerminal(sessionId, options) {
1820
1971
  });
1821
1972
  };
1822
1973
  }, [client, workspaceId, sessionId, activePtyId, canWrite, openedPtyId, identityKey]);
1823
- const close = useCallback13(() => {
1974
+ const close = useCallback14(() => {
1824
1975
  if (!activePtyId || !sessionId) return;
1825
1976
  void client.terminalPtyClose(workspaceId, sessionId, { ptyId: activePtyId }).catch(() => {
1826
1977
  });
@@ -1836,7 +1987,7 @@ function useSandboxTerminal(sessionId, options) {
1836
1987
  }
1837
1988
 
1838
1989
  // src/hooks/use-sandbox-files.ts
1839
- import { useCallback as useCallback14, useEffect as useEffect6, useMemo as useMemo5, useRef as useRef5, useState as useState4 } from "react";
1990
+ import { useCallback as useCallback15, useEffect as useEffect6, useMemo as useMemo6, useRef as useRef5, useState as useState4 } from "react";
1840
1991
  var CapturedFileUnavailableError = class extends Error {
1841
1992
  constructor(path, reason) {
1842
1993
  super(
@@ -1859,12 +2010,19 @@ var FileWriteConflictError = class extends Error {
1859
2010
  code = "file_write_conflict";
1860
2011
  };
1861
2012
  function normalizeRoot(root) {
1862
- const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
2013
+ const slashNormalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "");
2014
+ if (slashNormalized === "/") return "/";
2015
+ const normalized = slashNormalized.replace(/\/+$/, "");
1863
2016
  return normalized === "." ? "" : normalized;
1864
2017
  }
1865
- function qualifyStatusPath(root, path) {
2018
+ function joinTreeRoot(root, path) {
2019
+ if (!root || path.startsWith("/")) return path;
2020
+ return root === "/" ? `/${path}` : `${root}/${path}`;
2021
+ }
2022
+ function qualifyStatusPath(root, path, treeRoot) {
1866
2023
  const normalized = normalizeRoot(root);
1867
- return normalized ? `${normalized}/${path}` : path;
2024
+ const workspaceRelative = joinTreeRoot(normalized, path);
2025
+ return joinTreeRoot(normalizeRoot(treeRoot), workspaceRelative);
1868
2026
  }
1869
2027
  function summarizeStatuses(statuses) {
1870
2028
  const repositories = statuses.filter(({ status }) => status.isRepo);
@@ -1881,6 +2039,10 @@ function parentOf(path) {
1881
2039
  const i = path.lastIndexOf("/");
1882
2040
  return i <= 0 ? "" : path.slice(0, i);
1883
2041
  }
2042
+ function parentWithinTree(path, rootPath) {
2043
+ const parent = parentOf(path);
2044
+ return parent === "" && normalizeRoot(rootPath) === "/" && path.startsWith("/") ? "/" : parent;
2045
+ }
1884
2046
  function leafOf(path) {
1885
2047
  const i = path.lastIndexOf("/");
1886
2048
  return i < 0 ? path : path.slice(i + 1);
@@ -1938,37 +2100,29 @@ function findNodeByPath(nodes, path) {
1938
2100
  }
1939
2101
  return void 0;
1940
2102
  }
1941
- function loadedDirectoryPaths(nodes) {
1942
- const paths = [""];
1943
- const walk = (list) => {
1944
- for (const node of list) {
1945
- if (node.kind !== "dir" || node.children === void 0) continue;
1946
- paths.push(node.path);
1947
- walk(node.children);
1948
- }
1949
- };
1950
- walk(nodes);
1951
- return paths;
1952
- }
2103
+ var FS_LIST_BATCH_REQUEST_LIMIT = 16;
1953
2104
  function initialDirectoryFrontier(rootPath, repoPaths) {
1954
- const rootParts = normalizeRoot(rootPath).split("/").filter(Boolean);
2105
+ const root = normalizeRoot(rootPath);
2106
+ const rootParts = root.split("/").filter(Boolean);
1955
2107
  const paths = /* @__PURE__ */ new Set();
1956
2108
  for (const repoPath of repoPaths) {
1957
- const parts = normalizeRoot(repoPath).split("/").filter(Boolean);
2109
+ const canonicalRepoPath = joinTreeRoot(root, normalizeRoot(repoPath));
2110
+ const parts = canonicalRepoPath.split("/").filter(Boolean);
1958
2111
  if (rootParts.some((part, index) => parts[index] !== part)) continue;
1959
2112
  for (let depth = rootParts.length + 1; depth <= parts.length; depth += 1) {
1960
- paths.add(parts.slice(0, depth).join("/"));
2113
+ const path = parts.slice(0, depth).join("/");
2114
+ paths.add(root.startsWith("/") ? `/${path}` : path);
1961
2115
  }
1962
2116
  }
1963
2117
  return [...paths].sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b)).slice(0, 8);
1964
2118
  }
1965
- function parentIsLoaded(nodes, parent) {
1966
- if (parent === "") return true;
2119
+ function parentIsLoaded(nodes, parent, rootPath = "") {
2120
+ if (parent === normalizeRoot(rootPath)) return true;
1967
2121
  const node = findNodeByPath(nodes, parent);
1968
2122
  return Boolean(node && node.kind === "dir" && node.children !== void 0);
1969
2123
  }
1970
- function insertNode(nodes, parent, child) {
1971
- if (parent === "") {
2124
+ function insertNode(nodes, parent, child, rootPath = "") {
2125
+ if (parent === normalizeRoot(rootPath)) {
1972
2126
  if (nodes.some((n) => n.path === child.path)) return nodes;
1973
2127
  return sortNodes([...nodes, child]);
1974
2128
  }
@@ -1979,14 +2133,14 @@ function insertNode(nodes, parent, child) {
1979
2133
  return { ...node, children: sortNodes([...node.children, child]) };
1980
2134
  }
1981
2135
  if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
1982
- return { ...node, children: insertNode(node.children, parent, child) };
2136
+ return { ...node, children: insertNode(node.children, parent, child, rootPath) };
1983
2137
  }
1984
2138
  return node;
1985
2139
  });
1986
2140
  }
1987
- function removeNode(nodes, path) {
1988
- const parent = parentOf(path);
1989
- if (parent === "") {
2141
+ function removeNode(nodes, path, rootPath = "") {
2142
+ const parent = parentWithinTree(path, rootPath);
2143
+ if (parent === normalizeRoot(rootPath)) {
1990
2144
  if (!nodes.some((n) => n.path === path)) return nodes;
1991
2145
  return nodes.filter((n) => n.path !== path);
1992
2146
  }
@@ -1999,7 +2153,7 @@ function removeNode(nodes, path) {
1999
2153
  };
2000
2154
  }
2001
2155
  if (node.kind === "dir" && node.children && parent.startsWith(`${node.path}/`)) {
2002
- return { ...node, children: removeNode(node.children, path) };
2156
+ return { ...node, children: removeNode(node.children, path, rootPath) };
2003
2157
  }
2004
2158
  return node;
2005
2159
  });
@@ -2070,7 +2224,7 @@ function useSandboxFiles(sessionId, options = {}) {
2070
2224
  const repoPathsKey = [
2071
2225
  ...new Set((options.repoPaths?.length ? options.repoPaths : [rootPath]).map(normalizeRoot))
2072
2226
  ].join("\0");
2073
- const repoPaths = useMemo5(() => repoPathsKey.split("\0"), [repoPathsKey]);
2227
+ const repoPaths = useMemo6(() => repoPathsKey.split("\0"), [repoPathsKey]);
2074
2228
  const capture = options.capture ?? null;
2075
2229
  const isLive2 = sandboxAcceptsLiveIo(options.liveness);
2076
2230
  const acceptsEventReads = options.liveness === void 0 || isLive2;
@@ -2098,13 +2252,13 @@ function useSandboxFiles(sessionId, options = {}) {
2098
2252
  const lastSeqRef = useRef5(0);
2099
2253
  const pendingParentsRef = useRef5(/* @__PURE__ */ new Set());
2100
2254
  const pendingGitRef = useRef5(false);
2101
- const pendingAgentToolRef = useRef5(false);
2255
+ const eventReconcileInFlightRef = useRef5(null);
2256
+ const eventReconcilePendingRef = useRef5(false);
2102
2257
  const debounceRef = useRef5(null);
2103
- const lastCommandRefreshAtRef = useRef5(0);
2104
2258
  const ownRevisionsRef = useRef5(/* @__PURE__ */ new Set());
2105
2259
  const onMutationError = options.onMutationError;
2106
2260
  treeRef.current = tree;
2107
- const applyStatus = useCallback14((nodes) => {
2261
+ const applyStatus = useCallback15((nodes) => {
2108
2262
  const overlay = statusRef.current;
2109
2263
  const walk = (list) => {
2110
2264
  let changed = false;
@@ -2127,7 +2281,7 @@ function useSandboxFiles(sessionId, options = {}) {
2127
2281
  };
2128
2282
  return walk(nodes);
2129
2283
  }, []);
2130
- const refresh = useCallback14(async () => {
2284
+ const refresh = useCallback15(async () => {
2131
2285
  if (!sessionId) return;
2132
2286
  refreshAbortRef.current?.abort();
2133
2287
  const refreshAbort = new AbortController();
@@ -2142,21 +2296,6 @@ function useSandboxFiles(sessionId, options = {}) {
2142
2296
  path,
2143
2297
  depth: 1
2144
2298
  }));
2145
- const statusPromise = repoPaths.length > 0 ? client.gitReadBatch(
2146
- workspaceId,
2147
- sessionId,
2148
- {
2149
- requests: repoPaths.map((root) => ({
2150
- status: { path: root }
2151
- }))
2152
- },
2153
- { signal: refreshAbort.signal }
2154
- ).then(
2155
- (batch) => batch.results.map((result, index) => ({
2156
- root: repoPaths[index] ?? "",
2157
- status: result.status
2158
- }))
2159
- ).catch(() => null) : Promise.resolve([]);
2160
2299
  const listedBatch = await client.fsListBatch(
2161
2300
  workspaceId,
2162
2301
  sessionId,
@@ -2183,7 +2322,21 @@ function useSandboxFiles(sessionId, options = {}) {
2183
2322
  sourceRef.current = "live";
2184
2323
  setSource("live");
2185
2324
  setLoading(false);
2186
- const statuses = await statusPromise;
2325
+ const statuses = repoPaths.length > 0 ? await client.gitReadBatch(
2326
+ workspaceId,
2327
+ sessionId,
2328
+ {
2329
+ requests: repoPaths.map((root) => ({
2330
+ status: { path: root }
2331
+ }))
2332
+ },
2333
+ { signal: refreshAbort.signal }
2334
+ ).then(
2335
+ (batch) => batch.results.map((result, index) => ({
2336
+ root: repoPaths[index] ?? "",
2337
+ status: result.status
2338
+ }))
2339
+ ).catch(() => null) : [];
2187
2340
  if (refreshGenerationRef.current !== generation) return;
2188
2341
  if (statuses) {
2189
2342
  const overlay = /* @__PURE__ */ new Map();
@@ -2191,7 +2344,7 @@ function useSandboxFiles(sessionId, options = {}) {
2191
2344
  for (const file of status.files) {
2192
2345
  const code = file.worktree ?? file.index;
2193
2346
  const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
2194
- if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
2347
+ if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
2195
2348
  }
2196
2349
  }
2197
2350
  statusRef.current = overlay;
@@ -2211,7 +2364,7 @@ function useSandboxFiles(sessionId, options = {}) {
2211
2364
  if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
2212
2365
  }
2213
2366
  }, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
2214
- const seedFromCapture = useCallback14(
2367
+ const seedFromCapture = useCallback15(
2215
2368
  (manifest) => {
2216
2369
  const overlay = /* @__PURE__ */ new Map();
2217
2370
  for (const file of manifest.files) {
@@ -2239,7 +2392,7 @@ function useSandboxFiles(sessionId, options = {}) {
2239
2392
  },
2240
2393
  [applyStatus]
2241
2394
  );
2242
- const expand = useCallback14(
2395
+ const expand = useCallback15(
2243
2396
  async (path) => {
2244
2397
  if (!sessionId) return;
2245
2398
  if (source === "capture" && capture) return;
@@ -2276,7 +2429,7 @@ function useSandboxFiles(sessionId, options = {}) {
2276
2429
  },
2277
2430
  [client, workspaceId, sessionId, capture, source, applyStatus]
2278
2431
  );
2279
- const readFile = useCallback14(
2432
+ const readFile = useCallback15(
2280
2433
  async (path, requestOptions = {}) => {
2281
2434
  if (!sessionId) throw new Error("no session");
2282
2435
  const identitySignal = identityAbortRef.current.signal;
@@ -2351,19 +2504,20 @@ function useSandboxFiles(sessionId, options = {}) {
2351
2504
  },
2352
2505
  [client, workspaceId, sessionId, capture, source]
2353
2506
  );
2354
- const reconcilePath = useCallback14(
2507
+ const reconcilePath = useCallback15(
2355
2508
  async (path, requestSignal) => {
2356
2509
  if (!sessionId) return;
2357
2510
  const identityGeneration = identityGenerationRef.current;
2358
2511
  const identitySignal = identityAbortRef.current.signal;
2359
2512
  const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
2360
- if (!parentIsLoaded(treeRef.current, path)) return;
2513
+ if (!parentIsLoaded(treeRef.current, path, rootPath)) return;
2361
2514
  try {
2362
2515
  const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
2363
2516
  if (identityGenerationRef.current !== identityGeneration) return;
2364
2517
  const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
2365
- if (path === "") setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
2366
- else
2518
+ if (path === normalizeRoot(rootPath)) {
2519
+ setTree((prev) => applyStatus(mergeRootChildren(prev, children)));
2520
+ } else
2367
2521
  setTree((prev) => {
2368
2522
  const existing = findNodeByPath(prev, path);
2369
2523
  const merged = existing?.children ? mergeChildren(existing.children, children) : children;
@@ -2372,9 +2526,50 @@ function useSandboxFiles(sessionId, options = {}) {
2372
2526
  } catch {
2373
2527
  }
2374
2528
  },
2375
- [client, workspaceId, sessionId, applyStatus]
2529
+ [client, workspaceId, sessionId, rootPath, applyStatus]
2530
+ );
2531
+ const reconcilePaths = useCallback15(
2532
+ async (requestedPaths, requestSignal) => {
2533
+ if (!sessionId) return;
2534
+ const identityGeneration = identityGenerationRef.current;
2535
+ const identitySignal = identityAbortRef.current.signal;
2536
+ const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
2537
+ const paths = [...new Set(requestedPaths)].filter(
2538
+ (path) => parentIsLoaded(treeRef.current, path, rootPath)
2539
+ );
2540
+ for (let offset = 0; offset < paths.length; offset += FS_LIST_BATCH_REQUEST_LIMIT) {
2541
+ const chunk = paths.slice(offset, offset + FS_LIST_BATCH_REQUEST_LIMIT);
2542
+ try {
2543
+ const listed = await client.fsListBatch(
2544
+ workspaceId,
2545
+ sessionId,
2546
+ { requests: chunk.map((path) => ({ path, depth: 1 })) },
2547
+ { signal }
2548
+ );
2549
+ if (identityGenerationRef.current !== identityGeneration) return;
2550
+ setTree((previous) => {
2551
+ let next = previous;
2552
+ for (const [index, path] of chunk.entries()) {
2553
+ const result = listed.results[index];
2554
+ if (!result) continue;
2555
+ const children = (result.root.children ?? []).map((node) => fsNodeToTree(node));
2556
+ if (path === normalizeRoot(rootPath)) {
2557
+ next = mergeRootChildren(next, children);
2558
+ } else {
2559
+ const existing = findNodeByPath(next, path);
2560
+ const merged = existing?.children ? mergeChildren(existing.children, children) : children;
2561
+ next = replaceChildren(next, path, merged);
2562
+ }
2563
+ }
2564
+ return applyStatus(next);
2565
+ });
2566
+ } catch {
2567
+ }
2568
+ }
2569
+ },
2570
+ [client, workspaceId, sessionId, rootPath, applyStatus]
2376
2571
  );
2377
- const runOptimistic = useCallback14(
2572
+ const runOptimistic = useCallback15(
2378
2573
  async (opName, apply, op, reconcileParents) => {
2379
2574
  const identityGeneration = identityGenerationRef.current;
2380
2575
  const snapshot = treeRef.current;
@@ -2404,12 +2599,12 @@ function useSandboxFiles(sessionId, options = {}) {
2404
2599
  },
2405
2600
  [applyStatus, reconcilePath, onMutationError]
2406
2601
  );
2407
- const writeFile = useCallback14(
2602
+ const writeFile = useCallback15(
2408
2603
  async (path, content, writeOptions = {}) => {
2409
2604
  if (!sessionId) throw new Error("no session");
2410
2605
  const identityGeneration = identityGenerationRef.current;
2411
2606
  const identitySignal = identityAbortRef.current.signal;
2412
- const parent = parentOf(path);
2607
+ const parent = parentWithinTree(path, rootPath);
2413
2608
  const exists = Boolean(findNodeByPath(tree, path));
2414
2609
  const node = {
2415
2610
  path,
@@ -2419,7 +2614,7 @@ function useSandboxFiles(sessionId, options = {}) {
2419
2614
  };
2420
2615
  return await runOptimistic(
2421
2616
  "write",
2422
- (nodes) => exists ? nodes : insertNode(nodes, parent, node),
2617
+ (nodes) => exists ? nodes : insertNode(nodes, parent, node, rootPath),
2423
2618
  async () => {
2424
2619
  if (!writeOptions.force && writeOptions.expectedContent !== void 0) {
2425
2620
  const live = await client.fsRead(
@@ -2448,12 +2643,12 @@ function useSandboxFiles(sessionId, options = {}) {
2448
2643
  exists ? [] : [parent]
2449
2644
  );
2450
2645
  },
2451
- [client, workspaceId, sessionId, tree, runOptimistic]
2646
+ [client, workspaceId, sessionId, tree, rootPath, runOptimistic]
2452
2647
  );
2453
- const createFile = useCallback14(
2648
+ const createFile = useCallback15(
2454
2649
  async (path) => {
2455
2650
  if (!sessionId) throw new Error("no session");
2456
- const parent = parentOf(path);
2651
+ const parent = parentWithinTree(path, rootPath);
2457
2652
  const node = {
2458
2653
  path,
2459
2654
  name: leafOf(path),
@@ -2462,7 +2657,7 @@ function useSandboxFiles(sessionId, options = {}) {
2462
2657
  };
2463
2658
  await runOptimistic(
2464
2659
  "create file",
2465
- (nodes) => insertNode(nodes, parent, node),
2660
+ (nodes) => insertNode(nodes, parent, node, rootPath),
2466
2661
  () => client.fsWrite(workspaceId, sessionId, {
2467
2662
  path,
2468
2663
  content: "",
@@ -2471,12 +2666,12 @@ function useSandboxFiles(sessionId, options = {}) {
2471
2666
  [parent]
2472
2667
  );
2473
2668
  },
2474
- [client, workspaceId, sessionId, runOptimistic]
2669
+ [client, workspaceId, sessionId, rootPath, runOptimistic]
2475
2670
  );
2476
- const createDir = useCallback14(
2671
+ const createDir = useCallback15(
2477
2672
  async (path) => {
2478
2673
  if (!sessionId) throw new Error("no session");
2479
- const parent = parentOf(path);
2674
+ const parent = parentWithinTree(path, rootPath);
2480
2675
  const node = {
2481
2676
  path,
2482
2677
  name: leafOf(path),
@@ -2485,37 +2680,37 @@ function useSandboxFiles(sessionId, options = {}) {
2485
2680
  };
2486
2681
  await runOptimistic(
2487
2682
  "create folder",
2488
- (nodes) => insertNode(nodes, parent, node),
2683
+ (nodes) => insertNode(nodes, parent, node, rootPath),
2489
2684
  () => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),
2490
2685
  [parent]
2491
2686
  );
2492
2687
  },
2493
- [client, workspaceId, sessionId, runOptimistic]
2688
+ [client, workspaceId, sessionId, rootPath, runOptimistic]
2494
2689
  );
2495
- const deleteEntry = useCallback14(
2690
+ const deleteEntry = useCallback15(
2496
2691
  async (path, recursive = false) => {
2497
2692
  if (!sessionId) throw new Error("no session");
2498
2693
  await runOptimistic(
2499
2694
  "delete",
2500
- (nodes) => removeNode(nodes, path),
2695
+ (nodes) => removeNode(nodes, path, rootPath),
2501
2696
  () => client.fsDelete(workspaceId, sessionId, { path, recursive }),
2502
- [parentOf(path)]
2697
+ [parentWithinTree(path, rootPath)]
2503
2698
  );
2504
2699
  },
2505
- [client, workspaceId, sessionId, runOptimistic]
2700
+ [client, workspaceId, sessionId, rootPath, runOptimistic]
2506
2701
  );
2507
- const moveEntry = useCallback14(
2702
+ const moveEntry = useCallback15(
2508
2703
  async (path, newPath, opts) => {
2509
2704
  if (!sessionId) throw new Error("no session");
2510
- const from = parentOf(path);
2511
- const to = parentOf(newPath);
2705
+ const from = parentWithinTree(path, rootPath);
2706
+ const to = parentWithinTree(newPath, rootPath);
2512
2707
  await runOptimistic(
2513
2708
  "move",
2514
2709
  (nodes) => {
2515
2710
  const moving = findNodeByPath(nodes, path);
2516
2711
  if (!moving) return nodes;
2517
2712
  const moved = repathNode(moving, path, newPath);
2518
- return insertNode(removeNode(nodes, path), to, moved);
2713
+ return insertNode(removeNode(nodes, path, rootPath), to, moved, rootPath);
2519
2714
  },
2520
2715
  () => client.fsMove(workspaceId, sessionId, {
2521
2716
  path,
@@ -2525,7 +2720,7 @@ function useSandboxFiles(sessionId, options = {}) {
2525
2720
  to === from ? [from] : [from, to]
2526
2721
  );
2527
2722
  },
2528
- [client, workspaceId, sessionId, runOptimistic]
2723
+ [client, workspaceId, sessionId, rootPath, runOptimistic]
2529
2724
  );
2530
2725
  const captureRef = useRef5(capture);
2531
2726
  captureRef.current = capture;
@@ -2540,14 +2735,14 @@ function useSandboxFiles(sessionId, options = {}) {
2540
2735
  const identityChanged = previousIdentityRef.current !== identityKey;
2541
2736
  previousIdentityRef.current = identityKey;
2542
2737
  if (identityChanged || !enabled) {
2738
+ if (identityChanged) eventReconcileInFlightRef.current = null;
2543
2739
  identityAbortRef.current.abort();
2544
2740
  identityAbortRef.current = new AbortController();
2545
2741
  identityGenerationRef.current += 1;
2546
2742
  lastSeqRef.current = 0;
2547
2743
  pendingParentsRef.current = /* @__PURE__ */ new Set();
2548
2744
  pendingGitRef.current = false;
2549
- pendingAgentToolRef.current = false;
2550
- lastCommandRefreshAtRef.current = 0;
2745
+ eventReconcilePendingRef.current = false;
2551
2746
  if (debounceRef.current) {
2552
2747
  clearTimeout(debounceRef.current);
2553
2748
  debounceRef.current = null;
@@ -2588,6 +2783,7 @@ function useSandboxFiles(sessionId, options = {}) {
2588
2783
  refreshAbortRef.current = null;
2589
2784
  refreshGenerationRef.current += 1;
2590
2785
  eventReadAbortRef.current.abort();
2786
+ eventReconcilePendingRef.current = false;
2591
2787
  };
2592
2788
  }, [
2593
2789
  enabled,
@@ -2599,7 +2795,7 @@ function useSandboxFiles(sessionId, options = {}) {
2599
2795
  refresh,
2600
2796
  seedFromCapture
2601
2797
  ]);
2602
- const refreshGitOverlay = useCallback14(
2798
+ const refreshGitOverlay = useCallback15(
2603
2799
  async (requestSignal) => {
2604
2800
  if (!sessionId) return;
2605
2801
  const identityGeneration = identityGenerationRef.current;
@@ -2623,7 +2819,7 @@ function useSandboxFiles(sessionId, options = {}) {
2623
2819
  for (const file of status.files) {
2624
2820
  const code = file.worktree ?? file.index;
2625
2821
  const mapped = code ? GIT_STATUS_TO_TREE[code] : void 0;
2626
- if (mapped) overlay.set(qualifyStatusPath(root, file.path), mapped);
2822
+ if (mapped) overlay.set(qualifyStatusPath(root, file.path, rootPath), mapped);
2627
2823
  }
2628
2824
  }
2629
2825
  statusRef.current = overlay;
@@ -2634,8 +2830,38 @@ function useSandboxFiles(sessionId, options = {}) {
2634
2830
  if (identityGenerationRef.current === identityGeneration) setGitLoading(false);
2635
2831
  }
2636
2832
  },
2637
- [client, workspaceId, sessionId, repoPaths, applyStatus]
2833
+ [client, workspaceId, sessionId, repoPaths, rootPath, applyStatus]
2638
2834
  );
2835
+ const performEventReconcile = useCallback15(async () => {
2836
+ const parents = pendingParentsRef.current;
2837
+ pendingParentsRef.current = /* @__PURE__ */ new Set();
2838
+ const wantGit = pendingGitRef.current;
2839
+ pendingGitRef.current = false;
2840
+ const signal = eventReadAbortRef.current.signal;
2841
+ const paths = [...parents];
2842
+ await reconcilePaths(paths, signal);
2843
+ if (wantGit) await refreshGitOverlay(signal);
2844
+ }, [reconcilePaths, refreshGitOverlay]);
2845
+ const performEventReconcileRef = useRef5(performEventReconcile);
2846
+ performEventReconcileRef.current = performEventReconcile;
2847
+ const reconcilePendingEvents = useCallback15(async () => {
2848
+ eventReconcilePendingRef.current = true;
2849
+ const existing = eventReconcileInFlightRef.current;
2850
+ if (existing) return await existing;
2851
+ const identityGeneration = identityGenerationRef.current;
2852
+ const run = (async () => {
2853
+ while (eventReconcilePendingRef.current && identityGenerationRef.current === identityGeneration) {
2854
+ eventReconcilePendingRef.current = false;
2855
+ await performEventReconcileRef.current();
2856
+ }
2857
+ })();
2858
+ eventReconcileInFlightRef.current = run;
2859
+ try {
2860
+ await run;
2861
+ } finally {
2862
+ if (eventReconcileInFlightRef.current === run) eventReconcileInFlightRef.current = null;
2863
+ }
2864
+ }, []);
2639
2865
  const events = options.events;
2640
2866
  useEffect6(() => {
2641
2867
  if (!enabled || !events) return;
@@ -2649,12 +2875,11 @@ function useSandboxFiles(sessionId, options = {}) {
2649
2875
  }
2650
2876
  pendingParentsRef.current = /* @__PURE__ */ new Set();
2651
2877
  pendingGitRef.current = false;
2652
- pendingAgentToolRef.current = false;
2878
+ eventReconcilePendingRef.current = false;
2653
2879
  return;
2654
2880
  }
2655
2881
  let sawNew = false;
2656
2882
  let contentChanged = false;
2657
- let sawCommandDelta = false;
2658
2883
  for (const event of events) {
2659
2884
  if (event.sequence <= lastSeqRef.current) continue;
2660
2885
  if (event.type === "fs.changed") {
@@ -2666,8 +2891,10 @@ function useSandboxFiles(sessionId, options = {}) {
2666
2891
  continue;
2667
2892
  contentChanged = true;
2668
2893
  for (const change of payload.changes ?? []) {
2669
- pendingParentsRef.current.add(parentOf(change.path));
2670
- if (change.oldPath) pendingParentsRef.current.add(parentOf(change.oldPath));
2894
+ pendingParentsRef.current.add(parentWithinTree(change.path, rootPath));
2895
+ if (change.oldPath) {
2896
+ pendingParentsRef.current.add(parentWithinTree(change.oldPath, rootPath));
2897
+ }
2671
2898
  }
2672
2899
  } else if (event.type === "git.changed") {
2673
2900
  sawNew = true;
@@ -2675,55 +2902,25 @@ function useSandboxFiles(sessionId, options = {}) {
2675
2902
  if (payload && typeof payload === "object" && typeof payload.revision === "number" && ownRevisionsRef.current.has(payload.revision))
2676
2903
  continue;
2677
2904
  pendingGitRef.current = true;
2678
- } else if (event.type === "agent.toolCall.output") {
2679
- sawNew = true;
2680
- contentChanged = true;
2681
- pendingAgentToolRef.current = true;
2682
- pendingGitRef.current = true;
2683
- } else if (event.type === "sandbox.command.output.delta") {
2684
- sawNew = true;
2685
- contentChanged = true;
2686
- sawCommandDelta = true;
2687
- pendingAgentToolRef.current = true;
2688
- pendingGitRef.current = true;
2689
2905
  }
2690
2906
  }
2691
2907
  for (const event of events)
2692
2908
  if (event.sequence > lastSeqRef.current) lastSeqRef.current = event.sequence;
2693
2909
  if (!sawNew) return;
2694
- if (contentChanged && !sawCommandDelta) setContentRevision((revision) => revision + 1);
2910
+ if (contentChanged) setContentRevision((revision) => revision + 1);
2695
2911
  if (debounceRef.current) clearTimeout(debounceRef.current);
2696
2912
  const identityGeneration = identityGenerationRef.current;
2697
- const delay = sawCommandDelta ? Math.max(0, 1e3 - (Date.now() - lastCommandRefreshAtRef.current)) : 150;
2698
2913
  debounceRef.current = setTimeout(() => {
2699
2914
  debounceRef.current = null;
2700
2915
  if (identityGenerationRef.current !== identityGeneration) return;
2701
2916
  if (!acceptsEventReadsRef.current) {
2702
2917
  pendingParentsRef.current = /* @__PURE__ */ new Set();
2703
2918
  pendingGitRef.current = false;
2704
- pendingAgentToolRef.current = false;
2705
2919
  return;
2706
2920
  }
2707
- if (sawCommandDelta) {
2708
- lastCommandRefreshAtRef.current = Date.now();
2709
- setContentRevision((revision) => revision + 1);
2710
- }
2711
- const parents = pendingParentsRef.current;
2712
- pendingParentsRef.current = /* @__PURE__ */ new Set();
2713
- const wantGit = pendingGitRef.current;
2714
- pendingGitRef.current = false;
2715
- const wantAgentTool = pendingAgentToolRef.current;
2716
- pendingAgentToolRef.current = false;
2717
- const eventReadSignal = eventReadAbortRef.current.signal;
2718
- void (async () => {
2719
- const paths = wantAgentTool ? loadedDirectoryPaths(treeRef.current) : [...parents];
2720
- await Promise.all([
2721
- wantGit ? refreshGitOverlay(eventReadSignal) : Promise.resolve(),
2722
- Promise.all(paths.map((path) => reconcilePath(path, eventReadSignal)))
2723
- ]);
2724
- })();
2725
- }, delay);
2726
- }, [enabled, active, acceptsEventReads, events, reconcilePath, refreshGitOverlay]);
2921
+ void reconcilePendingEvents();
2922
+ }, 150);
2923
+ }, [enabled, active, acceptsEventReads, events, rootPath, reconcilePendingEvents]);
2727
2924
  useEffect6(() => {
2728
2925
  if (active && acceptsEventReads) return;
2729
2926
  if (debounceRef.current) {
@@ -2732,13 +2929,14 @@ function useSandboxFiles(sessionId, options = {}) {
2732
2929
  }
2733
2930
  pendingParentsRef.current = /* @__PURE__ */ new Set();
2734
2931
  pendingGitRef.current = false;
2735
- pendingAgentToolRef.current = false;
2932
+ eventReconcilePendingRef.current = false;
2736
2933
  }, [active, acceptsEventReads]);
2737
2934
  useEffect6(
2738
2935
  () => () => {
2739
2936
  refreshAbortRef.current?.abort();
2740
2937
  identityAbortRef.current.abort();
2741
2938
  eventReadAbortRef.current.abort();
2939
+ eventReconcilePendingRef.current = false;
2742
2940
  identityGenerationRef.current += 1;
2743
2941
  if (debounceRef.current) clearTimeout(debounceRef.current);
2744
2942
  },
@@ -2767,7 +2965,7 @@ function useSandboxFiles(sessionId, options = {}) {
2767
2965
  }
2768
2966
 
2769
2967
  // src/hooks/use-sandbox-git.ts
2770
- import { useCallback as useCallback15, useEffect as useEffect7, useMemo as useMemo6, useRef as useRef6, useState as useState5 } from "react";
2968
+ import { useCallback as useCallback16, useEffect as useEffect7, useMemo as useMemo7, useRef as useRef6, useState as useState5 } from "react";
2771
2969
  function normalizeRoot2(root) {
2772
2970
  const normalized = root.replaceAll("\\", "/").replace(/^\.\/+/, "").replace(/\/+$/, "");
2773
2971
  return normalized === "." ? "" : normalized;
@@ -2833,7 +3031,7 @@ function useSandboxGit(sessionId, options = {}) {
2833
3031
  const repoPathsKey = uniqueRoots(
2834
3032
  options.repoPaths && options.repoPaths.length > 0 ? options.repoPaths : [repoPath]
2835
3033
  ).join("\0");
2836
- const repoPaths = useMemo6(() => repoPathsKey.split("\0"), [repoPathsKey]);
3034
+ const repoPaths = useMemo7(() => repoPathsKey.split("\0"), [repoPathsKey]);
2837
3035
  const comparison = options.comparison ?? (options.staged ? "staged" : "working");
2838
3036
  const hasAdvertisedRepoPaths = (options.repoPaths?.length ?? 0) > 0;
2839
3037
  const capture = comparison === "staged" ? null : options.capture ?? null;
@@ -2856,11 +3054,13 @@ function useSandboxGit(sessionId, options = {}) {
2856
3054
  const [stateIdentity, setStateIdentity] = useState5(identityKey);
2857
3055
  const refreshGenerationRef = useRef6(0);
2858
3056
  const refreshAbortRef = useRef6(null);
3057
+ const refreshInFlightRef = useRef6(null);
3058
+ const refreshPendingRef = useRef6(false);
3059
+ const refreshWorkerEpochRef = useRef6(0);
2859
3060
  const lastChangeRef = useRef6(0);
2860
- const commandRefreshTimerRef = useRef6(null);
2861
- const refresh = useCallback15(async () => {
3061
+ const eventRefreshTimerRef = useRef6(null);
3062
+ const performRefresh = useCallback16(async () => {
2862
3063
  if (!sessionId) return;
2863
- refreshAbortRef.current?.abort();
2864
3064
  const refreshAbort = new AbortController();
2865
3065
  refreshAbortRef.current = refreshAbort;
2866
3066
  const generation = refreshGenerationRef.current += 1;
@@ -2971,7 +3171,27 @@ function useSandboxGit(sessionId, options = {}) {
2971
3171
  workspaceWide,
2972
3172
  comparison
2973
3173
  ]);
2974
- const seedFromCapture = useCallback15(
3174
+ const performRefreshRef = useRef6(performRefresh);
3175
+ performRefreshRef.current = performRefresh;
3176
+ const refresh = useCallback16(async () => {
3177
+ refreshPendingRef.current = true;
3178
+ const existing = refreshInFlightRef.current;
3179
+ if (existing) return await existing;
3180
+ const workerEpoch = refreshWorkerEpochRef.current;
3181
+ const run = (async () => {
3182
+ while (refreshPendingRef.current && refreshWorkerEpochRef.current === workerEpoch) {
3183
+ refreshPendingRef.current = false;
3184
+ await performRefreshRef.current();
3185
+ }
3186
+ })();
3187
+ refreshInFlightRef.current = run;
3188
+ try {
3189
+ await run;
3190
+ } finally {
3191
+ if (refreshInFlightRef.current === run) refreshInFlightRef.current = null;
3192
+ }
3193
+ }, []);
3194
+ const seedFromCapture = useCallback16(
2975
3195
  (manifest) => {
2976
3196
  const repositories = workspaceWide ? manifest.repos : [repoForPath(manifest, repoPath)].filter(Boolean);
2977
3197
  if (comparison === "branch" && repositories.some((repo) => repo.branchDiff === void 0)) {
@@ -3015,9 +3235,14 @@ function useSandboxGit(sessionId, options = {}) {
3015
3235
  useEffect7(() => {
3016
3236
  refreshAbortRef.current?.abort();
3017
3237
  refreshAbortRef.current = null;
3238
+ refreshPendingRef.current = false;
3018
3239
  refreshGenerationRef.current += 1;
3019
3240
  const identityChanged = previousIdentityRef.current !== identityKey;
3020
3241
  previousIdentityRef.current = identityKey;
3242
+ if (identityChanged) {
3243
+ refreshWorkerEpochRef.current += 1;
3244
+ refreshInFlightRef.current = null;
3245
+ }
3021
3246
  if (identityChanged || !enabled) {
3022
3247
  lastChangeRef.current = 0;
3023
3248
  setDiff([]);
@@ -3049,6 +3274,7 @@ function useSandboxGit(sessionId, options = {}) {
3049
3274
  return () => {
3050
3275
  refreshAbortRef.current?.abort();
3051
3276
  refreshAbortRef.current = null;
3277
+ refreshPendingRef.current = false;
3052
3278
  refreshGenerationRef.current += 1;
3053
3279
  };
3054
3280
  }, [
@@ -3064,9 +3290,9 @@ function useSandboxGit(sessionId, options = {}) {
3064
3290
  const events = options.events;
3065
3291
  useEffect7(() => {
3066
3292
  if (!enabled || !events) {
3067
- if (commandRefreshTimerRef.current) {
3068
- clearTimeout(commandRefreshTimerRef.current);
3069
- commandRefreshTimerRef.current = null;
3293
+ if (eventRefreshTimerRef.current) {
3294
+ clearTimeout(eventRefreshTimerRef.current);
3295
+ eventRefreshTimerRef.current = null;
3070
3296
  }
3071
3297
  return;
3072
3298
  }
@@ -3074,42 +3300,34 @@ function useSandboxGit(sessionId, options = {}) {
3074
3300
  for (const event of events) {
3075
3301
  if (event.sequence > lastChangeRef.current) lastChangeRef.current = event.sequence;
3076
3302
  }
3077
- if (commandRefreshTimerRef.current) {
3078
- clearTimeout(commandRefreshTimerRef.current);
3079
- commandRefreshTimerRef.current = null;
3303
+ if (eventRefreshTimerRef.current) {
3304
+ clearTimeout(eventRefreshTimerRef.current);
3305
+ eventRefreshTimerRef.current = null;
3080
3306
  }
3081
3307
  return;
3082
3308
  }
3083
3309
  let latest = lastChangeRef.current;
3084
- let immediate = false;
3085
- let commandDelta = false;
3310
+ let changed = false;
3086
3311
  for (const event of events) {
3087
3312
  if (event.sequence <= latest) continue;
3088
- if (event.type === "git.changed" || event.type === "agent.toolCall.output") {
3089
- latest = event.sequence;
3090
- immediate = true;
3091
- } else if (event.type === "sandbox.command.output.delta") {
3313
+ if (event.type === "git.changed") {
3092
3314
  latest = event.sequence;
3093
- commandDelta = true;
3315
+ changed = true;
3094
3316
  }
3095
3317
  }
3096
3318
  if (latest > lastChangeRef.current) {
3097
3319
  lastChangeRef.current = latest;
3098
- if (immediate) {
3099
- if (commandRefreshTimerRef.current) clearTimeout(commandRefreshTimerRef.current);
3100
- commandRefreshTimerRef.current = null;
3320
+ if (!changed) return;
3321
+ if (eventRefreshTimerRef.current) clearTimeout(eventRefreshTimerRef.current);
3322
+ eventRefreshTimerRef.current = setTimeout(() => {
3323
+ eventRefreshTimerRef.current = null;
3101
3324
  if (acceptsEventReadsRef.current) void refresh();
3102
- } else if (commandDelta && !commandRefreshTimerRef.current) {
3103
- commandRefreshTimerRef.current = setTimeout(() => {
3104
- commandRefreshTimerRef.current = null;
3105
- if (acceptsEventReadsRef.current) void refresh();
3106
- }, 1e3);
3107
- }
3325
+ }, 150);
3108
3326
  }
3109
3327
  }, [enabled, active, acceptsEventReads, events, refresh]);
3110
3328
  useEffect7(
3111
3329
  () => () => {
3112
- if (commandRefreshTimerRef.current) clearTimeout(commandRefreshTimerRef.current);
3330
+ if (eventRefreshTimerRef.current) clearTimeout(eventRefreshTimerRef.current);
3113
3331
  },
3114
3332
  []
3115
3333
  );
@@ -3132,7 +3350,7 @@ function useSandboxGit(sessionId, options = {}) {
3132
3350
  }
3133
3351
 
3134
3352
  // src/hooks/use-workspace-capture.ts
3135
- import { useCallback as useCallback16, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
3353
+ import { useCallback as useCallback17, useEffect as useEffect8, useRef as useRef7, useState as useState6 } from "react";
3136
3354
  var REVISION_CAPTURED = "workspace.revision.captured";
3137
3355
  var REVISION_DEGRADED = "workspace.revision.degraded";
3138
3356
  var MANIFEST_FETCH_TIMEOUT_MS = 15e3;
@@ -3182,7 +3400,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3182
3400
  const generationRef = useRef7(0);
3183
3401
  const requestAbortRef = useRef7(null);
3184
3402
  const lastSeqRef = useRef7(0);
3185
- const refresh = useCallback16(async () => {
3403
+ const refresh = useCallback17(async () => {
3186
3404
  if (!sessionId) return;
3187
3405
  requestAbortRef.current?.abort();
3188
3406
  const requestAbort = new AbortController();
@@ -3327,7 +3545,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3327
3545
  }
3328
3546
 
3329
3547
  // src/hooks/use-workspace-edit.ts
3330
- import { useCallback as useCallback17, useEffect as useEffect9, useRef as useRef8, useState as useState7 } from "react";
3548
+ import { useCallback as useCallback18, useEffect as useEffect9, useRef as useRef8, useState as useState7 } from "react";
3331
3549
  function isLive(liveness) {
3332
3550
  return sandboxAcceptsLiveIo(liveness);
3333
3551
  }
@@ -3370,7 +3588,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3370
3588
  readAbortRef.current = null;
3371
3589
  };
3372
3590
  }, [identityKey]);
3373
- const edit = useCallback17(
3591
+ const edit = useCallback18(
3374
3592
  (content) => {
3375
3593
  if (stateIdentity !== identityKey) return;
3376
3594
  if (offline) return;
@@ -3386,7 +3604,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3386
3604
  },
3387
3605
  [offline, live, onWarmRequested, stateIdentity, identityKey]
3388
3606
  );
3389
- const doFlush = useCallback17(
3607
+ const doFlush = useCallback18(
3390
3608
  async (force) => {
3391
3609
  if (stateIdentity !== identityKey) return;
3392
3610
  if (!sessionId || !path) return;
@@ -3443,10 +3661,10 @@ function useWorkspaceEdit(sessionId, options = {}) {
3443
3661
  if (flush === "flushed" || flush === "conflict" || flush === "flushing") return;
3444
3662
  void doFlush(false);
3445
3663
  }, [offline, live, buffer, flush, doFlush]);
3446
- const overwrite = useCallback17(async () => {
3664
+ const overwrite = useCallback18(async () => {
3447
3665
  await doFlush(true);
3448
3666
  }, [doFlush]);
3449
- const discard = useCallback17(() => {
3667
+ const discard = useCallback18(() => {
3450
3668
  setBuffer(null);
3451
3669
  setWantsWarm(false);
3452
3670
  setFlush("idle");
@@ -3486,7 +3704,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3486
3704
  }
3487
3705
 
3488
3706
  // src/hooks/use-machine-chip.ts
3489
- import { useMemo as useMemo7 } from "react";
3707
+ import { useMemo as useMemo8 } from "react";
3490
3708
  function formatAsOf(capturedAt, now) {
3491
3709
  const then = Date.parse(capturedAt);
3492
3710
  if (Number.isNaN(then)) return "recently";
@@ -3502,17 +3720,29 @@ function formatAsOf(capturedAt, now) {
3502
3720
  function deriveMachineChip(input) {
3503
3721
  const now = input.now ?? Date.now();
3504
3722
  const asOf = input.capturedAt ?? null;
3505
- const offlineLabel = asOf ? `Offline \u2014 as of ${formatAsOf(asOf, now)}` : "Offline";
3723
+ const managedRestingLabel = asOf ? `Sleeping \xB7 saved ${formatAsOf(asOf, now)}` : "Sleeping";
3724
+ const offlineLabel = "Offline";
3506
3725
  const selfhostedOnline = input.activeIsSelfhosted === true && input.activeMachineState != null && connectionStatusForState(input.activeMachineState) === "online";
3507
3726
  if (sandboxAcceptsLiveIo(input.liveness) || selfhostedOnline) {
3508
3727
  return { state: "live", label: "Live", asOf: null };
3509
3728
  }
3510
3729
  const selfhostedOffline = input.activeIsSelfhosted === true && input.activeMachineState === "offline";
3730
+ if (input.capabilitiesState === "error") {
3731
+ return {
3732
+ state: "offline",
3733
+ label: input.activeIsSelfhosted === true ? offlineLabel : managedRestingLabel,
3734
+ asOf
3735
+ };
3736
+ }
3511
3737
  const machineWarming = input.activeMachineState === "reconnecting" || input.activeMachineState === "enrolling";
3512
3738
  if (!selfhostedOffline && (input.capabilitiesState === "negotiating" || input.wantsWarm === true || machineWarming)) {
3513
3739
  return { state: "waking", label: "Waking\u2026", asOf };
3514
3740
  }
3515
- return { state: "offline", label: offlineLabel, asOf };
3741
+ return {
3742
+ state: "offline",
3743
+ label: input.activeIsSelfhosted === true ? offlineLabel : managedRestingLabel,
3744
+ asOf
3745
+ };
3516
3746
  }
3517
3747
  function useMachineChip(input) {
3518
3748
  const {
@@ -3524,7 +3754,7 @@ function useMachineChip(input) {
3524
3754
  capturedAt,
3525
3755
  now
3526
3756
  } = input;
3527
- return useMemo7(
3757
+ return useMemo8(
3528
3758
  () => deriveMachineChip({
3529
3759
  liveness,
3530
3760
  capabilitiesState,
@@ -3805,7 +4035,7 @@ function ChatComposer({
3805
4035
  {
3806
4036
  layout: true,
3807
4037
  transition: { duration: 0.36, ease: [0.22, 1, 0.36, 1] },
3808
- className: "inline-flex min-w-0 items-center gap-1.5",
4038
+ className: "inline-flex min-w-0 flex-1 items-center gap-1.5",
3809
4039
  children: controlsStart
3810
4040
  }
3811
4041
  ) : null
@@ -4468,11 +4698,11 @@ import {
4468
4698
  Trash2Icon
4469
4699
  } from "lucide-react";
4470
4700
  import {
4471
- useCallback as useCallback18,
4701
+ useCallback as useCallback19,
4472
4702
  useEffect as useEffect13,
4473
4703
  useId as useId2,
4474
4704
  useLayoutEffect as useLayoutEffect2,
4475
- useMemo as useMemo9,
4705
+ useMemo as useMemo10,
4476
4706
  useRef as useRef11,
4477
4707
  useState as useState10
4478
4708
  } from "react";
@@ -4480,11 +4710,11 @@ import { AlertDialog } from "radix-ui";
4480
4710
  import { VList } from "virtua";
4481
4711
 
4482
4712
  // src/lib/use-unicode-fonts.ts
4483
- import { useEffect as useEffect12, useMemo as useMemo8 } from "react";
4713
+ import { useEffect as useEffect12, useMemo as useMemo9 } from "react";
4484
4714
  var japaneseScript = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u;
4485
4715
  var arabicScript = /[\u0600-\u06ff\u0750-\u077f\u0870-\u08ff\ufb50-\ufdff\ufe70-\ufefc]/u;
4486
4716
  function useUnicodeFallbackFonts(values) {
4487
- const content = useMemo8(() => collectUnicodeContent(values), [values]);
4717
+ const content = useMemo9(() => collectUnicodeContent(values), [values]);
4488
4718
  useEffect12(() => {
4489
4719
  if (content.japanese) {
4490
4720
  void import("./noto-sans-jp-loader.generated-HFGLYBR3.js").then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese)).catch(() => void 0);
@@ -4535,21 +4765,49 @@ function itemKey(item) {
4535
4765
  return `residue:${item.path}`;
4536
4766
  }
4537
4767
  }
4768
+ function pathIsInsideDirectory(path, directory) {
4769
+ if (!path.startsWith(directory) || path === directory) return false;
4770
+ if (directory.endsWith("/") || directory.endsWith("\\")) return true;
4771
+ const boundary = path[directory.length];
4772
+ return boundary === "/" || boundary === "\\";
4773
+ }
4538
4774
  function findNode(nodes, path) {
4539
4775
  for (const node of nodes) {
4540
4776
  if (node.path === path) return node;
4541
- if (node.kind === "dir" && node.children && path.startsWith(`${node.path}/`)) {
4777
+ if (node.kind === "dir" && node.children && pathIsInsideDirectory(path, node.path)) {
4542
4778
  const hit = findNode(node.children, path);
4543
4779
  if (hit) return hit;
4544
4780
  }
4545
4781
  }
4546
4782
  return void 0;
4547
4783
  }
4784
+ function traceRevealPath(nodes, targetPath) {
4785
+ for (const node of nodes) {
4786
+ if (node.path === targetPath) {
4787
+ return {
4788
+ directories: node.kind === "dir" ? [node.path] : [],
4789
+ frontier: node.kind === "dir" && node.children === void 0 ? node.path : null
4790
+ };
4791
+ }
4792
+ if (node.kind !== "dir" || !pathIsInsideDirectory(targetPath, node.path)) continue;
4793
+ if (node.children === void 0) {
4794
+ return { directories: [node.path], frontier: node.path };
4795
+ }
4796
+ const child = traceRevealPath(node.children, targetPath);
4797
+ return {
4798
+ directories: [node.path, ...child.directories],
4799
+ frontier: child.frontier
4800
+ };
4801
+ }
4802
+ return { directories: [], frontier: null };
4803
+ }
4548
4804
  function FileBrowser({
4549
4805
  result,
4550
4806
  fallback,
4551
4807
  onSelectFile,
4552
4808
  selectedPath,
4809
+ revealPath,
4810
+ revealPathRequestId,
4553
4811
  renderNode,
4554
4812
  emptyState,
4555
4813
  editable = true,
@@ -4566,13 +4824,16 @@ function FileBrowser({
4566
4824
  const [pendingDelete, setPendingDelete] = useState10(null);
4567
4825
  const [busy, setBusy] = useState10(false);
4568
4826
  const [coarsePointer, setCoarsePointer] = useState10(false);
4827
+ const [revealRequest, setRevealRequest] = useState10(null);
4828
+ const handledRevealRequestRef = useRef11(null);
4829
+ const attemptedRevealPathsRef = useRef11(/* @__PURE__ */ new Set());
4569
4830
  const treeId = useId2();
4570
4831
  const container = usePortalTokenSource();
4571
4832
  const containerRef = container.currentRef;
4572
4833
  const deleteReturnFocusRef = useRef11(null);
4573
4834
  const vlistRef = useRef11(null);
4574
4835
  const portalTokenStyle = usePortalTokenStyle(container.source);
4575
- const getTreeElement = useCallback18(
4836
+ const getTreeElement = useCallback19(
4576
4837
  () => typeof document === "undefined" ? null : document.getElementById(treeId),
4577
4838
  [treeId]
4578
4839
  );
@@ -4590,8 +4851,24 @@ function FileBrowser({
4590
4851
  return () => query.removeListener(update);
4591
4852
  }
4592
4853
  }, []);
4854
+ const effectiveRevealRequestId = revealPath ? revealPathRequestId ?? revealPath : null;
4855
+ useEffect13(() => {
4856
+ if (!revealPath || effectiveRevealRequestId === null) {
4857
+ setRevealRequest(null);
4858
+ return;
4859
+ }
4860
+ const handled = handledRevealRequestRef.current;
4861
+ if (handled?.path === revealPath && handled.requestId === effectiveRevealRequestId) return;
4862
+ handledRevealRequestRef.current = {
4863
+ path: revealPath,
4864
+ requestId: effectiveRevealRequestId
4865
+ };
4866
+ attemptedRevealPathsRef.current = /* @__PURE__ */ new Set();
4867
+ setActive(null);
4868
+ setRevealRequest({ path: revealPath, requestId: effectiveRevealRequestId });
4869
+ }, [effectiveRevealRequestId, revealPath]);
4593
4870
  const cursor = active ?? selectedPath ?? null;
4594
- const expand = useCallback18(
4871
+ const expand = useCallback19(
4595
4872
  async (path) => {
4596
4873
  const node = findNode(result.tree, path);
4597
4874
  if (node && node.kind === "dir" && node.children === void 0) {
@@ -4609,14 +4886,36 @@ function FileBrowser({
4609
4886
  },
4610
4887
  [result]
4611
4888
  );
4612
- const open = useCallback18(
4889
+ const open = useCallback19(
4613
4890
  (path) => {
4614
4891
  setExpanded((prev) => new Set(prev).add(path));
4615
4892
  void expand(path);
4616
4893
  },
4617
4894
  [expand]
4618
4895
  );
4619
- const toggle = useCallback18(
4896
+ useEffect13(() => {
4897
+ if (!revealRequest) return;
4898
+ const trace = traceRevealPath(result.tree, revealRequest.path);
4899
+ if (trace.directories.length > 0) {
4900
+ setExpanded((previous) => {
4901
+ const next = new Set(previous);
4902
+ let changed = false;
4903
+ for (const path of trace.directories) {
4904
+ if (next.has(path)) continue;
4905
+ next.add(path);
4906
+ changed = true;
4907
+ }
4908
+ return changed ? next : previous;
4909
+ });
4910
+ }
4911
+ const frontier = trace.frontier;
4912
+ if (!frontier || attemptedRevealPathsRef.current.has(frontier) || loadingPaths.has(frontier) || result.expandingPaths.has(frontier)) {
4913
+ return;
4914
+ }
4915
+ attemptedRevealPathsRef.current.add(frontier);
4916
+ void expand(frontier);
4917
+ }, [expand, loadingPaths, result.expandingPaths, result.tree, revealRequest]);
4918
+ const toggle = useCallback19(
4620
4919
  async (node) => {
4621
4920
  if (node.kind !== "dir") return;
4622
4921
  const isOpen = expanded.has(node.path);
@@ -4631,7 +4930,7 @@ function FileBrowser({
4631
4930
  [expanded, expand]
4632
4931
  );
4633
4932
  const cold = result.source !== "live";
4634
- const renderItems = useMemo9(() => {
4933
+ const renderItems = useMemo10(() => {
4635
4934
  const items = [];
4636
4935
  if (draftCreate && draftCreate.parent === "") {
4637
4936
  items.push({ type: "create", parent: "", kind: draftCreate.kind, depth: 0 });
@@ -4662,22 +4961,22 @@ function FileBrowser({
4662
4961
  walk(result.tree, 0);
4663
4962
  return items;
4664
4963
  }, [result.tree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);
4665
- const unicodeFontPaths = useMemo9(
4964
+ const unicodeFontPaths = useMemo10(
4666
4965
  () => renderItems.flatMap((item) => item.type === "node" ? [item.node.path] : []),
4667
4966
  [renderItems]
4668
4967
  );
4669
4968
  useUnicodeFallbackFonts(unicodeFontPaths);
4670
- const flatRows = useMemo9(
4969
+ const flatRows = useMemo10(
4671
4970
  () => renderItems.flatMap(
4672
4971
  (item) => item.type === "node" ? [{ node: item.node, depth: item.depth }] : []
4673
4972
  ),
4674
4973
  [renderItems]
4675
4974
  );
4676
- const rowIndexByPath = useMemo9(
4975
+ const rowIndexByPath = useMemo10(
4677
4976
  () => new Map(flatRows.map((row, index) => [row.node.path, index])),
4678
4977
  [flatRows]
4679
4978
  );
4680
- const treePositionByPath = useMemo9(() => {
4979
+ const treePositionByPath = useMemo10(() => {
4681
4980
  const positions = /* @__PURE__ */ new Map();
4682
4981
  const visit = (nodes) => {
4683
4982
  nodes.forEach((node, index) => {
@@ -4691,7 +4990,7 @@ function FileBrowser({
4691
4990
  const cursorIndex = cursor ? rowIndexByPath.get(cursor) ?? -1 : -1;
4692
4991
  const activeDescendantId = cursorIndex >= 0 ? `${treeId}-item-${cursorIndex}` : void 0;
4693
4992
  const supportsMutation = editable && result.source === "live" && result.error === null && !result.loading;
4694
- const performDelete = useCallback18(
4993
+ const performDelete = useCallback19(
4695
4994
  async (node) => {
4696
4995
  if (!supportsMutation) return;
4697
4996
  const recursive = node.kind === "dir";
@@ -4705,7 +5004,7 @@ function FileBrowser({
4705
5004
  },
4706
5005
  [supportsMutation, result]
4707
5006
  );
4708
- const runDelete = useCallback18(
5007
+ const runDelete = useCallback19(
4709
5008
  async (node, returnFocus) => {
4710
5009
  if (!supportsMutation) return;
4711
5010
  setMenu(null);
@@ -4718,7 +5017,7 @@ function FileBrowser({
4718
5017
  },
4719
5018
  [supportsMutation, confirmDelete, performDelete]
4720
5019
  );
4721
- const closePendingDelete = useCallback18(
5020
+ const closePendingDelete = useCallback19(
4722
5021
  (restoreTrigger) => {
4723
5022
  const returnTarget = restoreTrigger ? deleteReturnFocusRef.current : getTreeElement();
4724
5023
  deleteReturnFocusRef.current = null;
@@ -4732,7 +5031,7 @@ function FileBrowser({
4732
5031
  },
4733
5032
  [getTreeElement]
4734
5033
  );
4735
- const commitCreate = useCallback18(
5034
+ const commitCreate = useCallback19(
4736
5035
  async (name) => {
4737
5036
  const draft = draftCreate;
4738
5037
  setDraftCreate(null);
@@ -4757,7 +5056,7 @@ function FileBrowser({
4757
5056
  },
4758
5057
  [draftCreate, supportsMutation, result, open, onSelectFile]
4759
5058
  );
4760
- const commitRename = useCallback18(
5059
+ const commitRename = useCallback19(
4761
5060
  async (name) => {
4762
5061
  const draft = draftRename;
4763
5062
  setDraftRename(null);
@@ -4780,7 +5079,7 @@ function FileBrowser({
4780
5079
  },
4781
5080
  [draftRename, supportsMutation, result, selectedPath, active, onSelectFile]
4782
5081
  );
4783
- const startCreate = useCallback18(
5082
+ const startCreate = useCallback19(
4784
5083
  (kind) => {
4785
5084
  if (!supportsMutation) return;
4786
5085
  const anchor = cursor ? findNode(result.tree, cursor) : void 0;
@@ -4792,7 +5091,7 @@ function FileBrowser({
4792
5091
  },
4793
5092
  [supportsMutation, cursor, result.tree, open]
4794
5093
  );
4795
- const startRename = useCallback18(
5094
+ const startRename = useCallback19(
4796
5095
  (node) => {
4797
5096
  if (!supportsMutation) return;
4798
5097
  setDraftCreate(null);
@@ -4801,7 +5100,7 @@ function FileBrowser({
4801
5100
  },
4802
5101
  [supportsMutation]
4803
5102
  );
4804
- const onDropOnto = useCallback18(
5103
+ const onDropOnto = useCallback19(
4805
5104
  async (targetDir, sourcePath) => {
4806
5105
  setDragOver(null);
4807
5106
  if (!supportsMutation || !sourcePath) return;
@@ -4825,7 +5124,7 @@ function FileBrowser({
4825
5124
  },
4826
5125
  [supportsMutation, result, selectedPath, active, onSelectFile]
4827
5126
  );
4828
- const onKeyDown = useCallback18(
5127
+ const onKeyDown = useCallback19(
4829
5128
  (e) => {
4830
5129
  if (draftCreate || draftRename) return;
4831
5130
  if (flatRows.length === 0) return;
@@ -5130,6 +5429,21 @@ function FileBrowser({
5130
5429
  const index = renderItems.findIndex((it) => it.type === "node" && it.node.path === cursor);
5131
5430
  if (index >= 0) vlistRef.current?.scrollToIndex(index);
5132
5431
  }, [cursor, renderItems]);
5432
+ useEffect13(() => {
5433
+ if (!revealRequest) return;
5434
+ const index = renderItems.findIndex(
5435
+ (item) => item.type === "node" && item.node.path === revealRequest.path
5436
+ );
5437
+ if (index < 0) return;
5438
+ if (usesVirtualTree) {
5439
+ vlistRef.current?.scrollToIndex(index);
5440
+ } else {
5441
+ document.getElementById(`${treeId}-item-${index}`)?.scrollIntoView?.({ block: "nearest" });
5442
+ }
5443
+ setRevealRequest(
5444
+ (current) => current?.path === revealRequest.path && current.requestId === revealRequest.requestId ? null : current
5445
+ );
5446
+ }, [renderItems, revealRequest, treeId, usesVirtualTree]);
5133
5447
  return /* @__PURE__ */ jsxs5("div", { className: cn("flex min-h-0 min-w-0 flex-col", className), children: [
5134
5448
  showToolbar && /* @__PURE__ */ jsxs5(
5135
5449
  "div",
@@ -5619,18 +5933,18 @@ function ContextMenu({
5619
5933
  import { ArrowUpRightIcon, FileWarningIcon, HistoryIcon } from "lucide-react";
5620
5934
  import {
5621
5935
  Component,
5622
- useCallback as useCallback20,
5936
+ useCallback as useCallback21,
5623
5937
  useEffect as useEffect15,
5624
5938
  useId as useId3,
5625
5939
  useLayoutEffect as useLayoutEffect3,
5626
- useMemo as useMemo11,
5940
+ useMemo as useMemo12,
5627
5941
  useRef as useRef13,
5628
5942
  useState as useState12
5629
5943
  } from "react";
5630
5944
  import { VList as VList2 } from "virtua";
5631
5945
 
5632
5946
  // src/hooks/use-windowed-sections.ts
5633
- import { useCallback as useCallback19, useEffect as useEffect14, useMemo as useMemo10, useRef as useRef12, useState as useState11 } from "react";
5947
+ import { useCallback as useCallback20, useEffect as useEffect14, useMemo as useMemo11, useRef as useRef12, useState as useState11 } from "react";
5634
5948
  function computeWindowRange(heights, scrollTop, viewport, overscan) {
5635
5949
  const n = heights.length;
5636
5950
  if (n === 0) return { start: 0, end: 0 };
@@ -5677,7 +5991,7 @@ function useWindowedSections(opts) {
5677
5991
  start: 0,
5678
5992
  end: Math.min(count, overscan + 1)
5679
5993
  }));
5680
- const recompute = useCallback19(() => {
5994
+ const recompute = useCallback20(() => {
5681
5995
  const el = scrollRef.current;
5682
5996
  if (!el) return;
5683
5997
  const next = computeWindowRange(heightsRef.current, el.scrollTop, el.clientHeight, overscan);
@@ -5704,7 +6018,7 @@ function useWindowedSections(opts) {
5704
6018
  if (raf && typeof cancelAnimationFrame === "function") cancelAnimationFrame(raf);
5705
6019
  };
5706
6020
  }, [recompute, count]);
5707
- const measure = useCallback19((index, height) => {
6021
+ const measure = useCallback20((index, height) => {
5708
6022
  if (height <= 0) return;
5709
6023
  setHeights((prev) => {
5710
6024
  if (Math.abs((prev[index] ?? 0) - height) < 1) return prev;
@@ -5713,7 +6027,7 @@ function useWindowedSections(opts) {
5713
6027
  return next;
5714
6028
  });
5715
6029
  }, []);
5716
- const offsets = useMemo10(() => {
6030
+ const offsets = useMemo11(() => {
5717
6031
  const out = new Array(count + 1);
5718
6032
  out[0] = 0;
5719
6033
  for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
@@ -5722,7 +6036,7 @@ function useWindowedSections(opts) {
5722
6036
  useEffect14(() => {
5723
6037
  recompute();
5724
6038
  }, [offsets, recompute]);
5725
- const scrollToIndex = useCallback19(
6039
+ const scrollToIndex = useCallback20(
5726
6040
  (index) => {
5727
6041
  const el = scrollRef.current;
5728
6042
  if (!el) return;
@@ -5931,12 +6245,12 @@ function WorkbenchChanges({
5931
6245
  const [layout, setLayout] = useState12("unified");
5932
6246
  const [activePath, setActivePath] = useState12(null);
5933
6247
  const pickerId = useId3();
5934
- const unicodeFontPaths = useMemo11(
6248
+ const unicodeFontPaths = useMemo12(
5935
6249
  () => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
5936
6250
  [diff]
5937
6251
  );
5938
6252
  useUnicodeFallbackFonts(unicodeFontPaths);
5939
- const { orderedFiles, rows, grouped } = useMemo11(() => buildRail(diff), [diff]);
6253
+ const { orderedFiles, rows, grouped } = useMemo12(() => buildRail(diff), [diff]);
5940
6254
  const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);
5941
6255
  const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;
5942
6256
  useChangesLayoutEffect(() => {
@@ -5966,9 +6280,9 @@ function WorkbenchChanges({
5966
6280
  return () => query.removeListener(update);
5967
6281
  }
5968
6282
  }, []);
5969
- const additions = useMemo11(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
5970
- const deletions = useMemo11(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
5971
- const estimateHeight = useCallback20(
6283
+ const additions = useMemo12(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
6284
+ const deletions = useMemo12(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
6285
+ const estimateHeight = useCallback21(
5972
6286
  (index) => {
5973
6287
  const file = orderedFiles[index];
5974
6288
  return file ? estimateSectionHeight(file) : HEADER_PX + LINE_PX;
@@ -5980,14 +6294,14 @@ function WorkbenchChanges({
5980
6294
  estimateHeight,
5981
6295
  overscan: OVERSCAN
5982
6296
  });
5983
- const jumpTo = useCallback20(
6297
+ const jumpTo = useCallback21(
5984
6298
  (index) => {
5985
6299
  setActivePath(orderedFiles[index]?.path ?? null);
5986
6300
  windowed.scrollToIndex(index);
5987
6301
  },
5988
6302
  [orderedFiles, windowed]
5989
6303
  );
5990
- const onPaneScroll = useCallback20(() => {
6304
+ const onPaneScroll = useCallback21(() => {
5991
6305
  const el = windowed.scrollRef.current;
5992
6306
  if (!el) {
5993
6307
  return;
@@ -6450,9 +6764,9 @@ function FileSkeleton() {
6450
6764
  // src/components/code-editor.tsx
6451
6765
  import { Loader2Icon, SaveIcon } from "lucide-react";
6452
6766
  import {
6453
- useCallback as useCallback21,
6767
+ useCallback as useCallback22,
6454
6768
  useEffect as useEffect17,
6455
- useMemo as useMemo12,
6769
+ useMemo as useMemo13,
6456
6770
  useRef as useRef14,
6457
6771
  useState as useState14
6458
6772
  } from "react";
@@ -6549,7 +6863,7 @@ function CodeEditor({
6549
6863
  setSaveError(null);
6550
6864
  setSavedTick(false);
6551
6865
  }, [path]);
6552
- const noteEdit = useCallback21(
6866
+ const noteEdit = useCallback22(
6553
6867
  (next) => {
6554
6868
  setBuffer((current) => ({ ...current, value: next }));
6555
6869
  setSavedTick(false);
@@ -6560,7 +6874,7 @@ function CodeEditor({
6560
6874
  },
6561
6875
  [baseline, onEditIntent]
6562
6876
  );
6563
- const langKey = useMemo12(() => languageForPath(path), [path]);
6877
+ const langKey = useMemo13(() => languageForPath(path), [path]);
6564
6878
  useEffect17(() => {
6565
6879
  let cancelled = false;
6566
6880
  void (async () => {
@@ -6603,7 +6917,7 @@ function CodeEditor({
6603
6917
  }, [langKey]);
6604
6918
  const saveRef = useRef14(() => {
6605
6919
  });
6606
- const save = useCallback21(async () => {
6920
+ const save = useCallback22(async () => {
6607
6921
  if (readOnly || saveInFlightRef.current) return;
6608
6922
  const snapshot = value;
6609
6923
  if (snapshot === baseline) return;
@@ -6624,7 +6938,7 @@ function CodeEditor({
6624
6938
  setSaving(false);
6625
6939
  }
6626
6940
  }, [readOnly, value, baseline, onSave, path]);
6627
- const overwrite = useCallback21(async () => {
6941
+ const overwrite = useCallback22(async () => {
6628
6942
  if (readOnly || !onOverwrite || saveInFlightRef.current) return;
6629
6943
  const snapshot = value;
6630
6944
  if (snapshot === baseline) return;
@@ -6654,14 +6968,14 @@ function CodeEditor({
6654
6968
  return () => clearTimeout(t);
6655
6969
  }, [savedTick]);
6656
6970
  const fileName = path.split("/").filter(Boolean).pop() ?? path;
6657
- const cmExtensions = useMemo12(() => {
6971
+ const cmExtensions = useMemo13(() => {
6658
6972
  if (!bundle) return [];
6659
6973
  const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
6660
6974
  if (bundle.languageExtension) exts.push(bundle.languageExtension);
6661
6975
  return exts;
6662
6976
  }, [bundle]);
6663
6977
  const Editor = bundle?.Editor;
6664
- const reload = useCallback21(() => {
6978
+ const reload = useCallback22(() => {
6665
6979
  setBuffer({ path, value: initialContents, baseline: initialContents });
6666
6980
  editIntentFiredRef.current = false;
6667
6981
  setSaveError(null);
@@ -6817,7 +7131,7 @@ function EditorSkeleton() {
6817
7131
 
6818
7132
  // src/components/sandbox-files.tsx
6819
7133
  import { FileCode2Icon, FileWarningIcon as FileWarningIcon2, LoaderCircleIcon } from "lucide-react";
6820
- import { useCallback as useCallback22, useEffect as useEffect18, useRef as useRef15, useState as useState15 } from "react";
7134
+ import { useCallback as useCallback23, useEffect as useEffect18, useRef as useRef15, useState as useState15 } from "react";
6821
7135
  import { Group, Panel, Separator } from "react-resizable-panels";
6822
7136
  import { Fragment as Fragment3, jsx as jsx10, jsxs as jsxs8 } from "react/jsx-runtime";
6823
7137
  function SandboxFiles({
@@ -6827,7 +7141,10 @@ function SandboxFiles({
6827
7141
  usePierre = true,
6828
7142
  editable = true,
6829
7143
  onEditIntent,
7144
+ initialSelectedPath,
7145
+ onSelectedPathChange,
6830
7146
  requestedPath,
7147
+ requestedLine,
6831
7148
  requestedPathRequestId,
6832
7149
  requestedPathReady = true,
6833
7150
  workspaceResting = false,
@@ -6837,10 +7154,13 @@ function SandboxFiles({
6837
7154
  themeType,
6838
7155
  className
6839
7156
  }) {
6840
- const [selected, setSelected] = useState15(null);
7157
+ const [selected, setSelected] = useState15(() => initialSelectedPath ?? null);
6841
7158
  const [editMode, setEditMode] = useState15(false);
7159
+ const [focusLine, setFocusLine] = useState15(null);
7160
+ const [treeRevealRequest, setTreeRevealRequest] = useState15(null);
6842
7161
  const [liveRequestedPath, setLiveRequestedPath] = useState15(null);
6843
7162
  const [viewReloadRevision, setViewReloadRevision] = useState15(0);
7163
+ const viewerScrollRef = useRef15(null);
6844
7164
  const pendingRequestRef = useRef15(null);
6845
7165
  const handledRequestRef = useRef15(null);
6846
7166
  const requestKey = requestedPath ? requestedPathRequestId ?? requestedPath : null;
@@ -6848,6 +7168,7 @@ function SandboxFiles({
6848
7168
  if (!requestedPath || requestKey === null) {
6849
7169
  pendingRequestRef.current = null;
6850
7170
  handledRequestRef.current = null;
7171
+ setTreeRevealRequest(null);
6851
7172
  return;
6852
7173
  }
6853
7174
  if (handledRequestRef.current === requestKey) {
@@ -6855,13 +7176,18 @@ function SandboxFiles({
6855
7176
  }
6856
7177
  if (!requestedPathReady) {
6857
7178
  pendingRequestRef.current = requestKey;
7179
+ setTreeRevealRequest(null);
6858
7180
  return;
6859
7181
  }
6860
7182
  handledRequestRef.current = requestKey;
6861
7183
  pendingRequestRef.current = null;
6862
7184
  setSelected(requestedPath);
7185
+ onSelectedPathChange?.(requestedPath);
6863
7186
  setEditMode(false);
6864
- }, [requestKey, requestedPath, requestedPathReady]);
7187
+ setFocusLine(requestedLine != null && requestedLine > 0 ? requestedLine : null);
7188
+ setTreeRevealRequest({ path: requestedPath, requestId: requestKey });
7189
+ setViewReloadRevision((revision) => revision + 1);
7190
+ }, [onSelectedPathChange, requestKey, requestedLine, requestedPath, requestedPathReady]);
6865
7191
  const rootRef = useRef15(null);
6866
7192
  const [wide, setWide] = useState15(false);
6867
7193
  useEffect18(() => {
@@ -6888,18 +7214,29 @@ function SandboxFiles({
6888
7214
  const fileView = useFileView(
6889
7215
  viewPath,
6890
7216
  files.readFile,
6891
- (files.contentRevision ?? 0) + viewReloadRevision
7217
+ `${files.contentRevision ?? 0}:${viewReloadRevision}`
6892
7218
  );
6893
- const selectFile = useCallback22((path) => {
6894
- if (pendingRequestRef.current !== null) {
6895
- handledRequestRef.current = pendingRequestRef.current;
6896
- pendingRequestRef.current = null;
7219
+ useEffect18(() => {
7220
+ if (focusLine === null && viewReloadRevision > 0 && viewerScrollRef.current) {
7221
+ viewerScrollRef.current.scrollTop = 0;
6897
7222
  }
6898
- setSelected(path);
6899
- setEditMode(false);
6900
- setLiveRequestedPath(null);
6901
- setViewReloadRevision(0);
6902
- }, []);
7223
+ }, [focusLine, viewReloadRevision]);
7224
+ const selectFile = useCallback23(
7225
+ (path) => {
7226
+ if (pendingRequestRef.current !== null) {
7227
+ handledRequestRef.current = pendingRequestRef.current;
7228
+ pendingRequestRef.current = null;
7229
+ }
7230
+ setSelected(path);
7231
+ onSelectedPathChange?.(path);
7232
+ setEditMode(false);
7233
+ setFocusLine(null);
7234
+ setTreeRevealRequest(null);
7235
+ setLiveRequestedPath(null);
7236
+ setViewReloadRevision(0);
7237
+ },
7238
+ [onSelectedPathChange]
7239
+ );
6903
7240
  const canEdit = editable && viewPath !== null && !fileView.loading && fileView.error === null && !fileView.isBinary && !fileView.truncated && fileView.content !== null;
6904
7241
  const showEditor = canEdit && editMode;
6905
7242
  const captureFileUnavailable = fileView.error instanceof CapturedFileUnavailableError ? fileView.error : null;
@@ -6970,6 +7307,10 @@ function SandboxFiles({
6970
7307
  {
6971
7308
  result: files,
6972
7309
  selectedPath: selected ?? void 0,
7310
+ ...treeRevealRequest ? {
7311
+ revealPath: treeRevealRequest.path,
7312
+ revealPathRequestId: treeRevealRequest.requestId
7313
+ } : {},
6973
7314
  onSelectFile: selectFile,
6974
7315
  editable,
6975
7316
  emptyState: "This directory is empty",
@@ -7003,7 +7344,7 @@ function SandboxFiles({
7003
7344
  {
7004
7345
  "data-opengeni-selected-file": true,
7005
7346
  className: "min-w-0 truncate font-og-mono text-og-xs text-og-fg-muted",
7006
- children: selected ?? "No file selected"
7347
+ children: selected ? focusLine !== null ? `${selected}:${focusLine}` : selected : "No file selected"
7007
7348
  }
7008
7349
  ),
7009
7350
  canEdit && /* @__PURE__ */ jsx10(
@@ -7018,106 +7359,122 @@ function SandboxFiles({
7018
7359
  }
7019
7360
  )
7020
7361
  ] }),
7021
- /* @__PURE__ */ jsx10("div", { className: "min-h-0 flex-1 overflow-auto", children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx10(
7022
- CodeEditor,
7023
- {
7024
- path: viewPath,
7025
- initialContents: fileView.content,
7026
- themeType: resolvedTheme,
7027
- onSave: (contents, expectedContents) => files.writeFile(viewPath, contents, { expectedContent: expectedContents }),
7028
- onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
7029
- onReload: () => setViewReloadRevision((revision) => revision + 1),
7030
- ...onEditIntent ? { onEditIntent } : {},
7031
- className: "h-full"
7032
- },
7033
- viewPath
7034
- ) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
7035
- Notice,
7362
+ /* @__PURE__ */ jsx10(
7363
+ "div",
7036
7364
  {
7037
- icon: /* @__PURE__ */ jsx10(
7038
- LoaderCircleIcon,
7365
+ ref: viewerScrollRef,
7366
+ className: "min-h-0 flex-1 overflow-auto",
7367
+ "data-opengeni-file-viewer-scroll": true,
7368
+ children: viewPath ? showEditor && fileView.content !== null ? /* @__PURE__ */ jsx10(
7369
+ CodeEditor,
7370
+ {
7371
+ path: viewPath,
7372
+ initialContents: fileView.content,
7373
+ themeType: resolvedTheme,
7374
+ onSave: (contents, expectedContents) => files.writeFile(viewPath, contents, { expectedContent: expectedContents }),
7375
+ onOverwrite: (contents) => files.writeFile(viewPath, contents, { force: true }),
7376
+ onReload: () => setViewReloadRevision((revision) => revision + 1),
7377
+ ...onEditIntent ? { onEditIntent } : {},
7378
+ className: "h-full"
7379
+ },
7380
+ viewPath
7381
+ ) : waitingForSelectedFile ? /* @__PURE__ */ jsxs8(
7382
+ Notice,
7039
7383
  {
7040
- className: "size-5 animate-spin motion-reduce:animate-none",
7041
- "aria-hidden": true
7384
+ icon: /* @__PURE__ */ jsx10(
7385
+ LoaderCircleIcon,
7386
+ {
7387
+ className: "size-5 animate-spin motion-reduce:animate-none",
7388
+ "aria-hidden": true
7389
+ }
7390
+ ),
7391
+ title: "Waking workspace",
7392
+ announce: "status",
7393
+ children: [
7394
+ "Opening ",
7395
+ viewPath,
7396
+ " when the live file system is ready\u2026"
7397
+ ]
7042
7398
  }
7043
- ),
7044
- title: "Waking workspace",
7045
- announce: "status",
7046
- children: [
7047
- "Opening ",
7399
+ ) : captureFileUnavailable ? /* @__PURE__ */ jsxs8(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
7400
+ /* @__PURE__ */ jsx10("p", { children: captureFileUnavailable.reason === "too-large" ? "This file is larger than the captured preview limit." : captureFileUnavailable.reason === "content-missing" ? "The captured copy is no longer available." : "This file was indexed, but it was not changed in the captured turn." }),
7401
+ onWakeWorkspace ? /* @__PURE__ */ jsx10(
7402
+ WakeButton,
7403
+ {
7404
+ onClick: () => {
7405
+ setLiveRequestedPath(viewPath);
7406
+ if (liveWorkspaceReady) void files.refresh();
7407
+ else onWakeWorkspace();
7408
+ },
7409
+ children: liveWorkspaceReady ? "Retry live file" : "Open live file"
7410
+ }
7411
+ ) : null
7412
+ ] }) : fileView.error ? /* @__PURE__ */ jsxs8(Notice, { announce: "alert", children: [
7413
+ "Could not open ",
7048
7414
  viewPath,
7049
- " when the live file system is ready\u2026"
7050
- ]
7051
- }
7052
- ) : captureFileUnavailable ? /* @__PURE__ */ jsxs8(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "On machine", children: [
7053
- /* @__PURE__ */ jsx10("p", { children: captureFileUnavailable.reason === "too-large" ? "This file is larger than the captured preview limit." : captureFileUnavailable.reason === "content-missing" ? "The captured copy is no longer available." : "This file was indexed, but it was not changed in the captured turn." }),
7054
- onWakeWorkspace ? /* @__PURE__ */ jsx10(
7055
- WakeButton,
7056
- {
7057
- onClick: () => {
7058
- setLiveRequestedPath(viewPath);
7059
- if (liveWorkspaceReady) void files.refresh();
7060
- else onWakeWorkspace();
7061
- },
7062
- children: liveWorkspaceReady ? "Retry live file" : "Open live file"
7063
- }
7064
- ) : null
7065
- ] }) : fileView.error ? /* @__PURE__ */ jsxs8(Notice, { announce: "alert", children: [
7066
- "Could not open ",
7067
- viewPath,
7068
- ": ",
7069
- fileView.error.message
7070
- ] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
7071
- "Loading ",
7072
- viewPath,
7073
- "\u2026"
7074
- ] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
7075
- viewPath,
7076
- " is a binary file (",
7077
- fileView.sizeBytes ?? 0,
7078
- " bytes)."
7079
- ] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
7080
- fileView.truncated && /* @__PURE__ */ jsxs8("div", { className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running", children: [
7081
- "Large file \u2014 showing a truncated preview (",
7082
- fileView.sizeBytes ?? 0,
7083
- " bytes loaded). Editing is disabled to avoid corrupting the file."
7084
- ] }),
7085
- usePierre ? /* @__PURE__ */ jsx10(
7086
- PierreFile,
7087
- {
7088
- path: viewPath,
7089
- contents: fileView.content,
7090
- themeType: resolvedTheme,
7091
- fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
7092
- className: "p-1"
7093
- }
7094
- ) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
7095
- ] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
7096
- "Loading ",
7097
- viewPath,
7098
- "\u2026"
7099
- ] }) : (
7100
- // Nothing selected — the tree shows the whole workspace; pick a file.
7101
- requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
7102
- Notice,
7103
- {
7104
- icon: /* @__PURE__ */ jsx10(
7105
- LoaderCircleIcon,
7415
+ ": ",
7416
+ fileView.error.message
7417
+ ] }) : fileView.loading ? /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
7418
+ "Loading ",
7419
+ viewPath,
7420
+ "\u2026"
7421
+ ] }) : fileView.isBinary ? /* @__PURE__ */ jsxs8(Notice, { children: [
7422
+ viewPath,
7423
+ " is a binary file (",
7424
+ fileView.sizeBytes ?? 0,
7425
+ " bytes)."
7426
+ ] }) : fileView.content !== null ? /* @__PURE__ */ jsxs8(Fragment3, { children: [
7427
+ fileView.truncated && /* @__PURE__ */ jsxs8("div", { className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-status-running", children: [
7428
+ "Large file \u2014 showing a truncated preview (",
7429
+ fileView.sizeBytes ?? 0,
7430
+ " bytes loaded). Editing is disabled to avoid corrupting the file."
7431
+ ] }),
7432
+ usePierre && focusLine === null ? /* @__PURE__ */ jsx10(
7433
+ PierreFile,
7106
7434
  {
7107
- className: "size-5 animate-spin motion-reduce:animate-none",
7108
- "aria-hidden": true
7435
+ path: viewPath,
7436
+ contents: fileView.content,
7437
+ themeType: resolvedTheme,
7438
+ fallback: /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content }),
7439
+ className: "p-1"
7440
+ },
7441
+ `${viewPath}:${viewReloadRevision}`
7442
+ ) : focusLine !== null ? /* @__PURE__ */ jsx10(
7443
+ LineNumberedFile,
7444
+ {
7445
+ path: viewPath,
7446
+ contents: fileView.content,
7447
+ focusLine
7109
7448
  }
7110
- ),
7111
- title: "Waking sandbox",
7112
- announce: "status",
7113
- children: [
7114
- "Opening ",
7115
- requestedPath,
7116
- " when the live workspace is ready\u2026"
7117
- ]
7118
- }
7119
- ) : /* @__PURE__ */ jsx10(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "Choose a file", children: "Select a file in the tree to preview it." })
7120
- ) })
7449
+ ) : /* @__PURE__ */ jsx10("pre", { className: "overflow-auto whitespace-pre p-2 font-og-mono text-og-sm text-og-fg", children: fileView.content })
7450
+ ] }) : /* @__PURE__ */ jsxs8(Notice, { announce: "status", children: [
7451
+ "Loading ",
7452
+ viewPath,
7453
+ "\u2026"
7454
+ ] }) : (
7455
+ // Nothing selected — the tree shows the whole workspace; pick a file.
7456
+ requestedPath && !requestedPathReady ? /* @__PURE__ */ jsxs8(
7457
+ Notice,
7458
+ {
7459
+ icon: /* @__PURE__ */ jsx10(
7460
+ LoaderCircleIcon,
7461
+ {
7462
+ className: "size-5 animate-spin motion-reduce:animate-none",
7463
+ "aria-hidden": true
7464
+ }
7465
+ ),
7466
+ title: "Waking sandbox",
7467
+ announce: "status",
7468
+ children: [
7469
+ "Opening ",
7470
+ requestedPath,
7471
+ " when the live workspace is ready\u2026"
7472
+ ]
7473
+ }
7474
+ ) : /* @__PURE__ */ jsx10(Notice, { icon: /* @__PURE__ */ jsx10(FileCode2Icon, { className: "size-5", "aria-hidden": true }), title: "Choose a file", children: "Select a file in the tree to preview it." })
7475
+ )
7476
+ }
7477
+ )
7121
7478
  ]
7122
7479
  }
7123
7480
  )
@@ -7137,6 +7494,62 @@ function WakeButton({ children, onClick }) {
7137
7494
  }
7138
7495
  );
7139
7496
  }
7497
+ function LineNumberedFile({
7498
+ path,
7499
+ contents,
7500
+ focusLine
7501
+ }) {
7502
+ const lines = contents.split("\n");
7503
+ const inRange = focusLine >= 1 && focusLine <= lines.length;
7504
+ const targetRef = useRef15(null);
7505
+ useEffect18(() => {
7506
+ targetRef.current?.scrollIntoView({
7507
+ block: "center",
7508
+ behavior: prefersReducedMotion() ? "auto" : "smooth"
7509
+ });
7510
+ }, [path, focusLine, contents]);
7511
+ return /* @__PURE__ */ jsxs8(
7512
+ "div",
7513
+ {
7514
+ className: "h-full min-h-0 overflow-auto font-og-mono text-og-sm text-og-fg",
7515
+ role: "region",
7516
+ "aria-label": inRange ? `${path} at line ${focusLine}` : path,
7517
+ children: [
7518
+ !inRange ? /* @__PURE__ */ jsxs8(
7519
+ "p",
7520
+ {
7521
+ className: "border-b border-og-border bg-og-surface-1 px-2 py-1 text-og-xs text-og-fg-muted",
7522
+ role: "status",
7523
+ children: [
7524
+ "Line ",
7525
+ focusLine,
7526
+ " is past the end of this file."
7527
+ ]
7528
+ }
7529
+ ) : null,
7530
+ /* @__PURE__ */ jsx10("div", { children: lines.map((text, index) => {
7531
+ const lineNumber = index + 1;
7532
+ const focused = inRange && lineNumber === focusLine;
7533
+ return /* @__PURE__ */ jsxs8(
7534
+ "div",
7535
+ {
7536
+ ref: focused ? targetRef : void 0,
7537
+ "data-opengeni-file-line": lineNumber,
7538
+ ...focused ? { "data-opengeni-focus-line": "" } : {},
7539
+ "aria-current": focused ? "location" : void 0,
7540
+ className: cn("flex gap-3 px-2 py-0.5 leading-6", focused && "bg-og-accent-soft"),
7541
+ children: [
7542
+ /* @__PURE__ */ jsx10("span", { className: "w-11 shrink-0 select-none text-right tabular-nums text-og-fg-subtle", children: lineNumber }),
7543
+ /* @__PURE__ */ jsx10("pre", { className: "min-w-0 flex-1 whitespace-pre-wrap break-words", children: text || " " })
7544
+ ]
7545
+ },
7546
+ lineNumber
7547
+ );
7548
+ }) })
7549
+ ]
7550
+ }
7551
+ );
7552
+ }
7140
7553
  function GitHeader({ git, loading }) {
7141
7554
  if (loading) {
7142
7555
  return /* @__PURE__ */ jsxs8(
@@ -7318,7 +7731,7 @@ function Notice({
7318
7731
  children: /* @__PURE__ */ jsxs8("div", { className: "flex max-w-sm flex-col items-center gap-2.5", children: [
7319
7732
  icon ? /* @__PURE__ */ jsx10("span", { className: "grid size-10 place-items-center rounded-og-lg border border-og-border bg-og-surface-1 text-og-fg-muted shadow-sm", children: icon }) : null,
7320
7733
  title ? /* @__PURE__ */ jsx10("p", { className: "font-medium text-og-fg", children: title }) : null,
7321
- /* @__PURE__ */ jsx10("div", { className: "leading-5", children })
7734
+ /* @__PURE__ */ jsx10("div", { "data-contrast-audited": true, className: "leading-5", children })
7322
7735
  ] })
7323
7736
  }
7324
7737
  );
@@ -7326,7 +7739,7 @@ function Notice({
7326
7739
 
7327
7740
  // src/components/workspace-dock.tsx
7328
7741
  import {
7329
- useCallback as useCallback23,
7742
+ useCallback as useCallback24,
7330
7743
  useEffect as useEffect19,
7331
7744
  useId as useId4,
7332
7745
  useLayoutEffect as useLayoutEffect4,
@@ -7388,7 +7801,7 @@ function WorkspaceDock({
7388
7801
  onActiveTabChange,
7389
7802
  collapsed: collapsedProp,
7390
7803
  onCollapsedChange,
7391
- showCollapseControl = false,
7804
+ showCollapseControl = true,
7392
7805
  headerAccessory,
7393
7806
  mobileLeadingControl,
7394
7807
  autoSaveId = "og.session.dock",
@@ -7414,19 +7827,35 @@ function WorkspaceDock({
7414
7827
  storage: getLayoutStorage(),
7415
7828
  id: autoSaveId
7416
7829
  });
7830
+ const expandedDefaultLayout = defaultLayout && (defaultLayout.dock ?? 0) > 1 ? defaultLayout : void 0;
7831
+ const lastExpandedSizeRef = useRef16(expandedDefaultLayout?.dock ?? defaultSize);
7832
+ const layoutIdentityRef = useRef16(autoSaveId);
7833
+ if (layoutIdentityRef.current !== autoSaveId) {
7834
+ layoutIdentityRef.current = autoSaveId;
7835
+ lastExpandedSizeRef.current = expandedDefaultLayout?.dock ?? defaultSize;
7836
+ }
7837
+ const persistExpandedLayout = useCallback24(
7838
+ (layout, meta) => {
7839
+ const dockSize = layout.dock ?? 0;
7840
+ if (dockSize <= 1) return;
7841
+ lastExpandedSizeRef.current = dockSize;
7842
+ onLayoutChanged(layout, meta);
7843
+ },
7844
+ [onLayoutChanged]
7845
+ );
7417
7846
  const requestedTab = activeTab ?? internalTab;
7418
7847
  const tabIds = tabs.map((tab) => tab.id);
7419
7848
  const firstTabId = tabs[0]?.id ?? "";
7420
7849
  const requestedTabIsValid = tabIds.includes(requestedTab);
7421
7850
  const current = requestedTabIsValid ? requestedTab : firstTabId;
7422
- const setTab = useCallback23(
7851
+ const setTab = useCallback24(
7423
7852
  (id) => {
7424
7853
  setInternalTab(id);
7425
7854
  onActiveTabChange?.(id);
7426
7855
  },
7427
7856
  [onActiveTabChange]
7428
7857
  );
7429
- const setCollapsed = useCallback23(
7858
+ const setCollapsed = useCallback24(
7430
7859
  (next) => {
7431
7860
  setInternalCollapsed((previous) => previous === next ? previous : next);
7432
7861
  onCollapsedChange?.(next);
@@ -7441,7 +7870,7 @@ function WorkspaceDock({
7441
7870
  dockPanelRef.current?.collapse();
7442
7871
  setMaximized(false);
7443
7872
  } else {
7444
- dockPanelRef.current?.expand();
7873
+ dockPanelRef.current?.resize(`${lastExpandedSizeRef.current}%`);
7445
7874
  }
7446
7875
  }, [collapsedProp, dockPanelRef]);
7447
7876
  useEffect19(() => {
@@ -7470,7 +7899,13 @@ function WorkspaceDock({
7470
7899
  return;
7471
7900
  }
7472
7901
  const target = returnFocusRef.current;
7473
- if (target?.isConnected) target.focus();
7902
+ if (target?.isConnected) {
7903
+ target.focus();
7904
+ return;
7905
+ }
7906
+ rootRef.current?.querySelector(
7907
+ '[data-workspace-primary] button:not([disabled]), [data-workspace-primary] a[href], [data-workspace-primary] input:not([disabled]), [data-workspace-primary] textarea:not([disabled]), [data-workspace-primary] select:not([disabled]), [data-workspace-primary] [tabindex]:not([tabindex="-1"])'
7908
+ )?.focus();
7474
7909
  });
7475
7910
  return () => cancelAnimationFrame(frame);
7476
7911
  }, [collapsed, hostControlled, narrow]);
@@ -7492,11 +7927,11 @@ function WorkspaceDock({
7492
7927
  setTab(firstTabId);
7493
7928
  }
7494
7929
  }, [firstTabId, requestedTabIsValid, setTab]);
7495
- const collapse = useCallback23(() => {
7930
+ const collapse = useCallback24(() => {
7496
7931
  dockPanelRef.current?.collapse();
7497
7932
  setCollapsed(true);
7498
7933
  }, [dockPanelRef, setCollapsed]);
7499
- const expand = useCallback23(() => {
7934
+ const expand = useCallback24(() => {
7500
7935
  dockPanelRef.current?.expand();
7501
7936
  setCollapsed(false);
7502
7937
  }, [dockPanelRef, setCollapsed]);
@@ -7581,7 +8016,7 @@ function WorkspaceDock({
7581
8016
  onTab: setTab,
7582
8017
  leading: mobileLeadingControl,
7583
8018
  accessory: headerAccessory,
7584
- controls: /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Close workspace", label: "Close workspace", children: /* @__PURE__ */ jsx11(XIcon2, { className: "size-4" }) })
8019
+ controls: /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Hide workspace", label: "Hide workspace", children: /* @__PURE__ */ jsx11(XIcon2, { className: "size-4" }) })
7585
8020
  }
7586
8021
  )
7587
8022
  }
@@ -7618,7 +8053,7 @@ function WorkspaceDock({
7618
8053
  children: maximized ? /* @__PURE__ */ jsx11(Minimize2Icon, { className: "size-3.5" }) : /* @__PURE__ */ jsx11(Maximize2Icon, { className: "size-3.5" })
7619
8054
  }
7620
8055
  ),
7621
- hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Collapse", label: "Collapse dock", children: /* @__PURE__ */ jsx11(PanelRightCloseIcon, { className: "size-3.5" }) })
8056
+ hostControlled && !showCollapseControl ? null : /* @__PURE__ */ jsx11(ChromeButton, { onClick: collapse, title: "Hide workspace", label: "Hide workspace", children: /* @__PURE__ */ jsx11(PanelRightCloseIcon, { className: "size-3.5" }) })
7622
8057
  ] })
7623
8058
  }
7624
8059
  );
@@ -7628,8 +8063,8 @@ function WorkspaceDock({
7628
8063
  {
7629
8064
  orientation: "horizontal",
7630
8065
  className: "min-h-0 flex-1",
7631
- ...defaultLayout ? { defaultLayout } : {},
7632
- onLayoutChanged,
8066
+ ...expandedDefaultLayout ? { defaultLayout: expandedDefaultLayout } : {},
8067
+ onLayoutChanged: persistExpandedLayout,
7633
8068
  children: [
7634
8069
  /* @__PURE__ */ jsx11(Panel2, { id: "primary", minSize: "30%", className: "min-h-0 min-w-0", children: /* @__PURE__ */ jsx11(
7635
8070
  "div",
@@ -7725,17 +8160,14 @@ function DockChrome({
7725
8160
  const active = tabs.find((t) => t.id === current) ?? tabs[0];
7726
8161
  const tabsetId = useId4();
7727
8162
  const tabRefs = useRef16([]);
7728
- const tabListRef = useRef16(null);
7729
8163
  const [visitedTabs, setVisitedTabs] = useState16(() => /* @__PURE__ */ new Set());
7730
- const [tabOverflow, setTabOverflow] = useState16({ start: false, end: false });
7731
8164
  const activeId = active?.id ?? "";
7732
- const activeTabIndex = tabs.findIndex((tab) => tab.id === activeId);
7733
8165
  useDockLayoutEffect(() => {
7734
8166
  const node = chromeRef.current;
7735
8167
  if (!node) return;
7736
8168
  const update = () => {
7737
8169
  const desktop = typeof window.matchMedia !== "function" || window.matchMedia("(min-width: 1024px)").matches;
7738
- const next = desktop && node.getBoundingClientRect().width < 420;
8170
+ const next = desktop && node.getBoundingClientRect().width < 440;
7739
8171
  setCompact((previous) => previous === next ? previous : next);
7740
8172
  };
7741
8173
  update();
@@ -7747,10 +8179,6 @@ function DockChrome({
7747
8179
  observer.observe(node);
7748
8180
  return () => observer.disconnect();
7749
8181
  }, []);
7750
- const keepActiveTabVisible = useCallback23(() => {
7751
- const selected = activeTabIndex >= 0 ? tabRefs.current[activeTabIndex] : null;
7752
- selected?.scrollIntoView({ block: "nearest", inline: "nearest" });
7753
- }, [activeTabIndex]);
7754
8182
  useEffect19(() => {
7755
8183
  if (!activeId) return;
7756
8184
  setVisitedTabs((previous) => {
@@ -7760,35 +8188,6 @@ function DockChrome({
7760
8188
  return next;
7761
8189
  });
7762
8190
  }, [activeId]);
7763
- useDockLayoutEffect(() => {
7764
- keepActiveTabVisible();
7765
- }, [activeId, keepActiveTabVisible]);
7766
- useDockLayoutEffect(() => {
7767
- const list = tabListRef.current;
7768
- if (!list) return;
7769
- const update = () => {
7770
- const max = Math.max(0, list.scrollWidth - list.clientWidth);
7771
- const next = { start: list.scrollLeft > 1, end: list.scrollLeft < max - 1 };
7772
- setTabOverflow(
7773
- (previous) => previous.start === next.start && previous.end === next.end ? previous : next
7774
- );
7775
- };
7776
- update();
7777
- list.addEventListener("scroll", update, { passive: true });
7778
- const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(() => {
7779
- keepActiveTabVisible();
7780
- update();
7781
- });
7782
- observer?.observe(list);
7783
- for (const tab of tabRefs.current) {
7784
- if (tab) observer?.observe(tab);
7785
- }
7786
- return () => {
7787
- list.removeEventListener("scroll", update);
7788
- observer?.disconnect();
7789
- };
7790
- }, [activeId, keepActiveTabVisible, tabs.length]);
7791
- const tabMask = tabOverflow.start && tabOverflow.end ? "linear-gradient(to right, transparent 0, black 14px, black calc(100% - 14px), transparent 100%)" : tabOverflow.start ? "linear-gradient(to right, transparent 0, black 14px, black 100%)" : tabOverflow.end ? "linear-gradient(to right, black 0, black calc(100% - 14px), transparent 100%)" : void 0;
7792
8191
  const activateTab = (index) => {
7793
8192
  const tab = tabs[index];
7794
8193
  if (!tab) {
@@ -7800,128 +8199,114 @@ function DockChrome({
7800
8199
  const onTabKeyDown = (event, index) => {
7801
8200
  if (tabs.length === 0) return;
7802
8201
  let nextIndex = null;
7803
- if (event.key === "ArrowRight") nextIndex = (index + 1) % tabs.length;
7804
- else if (event.key === "ArrowLeft") nextIndex = (index - 1 + tabs.length) % tabs.length;
8202
+ if (event.key === "ArrowDown") nextIndex = (index + 1) % tabs.length;
8203
+ else if (event.key === "ArrowUp") nextIndex = (index - 1 + tabs.length) % tabs.length;
7805
8204
  else if (event.key === "Home") nextIndex = 0;
7806
8205
  else if (event.key === "End") nextIndex = tabs.length - 1;
7807
8206
  if (nextIndex === null) return;
7808
8207
  event.preventDefault();
7809
8208
  activateTab(nextIndex);
7810
8209
  };
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
- ),
8210
+ return /* @__PURE__ */ jsxs9(
8211
+ "div",
8212
+ {
8213
+ ref: chromeRef,
8214
+ "data-dock-chrome": true,
8215
+ "data-compact": compact ? "true" : void 0,
8216
+ className: "flex h-full min-h-0 min-w-0 flex-col",
8217
+ children: [
8218
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-h-10 shrink-0 items-center gap-2 border-b border-og-border px-2 max-[1023px]:min-h-12", children: [
8219
+ /* @__PURE__ */ jsx11("div", { className: "flex min-w-0 flex-1 items-center gap-2", children: leading ?? /* @__PURE__ */ jsx11("span", { className: "hidden truncate text-og-sm font-semibold text-og-fg max-[1023px]:inline", children: "Workspace" }) }),
8220
+ /* @__PURE__ */ jsx11("div", { className: "min-w-0 shrink text-og-fg-muted", children: accessory }),
8221
+ /* @__PURE__ */ jsx11("div", { className: "flex shrink-0 items-center gap-0.5 text-og-fg-subtle", children: controls })
8222
+ ] }),
8223
+ /* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 min-w-0 flex-1", children: [
7834
8224
  /* @__PURE__ */ jsx11(
7835
8225
  "div",
7836
8226
  {
7837
- ref: tabListRef,
7838
8227
  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"
8228
+ "flex shrink-0 flex-col gap-1 overflow-x-hidden overflow-y-auto border-r border-og-border bg-og-surface-1/45 p-1.5",
8229
+ compact ? "w-12" : "w-32 max-[1023px]:w-36"
7841
8230
  ),
7842
8231
  role: "tablist",
7843
- "aria-orientation": "horizontal",
7844
- style: {
7845
- maskImage: tabMask,
7846
- WebkitMaskImage: tabMask,
7847
- scrollPaddingInline: "16px"
7848
- },
7849
- children: tabs.map((tab, index) => /* @__PURE__ */ jsxs9(
7850
- "button",
7851
- {
7852
- ref: (node) => {
7853
- tabRefs.current[index] = node;
8232
+ "aria-label": "Workspace panels",
8233
+ "aria-orientation": "vertical",
8234
+ children: tabs.map((tab, index) => {
8235
+ const tabName = tab.ariaLabel ?? (typeof tab.label === "string" ? tab.label : tab.id);
8236
+ return /* @__PURE__ */ jsxs9(
8237
+ "button",
8238
+ {
8239
+ ref: (node) => {
8240
+ tabRefs.current[index] = node;
8241
+ },
8242
+ id: `${tabsetId}-tab-${index}`,
8243
+ type: "button",
8244
+ role: "tab",
8245
+ "aria-label": compact && tab.icon ? tabName : void 0,
8246
+ title: compact && tab.icon ? tabName : void 0,
8247
+ "aria-selected": tab.id === current,
8248
+ "aria-controls": `${tabsetId}-panel-${index}`,
8249
+ tabIndex: tab.id === current ? 0 : -1,
8250
+ onClick: () => onTab(tab.id),
8251
+ onKeyDown: (event) => onTabKeyDown(event, index),
8252
+ className: cn(
8253
+ "group relative flex min-h-9 w-full items-center gap-2 rounded-og-md px-2 text-left 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 pointer-coarse:min-h-11",
8254
+ compact && "justify-center px-1",
8255
+ tab.id === current ? "bg-og-accent-soft text-og-fg shadow-[inset_2px_0_0_var(--og-color-accent)]" : "text-og-fg-subtle hover:bg-og-surface-2 hover:text-og-fg"
8256
+ ),
8257
+ children: [
8258
+ tab.icon ? /* @__PURE__ */ jsx11(
8259
+ "span",
8260
+ {
8261
+ className: "flex size-4 shrink-0 items-center justify-center [&>svg]:size-4",
8262
+ "aria-hidden": true,
8263
+ children: tab.icon
8264
+ }
8265
+ ) : null,
8266
+ !compact || !tab.icon ? /* @__PURE__ */ jsx11("span", { "data-contrast-audited": true, className: "min-w-0 flex-1 truncate", children: tab.label }) : null,
8267
+ tab.badge ? /* @__PURE__ */ jsx11(
8268
+ "span",
8269
+ {
8270
+ className: cn("shrink-0", compact && "absolute -right-0.5 -top-0.5 scale-90"),
8271
+ children: tab.badge
8272
+ }
8273
+ ) : null
8274
+ ]
7854
8275
  },
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
- {
7879
- className: cn(
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"
7882
- ),
7883
- children: accessory
8276
+ tab.id
8277
+ );
8278
+ })
7884
8279
  }
7885
8280
  ),
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) => {
7900
- const selected = tab.id === activeId;
7901
- const shouldMount = selected || visitedTabs.has(tab.id);
7902
- return /* @__PURE__ */ jsx11(
7903
- "div",
7904
- {
7905
- id: `${tabsetId}-panel-${index}`,
7906
- "aria-labelledby": `${tabsetId}-tab-${index}`,
7907
- className: "h-full min-h-0 min-w-0 overflow-hidden",
7908
- hidden: !selected,
7909
- role: "tabpanel",
7910
- children: shouldMount ? tab.content : null
7911
- },
7912
- tab.id
7913
- );
7914
- }) : /* @__PURE__ */ jsx11("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
7915
- ] });
8281
+ /* @__PURE__ */ jsx11("div", { className: "min-h-0 min-w-0 flex-1 overflow-hidden", children: tabs.length > 0 ? tabs.map((tab, index) => {
8282
+ const selected = tab.id === activeId;
8283
+ const shouldMount = selected || visitedTabs.has(tab.id);
8284
+ return /* @__PURE__ */ jsx11(
8285
+ "div",
8286
+ {
8287
+ id: `${tabsetId}-panel-${index}`,
8288
+ "aria-labelledby": `${tabsetId}-tab-${index}`,
8289
+ className: "h-full min-h-0 min-w-0 overflow-hidden",
8290
+ hidden: !selected,
8291
+ role: "tabpanel",
8292
+ children: shouldMount ? tab.content : null
8293
+ },
8294
+ tab.id
8295
+ );
8296
+ }) : /* @__PURE__ */ jsx11("div", { className: "h-full min-h-0 min-w-0", "aria-label": "Workspace", role: "tabpanel" }) })
8297
+ ] })
8298
+ ]
8299
+ }
8300
+ );
7916
8301
  }
7917
8302
 
7918
8303
  // src/components/sandbox-workspace.tsx
7919
8304
  import {
7920
8305
  lazy as lazy2,
7921
8306
  Suspense as Suspense2,
7922
- useCallback as useCallback24,
8307
+ useCallback as useCallback25,
7923
8308
  useEffect as useEffect20,
7924
- useMemo as useMemo13,
8309
+ useMemo as useMemo14,
7925
8310
  useRef as useRef17,
7926
8311
  useState as useState17
7927
8312
  } from "react";
@@ -7939,12 +8324,12 @@ import {
7939
8324
  } from "lucide-react";
7940
8325
  import { jsx as jsx12, jsxs as jsxs10 } from "react/jsx-runtime";
7941
8326
  var LazyBrowserViewer = lazy2(async () => {
7942
- const viewers = await import("./interactive-workbench-viewers-TOHBXJEE.js");
7943
- return { default: viewers.BrowserViewer };
8327
+ const { BrowserViewer } = await import("./browser-viewer-GXUICIAG.js");
8328
+ return { default: BrowserViewer };
7944
8329
  });
7945
8330
  var LazyComputerViewer = lazy2(async () => {
7946
- const viewers = await import("./interactive-workbench-viewers-TOHBXJEE.js");
7947
- return { default: viewers.ComputerViewer };
8331
+ const { ComputerViewer } = await import("./computer-viewer-ZOP4Q7A7.js");
8332
+ return { default: ComputerViewer };
7948
8333
  });
7949
8334
  var WORKBENCH_TAB_CHANGES = "changes";
7950
8335
  var WORKBENCH_TAB_FILES = "files";
@@ -7979,12 +8364,6 @@ function captureDegradedMessage(reason) {
7979
8364
  return "Workspace capture is incomplete because repository discovery failed. Live files remain authoritative.";
7980
8365
  }
7981
8366
  }
7982
- function WorkbenchTabLabel({ icon, 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 })
7986
- ] });
7987
- }
7988
8367
  function WorkbenchSurfaceLoading({ name }) {
7989
8368
  return /* @__PURE__ */ jsx12(
7990
8369
  CenteredState,
@@ -8037,6 +8416,29 @@ function sandboxProvisionInFlight(events) {
8037
8416
  }
8038
8417
  return inFlight;
8039
8418
  }
8419
+ function workspaceTurnInFlight(events) {
8420
+ const activeTurnIds = /* @__PURE__ */ new Set();
8421
+ let anonymousTurnInFlight = false;
8422
+ let sessionOwnsWorkspace = false;
8423
+ for (const event of events) {
8424
+ if (event.type === "session.status.changed") {
8425
+ const status = event.payload && typeof event.payload === "object" && !Array.isArray(event.payload) ? event.payload.status : null;
8426
+ sessionOwnsWorkspace = status === "running" || status === "recovering";
8427
+ continue;
8428
+ }
8429
+ if (event.type === "turn.started") {
8430
+ if (event.turnId) activeTurnIds.add(event.turnId);
8431
+ else anonymousTurnInFlight = true;
8432
+ continue;
8433
+ }
8434
+ if (event.type !== "turn.completed" && event.type !== "turn.failed" && event.type !== "turn.cancelled" && event.type !== "turn.superseded") {
8435
+ continue;
8436
+ }
8437
+ if (event.turnId) activeTurnIds.delete(event.turnId);
8438
+ else anonymousTurnInFlight = false;
8439
+ }
8440
+ return sessionOwnsWorkspace || anonymousTurnInFlight || activeTurnIds.size > 0;
8441
+ }
8040
8442
  function emptyWarmIntents(sessionId) {
8041
8443
  return {
8042
8444
  sessionId,
@@ -8052,9 +8454,17 @@ function useSandboxWorkspaceTabs(options) {
8052
8454
  onNotify,
8053
8455
  browserWebSocketFactory,
8054
8456
  computerWebSocketFactory,
8457
+ browserExtensionSetupUrl,
8055
8458
  requestedFilePath,
8056
8459
  requestedFileRequestId,
8460
+ requestedFileLine,
8057
8461
  onOpenFile,
8462
+ initialFilePath,
8463
+ onFilePathChange,
8464
+ initialBrowserSessionId,
8465
+ onBrowserSessionIdChange,
8466
+ initialComputerSessionId,
8467
+ onComputerSessionIdChange,
8058
8468
  onOpenComputerSession,
8059
8469
  requestedComputerSessionId,
8060
8470
  requestedComputerRequestId
@@ -8069,7 +8479,7 @@ function useSandboxWorkspaceTabs(options) {
8069
8479
  const terminalEnabled = surfaceSet.has(WORKBENCH_TAB_TERMINAL);
8070
8480
  const browserEnabled = surfaceSet.has(WORKBENCH_TAB_BROWSER);
8071
8481
  const desktopEnabled = surfaceSet.has(WORKBENCH_TAB_DESKTOP);
8072
- const createLinkedComputer = useCallback24(
8482
+ const createLinkedComputer = useCallback25(
8073
8483
  async (name, placement) => {
8074
8484
  const response = await client.createComputerSession(workspaceId, {
8075
8485
  operationId: crypto.randomUUID(),
@@ -8078,7 +8488,7 @@ function useSandboxWorkspaceTabs(options) {
8078
8488
  ...placement ? { placement } : {}
8079
8489
  });
8080
8490
  if (response.operation.state !== "completed" || response.session.lifecycle !== "active") {
8081
- throw new Error(response.operation.error?.message ?? "The computer could not be opened.");
8491
+ throw new Error(response.operation.error?.message ?? "The desktop could not be opened.");
8082
8492
  }
8083
8493
  return {
8084
8494
  id: response.session.id,
@@ -8092,13 +8502,14 @@ function useSandboxWorkspaceTabs(options) {
8092
8502
  const resolvedActiveTab = activeTab ?? initialTab;
8093
8503
  const changesActive = resolvedActiveTab === WORKBENCH_TAB_CHANGES;
8094
8504
  const filesActive = resolvedActiveTab === WORKBENCH_TAB_FILES;
8505
+ const turnInFlight = workspaceTurnInFlight(events);
8095
8506
  const surfaceIdentity = WORKBENCH_SURFACES.filter((surface) => surfaceSet.has(surface)).join(",");
8096
8507
  const [storedWarmIntents, setStoredWarmIntents] = useState17(
8097
8508
  () => emptyWarmIntents(sessionId)
8098
8509
  );
8099
8510
  const warmIntents = storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
8100
8511
  const { warmTerminal, warmFiles } = warmIntents;
8101
- const requestWarmIntent = useCallback24(
8512
+ const requestWarmIntent = useCallback25(
8102
8513
  (intent) => {
8103
8514
  setStoredWarmIntents((previous) => {
8104
8515
  const current = previous.sessionId === sessionId ? previous : emptyWarmIntents(sessionId);
@@ -8107,10 +8518,13 @@ function useSandboxWorkspaceTabs(options) {
8107
8518
  },
8108
8519
  [sessionId]
8109
8520
  );
8521
+ useEffect20(() => {
8522
+ if (requestedFilePath) requestWarmIntent("warmFiles");
8523
+ }, [requestWarmIntent, requestedFilePath, requestedFileRequestId]);
8110
8524
  const machines = useMachines({
8111
8525
  workspaceId,
8112
8526
  sessionId,
8113
- pollIntervalMs: 8e3,
8527
+ pollIntervalMs: MACHINES_SESSION_POLL_MS,
8114
8528
  enabled: machineSurfaceEnabled
8115
8529
  });
8116
8530
  const activeMachine = machines.machines.find((m) => m.sandboxId === machines.activeSandboxId) ?? machines.machines.find((m) => m.active) ?? null;
@@ -8158,7 +8572,7 @@ function useSandboxWorkspaceTabs(options) {
8158
8572
  const captureAvailable = captureState.available;
8159
8573
  const liveWorkspaceExpected = sandboxAcceptsLiveIo(liveness);
8160
8574
  const capturePending = !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);
8161
- const repoPaths = useMemo13(() => {
8575
+ const repoPaths = useMemo14(() => {
8162
8576
  const advertised = capabilities?.Git.repos ?? [];
8163
8577
  if (advertised.length > 0) return advertised;
8164
8578
  return captureState.capture?.repos.map((repo) => repo.root) ?? [];
@@ -8181,7 +8595,14 @@ function useSandboxWorkspaceTabs(options) {
8181
8595
  // No passive Channel-A reads while cold, even after a conclusive capture
8182
8596
  // miss. A missing capture gets an explicit "Open live workspace" gate.
8183
8597
  enabled: filesEnabled && (captureAvailable || fileSystemOn && liveWorkspaceExpected),
8184
- active: filesActive,
8598
+ // A cold sandbox can become warm while the first agent command still owns
8599
+ // startup. Do not race that command with an automatic Files read. Keeping
8600
+ // this fence at turn scope also avoids unsafe millisecond gaps between
8601
+ // sequential tool calls; rendered state and explicit user actions remain.
8602
+ active: filesActive && !turnInFlight,
8603
+ // A deliberate canonical absolute-path open browses in the selected target's
8604
+ // advertised namespace, so the authoritative tree and link share exact paths.
8605
+ ...requestedFilePath?.startsWith("/") && capabilities?.FileSystem.root ? { rootPath: capabilities.FileSystem.root } : {},
8185
8606
  repoPaths,
8186
8607
  liveness: liveIoLiveness,
8187
8608
  capture: captureState.capture,
@@ -8195,17 +8616,18 @@ function useSandboxWorkspaceTabs(options) {
8195
8616
  const [storedChangesComparison, setStoredChangesComparison] = useState17(null);
8196
8617
  const captureSupportsBranch = captureState.capture !== null && captureState.capture.repos.length > 0 && captureState.capture.repos.every((repo) => repo.branchDiff !== void 0);
8197
8618
  const changesComparison = storedChangesComparison?.sessionId === sessionId ? storedChangesComparison.value : liveWorkspaceExpected || captureSupportsBranch ? "branch" : "working";
8198
- const setChangesComparison = useCallback24(
8619
+ const setChangesComparison = useCallback25(
8199
8620
  (value) => setStoredChangesComparison({ sessionId, value }),
8200
8621
  [sessionId]
8201
8622
  );
8202
8623
  const git = useSandboxGit(sessionId, {
8203
8624
  events,
8204
8625
  enabled: changesComparison === "working" ? workspaceDataEnabled && (captureAvailable || gitOn && liveWorkspaceExpected) : changesComparison === "branch" ? workspaceDataEnabled && (captureSupportsBranch || gitOn && liveWorkspaceExpected) : workspaceDataEnabled && gitOn && liveWorkspaceExpected,
8205
- // Before the dock chooses its source-driven default, Git is the sole probe
8206
- // that may need to resolve that choice on a live workspace. Files remains
8207
- // dormant, avoiding the old duplicate full-tree read.
8208
- active: changesActive || resolvedActiveTab === null,
8626
+ // Live Git belongs to the visible Changes surface. In particular, do not
8627
+ // turn an unresolved/default tab into a hidden provider read: that races the
8628
+ // agent's own sandbox commands and used to refresh after every tool output
8629
+ // even while Files was visibly selected.
8630
+ active: changesActive && !turnInFlight,
8209
8631
  repoPaths,
8210
8632
  liveness: liveIoLiveness,
8211
8633
  comparison: changesComparison,
@@ -8246,48 +8668,31 @@ function useSandboxWorkspaceTabs(options) {
8246
8668
  if (defaultTabRef.current.identity !== defaultIdentity) {
8247
8669
  defaultTabRef.current = { identity: defaultIdentity, value: null };
8248
8670
  }
8249
- const captureIsAuthoritative = !liveWorkspaceExpected && (liveness !== void 0 || caps.error !== null);
8250
8671
  const captureHasChanges = (captureState.fileCount ?? 0) > 0 || (captureState.capture?.repos.some((repo) => (repo.branchDiff?.length ?? 0) > 0) ?? false);
8251
8672
  const captureUnavailable = captureState.fileCount === 0 && !captureHasChanges || captureState.error !== null;
8252
- const implicitBranchFallbackPending = changesComparison === "branch" && git.error !== null && storedChangesComparison?.sessionId !== sessionId;
8253
8673
  if (defaultTabRef.current.value === null) {
8254
8674
  if (initialTab && (!isWorkbenchSurface(initialTab) || surfaceSet.has(initialTab))) {
8255
8675
  defaultTabRef.current.value = initialTab;
8256
- } else if (git.source === "live") {
8257
- defaultTabRef.current.value = sourceDrivenDefaultTab(
8258
- git.diff.length > 0,
8259
- changesEnabled,
8260
- filesEnabled
8261
- );
8262
- } else if (captureIsAuthoritative && captureState.fileCount !== null && captureState.available) {
8676
+ } else if (captureState.fileCount !== null && captureState.available) {
8263
8677
  defaultTabRef.current.value = sourceDrivenDefaultTab(
8264
8678
  captureHasChanges,
8265
8679
  changesEnabled,
8266
8680
  filesEnabled
8267
8681
  );
8268
- } else if (captureIsAuthoritative && captureUnavailable && initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES) {
8269
- defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);
8270
- } else if (captureUnavailable && caps.error) {
8682
+ } else if (captureUnavailable && initialWorkspaceTab(events) === WORKBENCH_TAB_CHANGES) {
8271
8683
  defaultTabRef.current.value = sourceDrivenDefaultTab(true, changesEnabled, filesEnabled);
8272
- } else if (captureIsAuthoritative && captureState.fileCount !== null && captureUnavailable) {
8273
- defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
8274
- } else if (captureState.available && git.error && captureState.fileCount !== null) {
8275
- defaultTabRef.current.value = sourceDrivenDefaultTab(
8276
- captureHasChanges,
8277
- changesEnabled,
8278
- filesEnabled
8279
- );
8280
- } else if (captureUnavailable && git.error && !implicitBranchFallbackPending) {
8684
+ } else if (captureState.fileCount !== null || captureState.error !== null) {
8281
8685
  defaultTabRef.current.value = sourceDrivenDefaultTab(false, changesEnabled, filesEnabled);
8282
8686
  }
8283
8687
  }
8284
8688
  const defaultTab = defaultTabRef.current.value;
8285
- const tabs = useMemo13(() => {
8689
+ const tabs = useMemo14(() => {
8286
8690
  const list = [];
8287
8691
  if (changesEnabled)
8288
8692
  list.push({
8289
8693
  id: WORKBENCH_TAB_CHANGES,
8290
- label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(GitCompareArrowsIcon, {}), children: "Changes" }),
8694
+ label: "Changes",
8695
+ icon: /* @__PURE__ */ jsx12(GitCompareArrowsIcon, {}),
8291
8696
  badge: dirtyCount > 0 ? /* @__PURE__ */ jsx12(DirtyBadge, { count: dirtyCount }) : void 0,
8292
8697
  content: /* @__PURE__ */ jsx12(
8293
8698
  ChangesTabBody,
@@ -8316,7 +8721,8 @@ function useSandboxWorkspaceTabs(options) {
8316
8721
  if (filesEnabled)
8317
8722
  list.push({
8318
8723
  id: WORKBENCH_TAB_FILES,
8319
- label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(FileCode2Icon2, {}), children: "Files" }),
8724
+ label: "Files",
8725
+ icon: /* @__PURE__ */ jsx12(FileCode2Icon2, {}),
8320
8726
  content: /* @__PURE__ */ jsx12(
8321
8727
  SandboxFiles,
8322
8728
  {
@@ -8331,9 +8737,12 @@ function useSandboxWorkspaceTabs(options) {
8331
8737
  ...requestedFilePath ? {
8332
8738
  requestedPath: requestedFilePath,
8333
8739
  ...requestedFileRequestId !== null && requestedFileRequestId !== void 0 ? { requestedPathRequestId: requestedFileRequestId } : {},
8334
- requestedPathReady: sandboxAcceptsLiveIo(liveness)
8740
+ requestedPathReady: sandboxAcceptsLiveIo(liveness),
8741
+ ...requestedFileLine != null && requestedFileLine > 0 ? { requestedLine: requestedFileLine } : {}
8335
8742
  } : {},
8336
8743
  onEditIntent: () => requestWarmIntent("warmFiles"),
8744
+ initialSelectedPath: initialFilePath,
8745
+ onSelectedPathChange: onFilePathChange,
8337
8746
  className: "h-full"
8338
8747
  },
8339
8748
  sessionId
@@ -8342,7 +8751,8 @@ function useSandboxWorkspaceTabs(options) {
8342
8751
  if (terminalOn) {
8343
8752
  list.push({
8344
8753
  id: WORKBENCH_TAB_TERMINAL,
8345
- label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(SquareTerminalIcon, {}), children: "Terminal" }),
8754
+ label: "Terminal",
8755
+ icon: /* @__PURE__ */ jsx12(SquareTerminalIcon, {}),
8346
8756
  content: /* @__PURE__ */ jsx12("div", { className: "h-full bg-og-bg p-1", children: /* @__PURE__ */ jsx12(
8347
8757
  SandboxTerminal,
8348
8758
  {
@@ -8362,7 +8772,8 @@ function useSandboxWorkspaceTabs(options) {
8362
8772
  if (browserEnabled) {
8363
8773
  list.push({
8364
8774
  id: WORKBENCH_TAB_BROWSER,
8365
- label: /* @__PURE__ */ jsx12(WorkbenchTabLabel, { icon: /* @__PURE__ */ jsx12(Globe2Icon, {}), children: "Browser" }),
8775
+ label: "Browser",
8776
+ icon: /* @__PURE__ */ jsx12(Globe2Icon, {}),
8366
8777
  content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Browser" }), children: /* @__PURE__ */ jsx12(
8367
8778
  LazyBrowserViewer,
8368
8779
  {
@@ -8371,9 +8782,12 @@ function useSandboxWorkspaceTabs(options) {
8371
8782
  sessionId,
8372
8783
  enabled: workspaceVisible,
8373
8784
  onNotify,
8785
+ initialBrowserSessionId,
8786
+ onBrowserSessionIdChange,
8374
8787
  ...desktopEnabled ? { createLinkedComputer } : {},
8375
8788
  ...onOpenComputerSession ? { onOpenComputer: onOpenComputerSession } : {},
8376
8789
  ...browserWebSocketFactory ? { webSocketFactory: browserWebSocketFactory } : {},
8790
+ ...browserExtensionSetupUrl ? { browserExtensionSetupUrl } : {},
8377
8791
  className: "h-full"
8378
8792
  },
8379
8793
  sessionId
@@ -8383,8 +8797,9 @@ function useSandboxWorkspaceTabs(options) {
8383
8797
  if (desktopEnabled) {
8384
8798
  list.push({
8385
8799
  id: WORKBENCH_TAB_DESKTOP,
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(
8800
+ label: "Desktop",
8801
+ icon: /* @__PURE__ */ jsx12(MonitorIcon, {}),
8802
+ content: /* @__PURE__ */ jsx12(Suspense2, { fallback: /* @__PURE__ */ jsx12(WorkbenchSurfaceLoading, { name: "Desktop" }), children: /* @__PURE__ */ jsx12(
8388
8803
  LazyComputerViewer,
8389
8804
  {
8390
8805
  client,
@@ -8392,6 +8807,8 @@ function useSandboxWorkspaceTabs(options) {
8392
8807
  sessionId,
8393
8808
  enabled: workspaceVisible,
8394
8809
  onNotify,
8810
+ initialComputerSessionId,
8811
+ onComputerSessionIdChange,
8395
8812
  requestedComputerSessionId,
8396
8813
  requestedComputerRequestId,
8397
8814
  ...computerWebSocketFactory ? { webSocketFactory: computerWebSocketFactory } : {},
@@ -8414,7 +8831,14 @@ function useSandboxWorkspaceTabs(options) {
8414
8831
  warmTerminal,
8415
8832
  requestedFilePath,
8416
8833
  requestedFileRequestId,
8834
+ requestedFileLine,
8417
8835
  onOpenFile,
8836
+ initialFilePath,
8837
+ onFilePathChange,
8838
+ initialBrowserSessionId,
8839
+ onBrowserSessionIdChange,
8840
+ initialComputerSessionId,
8841
+ onComputerSessionIdChange,
8418
8842
  onOpenComputerSession,
8419
8843
  requestedComputerSessionId,
8420
8844
  requestedComputerRequestId,
@@ -8462,9 +8886,18 @@ function SandboxWorkspace(props) {
8462
8886
  leadingTabs,
8463
8887
  trailingTabs,
8464
8888
  initialTab,
8889
+ onActiveTabChange,
8890
+ initialFilePath,
8891
+ onFilePathChange,
8892
+ openFileRequest,
8893
+ initialBrowserSessionId,
8894
+ onBrowserSessionIdChange,
8895
+ initialComputerSessionId,
8896
+ onComputerSessionIdChange,
8465
8897
  onNotify,
8466
8898
  browserWebSocketFactory,
8467
8899
  computerWebSocketFactory,
8900
+ browserExtensionSetupUrl,
8468
8901
  collapsed,
8469
8902
  onCollapsedChange,
8470
8903
  showCollapseControl,
@@ -8482,15 +8915,29 @@ function SandboxWorkspace(props) {
8482
8915
  const nextComputerRequestId = useRef17(0);
8483
8916
  const selectedTab = storedSelection?.sessionId === sessionId ? storedSelection.tab : null;
8484
8917
  const activeTabHint = selectedTab ?? initialTab ?? leadingTabs?.[0]?.id ?? null;
8485
- const openFile = useCallback24(
8486
- (path) => {
8487
- nextFileRequestId.current += 1;
8488
- setRequestedFile({ sessionId, path, requestId: nextFileRequestId.current });
8918
+ const openFile = useCallback25(
8919
+ (path, line) => {
8920
+ setRequestedFile({
8921
+ sessionId,
8922
+ path,
8923
+ line: line != null && line > 0 ? line : null,
8924
+ requestId: ++nextFileRequestId.current
8925
+ });
8489
8926
  setStoredSelection({ sessionId, tab: WORKBENCH_TAB_FILES });
8927
+ onActiveTabChange?.(WORKBENCH_TAB_FILES);
8928
+ onFilePathChange?.(path);
8929
+ onCollapsedChange?.(false);
8490
8930
  },
8491
- [sessionId]
8931
+ [onActiveTabChange, onCollapsedChange, onFilePathChange, sessionId]
8492
8932
  );
8493
- const openComputer = useCallback24(
8933
+ const handledOpenFileRequestId = useRef17(null);
8934
+ useEffect20(() => {
8935
+ if (!openFileRequest) return;
8936
+ if (handledOpenFileRequestId.current === openFileRequest.requestId) return;
8937
+ handledOpenFileRequestId.current = openFileRequest.requestId;
8938
+ openFile(openFileRequest.path, openFileRequest.line);
8939
+ }, [openFile, openFileRequest]);
8940
+ const openComputer = useCallback25(
8494
8941
  (computerSessionId) => {
8495
8942
  nextComputerRequestId.current += 1;
8496
8943
  setRequestedComputer({
@@ -8499,8 +8946,10 @@ function SandboxWorkspace(props) {
8499
8946
  requestId: nextComputerRequestId.current
8500
8947
  });
8501
8948
  setStoredSelection({ sessionId, tab: WORKBENCH_TAB_DESKTOP });
8949
+ onActiveTabChange?.(WORKBENCH_TAB_DESKTOP);
8950
+ onComputerSessionIdChange?.(computerSessionId);
8502
8951
  },
8503
- [sessionId]
8952
+ [onActiveTabChange, onComputerSessionIdChange, sessionId]
8504
8953
  );
8505
8954
  const {
8506
8955
  tabs: workbenchTabs,
@@ -8518,8 +8967,16 @@ function SandboxWorkspace(props) {
8518
8967
  ...onNotify ? { onNotify } : {},
8519
8968
  ...browserWebSocketFactory ? { browserWebSocketFactory } : {},
8520
8969
  ...computerWebSocketFactory ? { computerWebSocketFactory } : {},
8970
+ ...browserExtensionSetupUrl ? { browserExtensionSetupUrl } : {},
8971
+ initialFilePath,
8972
+ onFilePathChange,
8973
+ initialBrowserSessionId,
8974
+ onBrowserSessionIdChange,
8975
+ initialComputerSessionId,
8976
+ onComputerSessionIdChange,
8521
8977
  requestedFilePath: requestedFile?.sessionId === sessionId ? requestedFile.path : null,
8522
8978
  requestedFileRequestId: requestedFile?.sessionId === sessionId ? requestedFile.requestId : null,
8979
+ requestedFileLine: requestedFile?.sessionId === sessionId ? requestedFile.line : null,
8523
8980
  onOpenFile: openFile,
8524
8981
  onOpenComputerSession: openComputer,
8525
8982
  requestedComputerSessionId: requestedComputer?.sessionId === sessionId ? requestedComputer.computerSessionId : null,
@@ -8528,10 +8985,17 @@ function SandboxWorkspace(props) {
8528
8985
  const tabs = [...leadingTabs ?? [], ...workbenchTabs, ...trailingTabs ?? []];
8529
8986
  const preferredTab = selectedTab ?? defaultTab;
8530
8987
  const activeTab = preferredTab && tabs.some((tab) => tab.id === preferredTab) ? preferredTab : tabs[0]?.id;
8531
- const selectTab = useCallback24(
8532
- (tab) => setStoredSelection({ sessionId, tab }),
8533
- [sessionId]
8988
+ const selectTab = useCallback25(
8989
+ (tab) => {
8990
+ setStoredSelection({ sessionId, tab });
8991
+ onActiveTabChange?.(tab);
8992
+ },
8993
+ [onActiveTabChange, sessionId]
8534
8994
  );
8995
+ useEffect20(() => {
8996
+ if (selectedTab !== null || defaultTab === null) return;
8997
+ setStoredSelection({ sessionId, tab: defaultTab });
8998
+ }, [defaultTab, selectedTab, sessionId]);
8535
8999
  return /* @__PURE__ */ jsx12(
8536
9000
  WorkspaceDock,
8537
9001
  {
@@ -8786,7 +9250,7 @@ function CenteredState({
8786
9250
  }
8787
9251
 
8788
9252
  // src/hooks/use-codex-accounts.ts
8789
- import { useCallback as useCallback25, useState as useState18 } from "react";
9253
+ import { useCallback as useCallback26, useState as useState18 } from "react";
8790
9254
  function isCodexAccountEvent(event) {
8791
9255
  return event.type === "codex.account.switched";
8792
9256
  }
@@ -8807,7 +9271,7 @@ function useCodexAccounts(options = {}) {
8807
9271
  const codexClient = options.codexClient ?? client;
8808
9272
  const sessionId = options.sessionId;
8809
9273
  const sharedEvents = options.events;
8810
- const load = useCallback25(async () => {
9274
+ const load = useCallback26(async () => {
8811
9275
  const accountsP = codexClient.listCodexAccounts(workspaceId);
8812
9276
  const sessionP = sessionId && codexClient.getSession ? codexClient.getSession(workspaceId, sessionId).catch(() => null) : Promise.resolve(null);
8813
9277
  const [acc, session] = await Promise.all([accountsP, sessionP]);
@@ -8841,7 +9305,7 @@ function useCodexAccounts(options = {}) {
8841
9305
  ...sharedEvents !== void 0 ? { events: sharedEvents } : {}
8842
9306
  }
8843
9307
  );
8844
- const pin = useCallback25(
9308
+ const pin = useCallback26(
8845
9309
  async (target) => {
8846
9310
  if (!sessionId || !codexClient.pinSessionCodexAccount) {
8847
9311
  return false;
@@ -8857,7 +9321,7 @@ function useCodexAccounts(options = {}) {
8857
9321
  },
8858
9322
  [codexClient, workspaceId, sessionId, runMutation, refresh]
8859
9323
  );
8860
- const refreshUsage = useCallback25(async () => {
9324
+ const refreshUsage = useCallback26(async () => {
8861
9325
  if (!codexClient.refreshCodexUsage) {
8862
9326
  return false;
8863
9327
  }
@@ -8889,7 +9353,7 @@ function useCodexAccounts(options = {}) {
8889
9353
  }
8890
9354
 
8891
9355
  // src/hooks/use-supergrok-accounts.ts
8892
- import { useCallback as useCallback26 } from "react";
9356
+ import { useCallback as useCallback27 } from "react";
8893
9357
  var EMPTY = {
8894
9358
  accounts: [],
8895
9359
  activeAccountId: null,
@@ -8902,7 +9366,7 @@ var EMPTY = {
8902
9366
  function useSuperGrokAccounts(options = {}) {
8903
9367
  const { client, workspaceId } = useOpenGeni(options);
8904
9368
  const supergrokClient = options.supergrokClient ?? client;
8905
- const load = useCallback26(
9369
+ const load = useCallback27(
8906
9370
  async () => await supergrokClient.listSuperGrokAccounts(workspaceId),
8907
9371
  [supergrokClient, workspaceId]
8908
9372
  );
@@ -8911,7 +9375,7 @@ function useSuperGrokAccounts(options = {}) {
8911
9375
  pollIntervalMs: options.pollIntervalMs
8912
9376
  });
8913
9377
  const { run, mutating: activating, mutationError } = useMutationRunner();
8914
- const activate = useCallback26(
9378
+ const activate = useCallback27(
8915
9379
  async (accountId) => {
8916
9380
  if (!supergrokClient.activateSuperGrokAccount) return false;
8917
9381
  const result = await run(async () => {
@@ -8970,6 +9434,9 @@ export {
8970
9434
  INITIAL_TRANSCRIPTION_CONTROL_STATE,
8971
9435
  IndexedDbVoiceRecordingStore,
8972
9436
  LightboxProvider,
9437
+ MACHINES_COMPOSER_POLL_MS,
9438
+ MACHINES_DASHBOARD_POLL_MS,
9439
+ MACHINES_SESSION_POLL_MS,
8973
9440
  MACHINE_STATE_BADGE_META,
8974
9441
  METRICS,
8975
9442
  METRIC_WINDOWS,
@@ -8978,6 +9445,7 @@ export {
8978
9445
  MachineDockBar,
8979
9446
  MachineHealthPill,
8980
9447
  MachineMetrics,
9448
+ MachineOperationPolicyEditor,
8981
9449
  MachineStatusPill,
8982
9450
  MachinesDashboard,
8983
9451
  Markdown,
@@ -9120,6 +9588,10 @@ export {
9120
9588
  rawTypeOf,
9121
9589
  runnableLatencyModesForModel,
9122
9590
  sandboxCommandExitCode,
9591
+ sandboxFileLocationFromHref,
9592
+ sandboxFilePathFromHref,
9593
+ sessionChromeGoalPillExplanation,
9594
+ sessionChromeGoalPillLabel,
9123
9595
  sessionChromeGoalPillState,
9124
9596
  sessionDisplayTitle,
9125
9597
  sessionStatusFromEvents,
@@ -9165,6 +9637,7 @@ export {
9165
9637
  useSandboxWorkspaceTabs,
9166
9638
  useScheduledTasks,
9167
9639
  useSession,
9640
+ useSessionBackgroundCommands,
9168
9641
  useSessionCapabilities,
9169
9642
  useSessionControl,
9170
9643
  useSessionEvents,