@opengeni/react 3.5.1-canary.0 → 3.7.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 (52) hide show
  1. package/dist/artifacts.d.ts +1 -1
  2. package/dist/artifacts.js +229 -6
  3. package/dist/artifacts.js.map +1 -1
  4. package/dist/{chunk-IB27C53Y.js → chunk-C6WDLHBA.js} +31 -23
  5. package/dist/chunk-C6WDLHBA.js.map +1 -0
  6. package/dist/{chunk-WY3MELVJ.js → chunk-DOK4KZF2.js} +119 -8
  7. package/dist/chunk-DOK4KZF2.js.map +1 -0
  8. package/dist/{chunk-TJFMMDQU.js → chunk-FJBOU2TH.js} +112 -6
  9. package/dist/chunk-FJBOU2TH.js.map +1 -0
  10. package/dist/{chunk-UNCXXIQR.js → chunk-R55HFTT3.js} +66 -24
  11. package/dist/chunk-R55HFTT3.js.map +1 -0
  12. package/dist/client.d.ts +1 -1
  13. package/dist/components/artifacts/index.d.ts +1 -1
  14. package/dist/components/artifacts/published-html-artifact-frame.d.ts +45 -3
  15. package/dist/components/composer.d.ts +2 -0
  16. package/dist/components/session-chrome.d.ts +1 -1
  17. package/dist/composer.js +1 -1
  18. package/dist/{fleet-decision-row-GGCAT4E4.js → fleet-decision-row-YZ3ZKWMM.js} +2 -1
  19. package/dist/fleet-decision-row-YZ3ZKWMM.js.map +1 -0
  20. package/dist/hooks/use-file-attachments.d.ts +6 -0
  21. package/dist/hooks/use-sandbox-files.d.ts +4 -1
  22. package/dist/hooks/use-workspace-edit.d.ts +4 -0
  23. package/dist/index.js +114 -58
  24. package/dist/index.js.map +1 -1
  25. package/dist/session-ui.js +2 -2
  26. package/dist/session.js +2 -2
  27. package/dist/timeline/registry.d.ts +2 -0
  28. package/dist/timeline/types.d.ts +1 -1
  29. package/package.json +2 -2
  30. package/src/artifacts.ts +3 -0
  31. package/src/client.ts +2 -1
  32. package/src/components/artifacts/index.ts +3 -0
  33. package/src/components/artifacts/published-html-artifact-frame.tsx +284 -4
  34. package/src/components/composer.tsx +89 -25
  35. package/src/components/message-timeline.tsx +32 -8
  36. package/src/components/sandbox-workspace.tsx +11 -1
  37. package/src/components/session-chrome.tsx +3 -3
  38. package/src/hooks/use-file-attachments.ts +34 -0
  39. package/src/hooks/use-sandbox-files.ts +68 -20
  40. package/src/hooks/use-session-events.ts +21 -26
  41. package/src/hooks/use-workspace-edit.ts +23 -5
  42. package/src/timeline/fleet-decision-projection.ts +2 -1
  43. package/src/timeline/fleet-decision-row.tsx +1 -0
  44. package/src/timeline/projection.ts +151 -7
  45. package/src/timeline/registry.ts +15 -3
  46. package/src/timeline/tool-renderers.tsx +117 -1
  47. package/src/timeline/types.ts +8 -1
  48. package/dist/chunk-IB27C53Y.js.map +0 -1
  49. package/dist/chunk-TJFMMDQU.js.map +0 -1
  50. package/dist/chunk-UNCXXIQR.js.map +0 -1
  51. package/dist/chunk-WY3MELVJ.js.map +0 -1
  52. package/dist/fleet-decision-row-GGCAT4E4.js.map +0 -1
package/dist/index.js CHANGED
@@ -95,7 +95,7 @@ import {
95
95
  useSlashCommands,
96
96
  useTranscription,
97
97
  useVoiceInput
98
- } from "./chunk-UNCXXIQR.js";
98
+ } from "./chunk-R55HFTT3.js";
99
99
  import {
100
100
  BillingClassMark,
101
101
  ModelPolicyPicker,
@@ -163,7 +163,7 @@ import {
163
163
  useThemeType,
164
164
  userMessageLikelyNeedsDisclosure,
165
165
  v4aToGitFileDiff
166
- } from "./chunk-WY3MELVJ.js";
166
+ } from "./chunk-DOK4KZF2.js";
167
167
  import {
168
168
  TimelineAnnotationsChip
169
169
  } from "./chunk-W3OUAH6M.js";
@@ -209,7 +209,7 @@ import {
209
209
  useSessionLineage,
210
210
  useSessionMcpApprovalPolicy,
211
211
  useTurnQueue
212
- } from "./chunk-IB27C53Y.js";
212
+ } from "./chunk-C6WDLHBA.js";
213
213
  import {
214
214
  FILE_ONLY_MESSAGE_TEXT,
215
215
  composeSendInput,
@@ -237,7 +237,7 @@ import {
237
237
  projectPendingHumanInputRequests,
238
238
  sessionStatusFromEvents,
239
239
  toolDisplayName
240
- } from "./chunk-TJFMMDQU.js";
240
+ } from "./chunk-FJBOU2TH.js";
241
241
  import {
242
242
  COMPOSER_PAYMENT_REQUIRED_MESSAGE,
243
243
  CREDIT_EXHAUSTION_MESSAGE,
@@ -2085,8 +2085,11 @@ function normalizeRoot(root) {
2085
2085
  const normalized = slashNormalized.replace(/\/+$/, "");
2086
2086
  return normalized === "." ? "" : normalized;
2087
2087
  }
2088
+ function isAbsoluteTreePath(path) {
2089
+ return path.startsWith("/") || /^[A-Za-z]:\//u.test(path);
2090
+ }
2088
2091
  function joinTreeRoot(root, path) {
2089
- if (!root || path.startsWith("/")) return path;
2092
+ if (!root || isAbsoluteTreePath(path)) return path;
2090
2093
  return root === "/" ? `/${path}` : `${root}/${path}`;
2091
2094
  }
2092
2095
  function qualifyStatusPath(root, path, treeRoot) {
@@ -2174,6 +2177,7 @@ var FS_LIST_BATCH_REQUEST_LIMIT = 16;
2174
2177
  function initialDirectoryFrontier(rootPath, repoPaths) {
2175
2178
  const root = normalizeRoot(rootPath);
2176
2179
  const rootParts = root.split("/").filter(Boolean);
2180
+ const rootPrefix = root.startsWith("//") ? "//" : root.startsWith("/") ? "/" : "";
2177
2181
  const paths = /* @__PURE__ */ new Set();
2178
2182
  for (const repoPath of repoPaths) {
2179
2183
  const canonicalRepoPath = joinTreeRoot(root, normalizeRoot(repoPath));
@@ -2181,7 +2185,7 @@ function initialDirectoryFrontier(rootPath, repoPaths) {
2181
2185
  if (rootParts.some((part, index) => parts[index] !== part)) continue;
2182
2186
  for (let depth = rootParts.length + 1; depth <= parts.length; depth += 1) {
2183
2187
  const path = parts.slice(0, depth).join("/");
2184
- paths.add(root.startsWith("/") ? `/${path}` : path);
2188
+ paths.add(`${rootPrefix}${path}`);
2185
2189
  }
2186
2190
  }
2187
2191
  return [...paths].sort((a, b) => a.split("/").length - b.split("/").length || a.localeCompare(b)).slice(0, 8);
@@ -2291,6 +2295,12 @@ function useSandboxFiles(sessionId, options = {}) {
2291
2295
  const enabled = (options.enabled ?? true) && Boolean(sessionId);
2292
2296
  const active = options.active ?? true;
2293
2297
  const rootPath = options.rootPath ?? "";
2298
+ const routeEpoch = options.route?.epoch ?? null;
2299
+ const routeRoot = options.route?.root ?? null;
2300
+ const route = useMemo6(
2301
+ () => routeEpoch !== null && routeRoot !== null ? { epoch: routeEpoch, root: routeRoot } : void 0,
2302
+ [routeEpoch, routeRoot]
2303
+ );
2294
2304
  const repoPathsKey = [
2295
2305
  ...new Set((options.repoPaths?.length ? options.repoPaths : [rootPath]).map(normalizeRoot))
2296
2306
  ].join("\0");
@@ -2301,7 +2311,7 @@ function useSandboxFiles(sessionId, options = {}) {
2301
2311
  const acceptsEventReadsRef = useRef5(acceptsEventReads);
2302
2312
  acceptsEventReadsRef.current = acceptsEventReads;
2303
2313
  const hasLiveIoFence = options.liveness !== void 0;
2304
- const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${rootPath}\0${repoPathsKey}`;
2314
+ const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${rootPath}\0${repoPathsKey}\0${routeEpoch ?? ""}\0${routeRoot ?? ""}`;
2305
2315
  const [tree, setTree] = useState4([]);
2306
2316
  const treeRef = useRef5([]);
2307
2317
  const [loading, setLoading] = useState4(false);
@@ -2364,7 +2374,8 @@ function useSandboxFiles(sessionId, options = {}) {
2364
2374
  const frontierPaths = initialDirectoryFrontier(rootPath, repoPaths);
2365
2375
  const listRequests = [rootPath, ...frontierPaths].map((path) => ({
2366
2376
  path,
2367
- depth: 1
2377
+ depth: 1,
2378
+ ...route ? { route } : {}
2368
2379
  }));
2369
2380
  const listedBatch = await client.fsListBatch(
2370
2381
  workspaceId,
@@ -2433,7 +2444,7 @@ function useSandboxFiles(sessionId, options = {}) {
2433
2444
  if (refreshGenerationRef.current === generation) setGitLoading(false);
2434
2445
  if (refreshAbortRef.current === refreshAbort) refreshAbortRef.current = null;
2435
2446
  }
2436
- }, [client, workspaceId, sessionId, rootPath, repoPaths, applyStatus]);
2447
+ }, [client, workspaceId, sessionId, rootPath, repoPaths, route, applyStatus]);
2437
2448
  const seedFromCapture = useCallback15(
2438
2449
  (manifest) => {
2439
2450
  const overlay = /* @__PURE__ */ new Map();
@@ -2477,7 +2488,7 @@ function useSandboxFiles(sessionId, options = {}) {
2477
2488
  const listed = await client.fsList(
2478
2489
  workspaceId,
2479
2490
  sessionId,
2480
- { path, depth: 1 },
2491
+ { path, depth: 1, ...route ? { route } : {} },
2481
2492
  { signal: identitySignal }
2482
2493
  );
2483
2494
  if (identityGenerationRef.current !== identityGeneration) return;
@@ -2497,7 +2508,7 @@ function useSandboxFiles(sessionId, options = {}) {
2497
2508
  }
2498
2509
  }
2499
2510
  },
2500
- [client, workspaceId, sessionId, capture, source, applyStatus]
2511
+ [client, workspaceId, sessionId, capture, source, route, applyStatus]
2501
2512
  );
2502
2513
  const readFile = useCallback15(
2503
2514
  async (path, requestOptions = {}) => {
@@ -2570,9 +2581,14 @@ function useSandboxFiles(sessionId, options = {}) {
2570
2581
  }
2571
2582
  throw new Error("Captured file download failed after refreshing its URL.");
2572
2583
  }
2573
- return await client.fsRead(workspaceId, sessionId, { path }, { signal });
2584
+ return await client.fsRead(
2585
+ workspaceId,
2586
+ sessionId,
2587
+ { path, ...route ? { route } : {} },
2588
+ { signal }
2589
+ );
2574
2590
  },
2575
- [client, workspaceId, sessionId, capture, source]
2591
+ [client, workspaceId, sessionId, capture, source, route]
2576
2592
  );
2577
2593
  const reconcilePath = useCallback15(
2578
2594
  async (path, requestSignal) => {
@@ -2582,7 +2598,12 @@ function useSandboxFiles(sessionId, options = {}) {
2582
2598
  const signal = requestSignal ? AbortSignal.any([identitySignal, requestSignal]) : identitySignal;
2583
2599
  if (!parentIsLoaded(treeRef.current, path, rootPath)) return;
2584
2600
  try {
2585
- const listed = await client.fsList(workspaceId, sessionId, { path, depth: 1 }, { signal });
2601
+ const listed = await client.fsList(
2602
+ workspaceId,
2603
+ sessionId,
2604
+ { path, depth: 1, ...route ? { route } : {} },
2605
+ { signal }
2606
+ );
2586
2607
  if (identityGenerationRef.current !== identityGeneration) return;
2587
2608
  const children = (listed.root.children ?? []).map((node) => fsNodeToTree(node));
2588
2609
  if (path === normalizeRoot(rootPath)) {
@@ -2596,7 +2617,7 @@ function useSandboxFiles(sessionId, options = {}) {
2596
2617
  } catch {
2597
2618
  }
2598
2619
  },
2599
- [client, workspaceId, sessionId, rootPath, applyStatus]
2620
+ [client, workspaceId, sessionId, rootPath, route, applyStatus]
2600
2621
  );
2601
2622
  const reconcilePaths = useCallback15(
2602
2623
  async (requestedPaths, requestSignal) => {
@@ -2613,7 +2634,13 @@ function useSandboxFiles(sessionId, options = {}) {
2613
2634
  const listed = await client.fsListBatch(
2614
2635
  workspaceId,
2615
2636
  sessionId,
2616
- { requests: chunk.map((path) => ({ path, depth: 1 })) },
2637
+ {
2638
+ requests: chunk.map((path) => ({
2639
+ path,
2640
+ depth: 1,
2641
+ ...route ? { route } : {}
2642
+ }))
2643
+ },
2617
2644
  { signal }
2618
2645
  );
2619
2646
  if (identityGenerationRef.current !== identityGeneration) return;
@@ -2637,7 +2664,7 @@ function useSandboxFiles(sessionId, options = {}) {
2637
2664
  }
2638
2665
  }
2639
2666
  },
2640
- [client, workspaceId, sessionId, rootPath, applyStatus]
2667
+ [client, workspaceId, sessionId, rootPath, route, applyStatus]
2641
2668
  );
2642
2669
  const runOptimistic = useCallback15(
2643
2670
  async (opName, apply, op, reconcileParents) => {
@@ -2690,7 +2717,7 @@ function useSandboxFiles(sessionId, options = {}) {
2690
2717
  const live = await client.fsRead(
2691
2718
  workspaceId,
2692
2719
  sessionId,
2693
- { path },
2720
+ { path, ...route ? { route } : {} },
2694
2721
  { signal: identitySignal }
2695
2722
  );
2696
2723
  if (identityGenerationRef.current !== identityGeneration) {
@@ -2707,13 +2734,14 @@ function useSandboxFiles(sessionId, options = {}) {
2707
2734
  return await client.fsWrite(workspaceId, sessionId, {
2708
2735
  path,
2709
2736
  content,
2710
- overwrite: true
2737
+ overwrite: true,
2738
+ ...route ? { route } : {}
2711
2739
  });
2712
2740
  },
2713
2741
  exists ? [] : [parent]
2714
2742
  );
2715
2743
  },
2716
- [client, workspaceId, sessionId, tree, rootPath, runOptimistic]
2744
+ [client, workspaceId, sessionId, tree, rootPath, route, runOptimistic]
2717
2745
  );
2718
2746
  const createFile = useCallback15(
2719
2747
  async (path) => {
@@ -2731,12 +2759,13 @@ function useSandboxFiles(sessionId, options = {}) {
2731
2759
  () => client.fsWrite(workspaceId, sessionId, {
2732
2760
  path,
2733
2761
  content: "",
2734
- overwrite: false
2762
+ overwrite: false,
2763
+ ...route ? { route } : {}
2735
2764
  }),
2736
2765
  [parent]
2737
2766
  );
2738
2767
  },
2739
- [client, workspaceId, sessionId, rootPath, runOptimistic]
2768
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic]
2740
2769
  );
2741
2770
  const createDir = useCallback15(
2742
2771
  async (path) => {
@@ -2751,11 +2780,15 @@ function useSandboxFiles(sessionId, options = {}) {
2751
2780
  await runOptimistic(
2752
2781
  "create folder",
2753
2782
  (nodes) => insertNode(nodes, parent, node, rootPath),
2754
- () => client.fsMkdir(workspaceId, sessionId, { path, recursive: true }),
2783
+ () => client.fsMkdir(workspaceId, sessionId, {
2784
+ path,
2785
+ recursive: true,
2786
+ ...route ? { route } : {}
2787
+ }),
2755
2788
  [parent]
2756
2789
  );
2757
2790
  },
2758
- [client, workspaceId, sessionId, rootPath, runOptimistic]
2791
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic]
2759
2792
  );
2760
2793
  const deleteEntry = useCallback15(
2761
2794
  async (path, recursive = false) => {
@@ -2763,11 +2796,15 @@ function useSandboxFiles(sessionId, options = {}) {
2763
2796
  await runOptimistic(
2764
2797
  "delete",
2765
2798
  (nodes) => removeNode(nodes, path, rootPath),
2766
- () => client.fsDelete(workspaceId, sessionId, { path, recursive }),
2799
+ () => client.fsDelete(workspaceId, sessionId, {
2800
+ path,
2801
+ recursive,
2802
+ ...route ? { route } : {}
2803
+ }),
2767
2804
  [parentWithinTree(path, rootPath)]
2768
2805
  );
2769
2806
  },
2770
- [client, workspaceId, sessionId, rootPath, runOptimistic]
2807
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic]
2771
2808
  );
2772
2809
  const moveEntry = useCallback15(
2773
2810
  async (path, newPath, opts) => {
@@ -2785,12 +2822,13 @@ function useSandboxFiles(sessionId, options = {}) {
2785
2822
  () => client.fsMove(workspaceId, sessionId, {
2786
2823
  path,
2787
2824
  newPath,
2788
- overwrite: opts?.overwrite ?? false
2825
+ overwrite: opts?.overwrite ?? false,
2826
+ ...route ? { route } : {}
2789
2827
  }),
2790
2828
  to === from ? [from] : [from, to]
2791
2829
  );
2792
2830
  },
2793
- [client, workspaceId, sessionId, rootPath, runOptimistic]
2831
+ [client, workspaceId, sessionId, rootPath, route, runOptimistic]
2794
2832
  );
2795
2833
  const captureRef = useRef5(capture);
2796
2834
  captureRef.current = capture;
@@ -3615,7 +3653,7 @@ function useWorkspaceCapture(sessionId, options = {}) {
3615
3653
  }
3616
3654
 
3617
3655
  // src/hooks/use-workspace-edit.ts
3618
- import { useCallback as useCallback18, useEffect as useEffect9, useRef as useRef8, useState as useState7 } from "react";
3656
+ import { useCallback as useCallback18, useEffect as useEffect9, useMemo as useMemo8, useRef as useRef8, useState as useState7 } from "react";
3619
3657
  function isLive(liveness) {
3620
3658
  return sandboxAcceptsLiveIo(liveness);
3621
3659
  }
@@ -3623,9 +3661,15 @@ function useWorkspaceEdit(sessionId, options = {}) {
3623
3661
  const { client, workspaceId } = useOpenGeni(options);
3624
3662
  const path = options.path ?? null;
3625
3663
  const baseContent = options.baseContent ?? null;
3664
+ const routeEpoch = options.route?.epoch;
3665
+ const routeRoot = options.route?.root;
3666
+ const route = useMemo8(
3667
+ () => routeEpoch !== void 0 && routeRoot !== void 0 ? { epoch: routeEpoch, root: routeRoot } : void 0,
3668
+ [routeEpoch, routeRoot]
3669
+ );
3626
3670
  const live = isLive(options.liveness);
3627
3671
  const offline = options.offline === true;
3628
- const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${path ?? ""}`;
3672
+ const identityKey = `${workspaceId}\0${sessionId ?? ""}\0${path ?? ""}\0${routeEpoch ?? ""}\0${routeRoot ?? ""}`;
3629
3673
  const [buffer, setBuffer] = useState7(null);
3630
3674
  const [wantsWarm, setWantsWarm] = useState7(false);
3631
3675
  const [flush, setFlush] = useState7("idle");
@@ -3694,7 +3738,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3694
3738
  const liveRead = await client.fsRead(
3695
3739
  workspaceId,
3696
3740
  sessionId,
3697
- { path },
3741
+ { path, ...route ? { route } : {} },
3698
3742
  { signal: readAbort.signal }
3699
3743
  );
3700
3744
  if (readAbortRef.current === readAbort) readAbortRef.current = null;
@@ -3706,7 +3750,12 @@ function useWorkspaceEdit(sessionId, options = {}) {
3706
3750
  return;
3707
3751
  }
3708
3752
  }
3709
- await client.fsWrite(workspaceId, sessionId, { path, content, overwrite: true });
3753
+ await client.fsWrite(workspaceId, sessionId, {
3754
+ path,
3755
+ content,
3756
+ overwrite: true,
3757
+ ...route ? { route } : {}
3758
+ });
3710
3759
  if (identityGenerationRef.current !== identityGeneration) return;
3711
3760
  setConflict(null);
3712
3761
  setFlush("flushed");
@@ -3722,7 +3771,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3722
3771
  }
3723
3772
  }
3724
3773
  },
3725
- [client, workspaceId, sessionId, path, baseContent, stateIdentity, identityKey]
3774
+ [client, workspaceId, sessionId, path, baseContent, route, stateIdentity, identityKey]
3726
3775
  );
3727
3776
  useEffect9(() => {
3728
3777
  if (offline) return;
@@ -3774,7 +3823,7 @@ function useWorkspaceEdit(sessionId, options = {}) {
3774
3823
  }
3775
3824
 
3776
3825
  // src/hooks/use-machine-chip.ts
3777
- import { useMemo as useMemo8 } from "react";
3826
+ import { useMemo as useMemo9 } from "react";
3778
3827
  function formatAsOf(capturedAt, now) {
3779
3828
  const then = Date.parse(capturedAt);
3780
3829
  if (Number.isNaN(then)) return "recently";
@@ -3824,7 +3873,7 @@ function useMachineChip(input) {
3824
3873
  capturedAt,
3825
3874
  now
3826
3875
  } = input;
3827
- return useMemo8(
3876
+ return useMemo9(
3828
3877
  () => deriveMachineChip({
3829
3878
  liveness,
3830
3879
  capabilitiesState,
@@ -4765,7 +4814,7 @@ import {
4765
4814
  useEffect as useEffect13,
4766
4815
  useId as useId2,
4767
4816
  useLayoutEffect as useLayoutEffect2,
4768
- useMemo as useMemo10,
4817
+ useMemo as useMemo11,
4769
4818
  useRef as useRef11,
4770
4819
  useState as useState10
4771
4820
  } from "react";
@@ -4773,11 +4822,11 @@ import { AlertDialog } from "radix-ui";
4773
4822
  import { VList } from "virtua";
4774
4823
 
4775
4824
  // src/lib/use-unicode-fonts.ts
4776
- import { useEffect as useEffect12, useMemo as useMemo9 } from "react";
4825
+ import { useEffect as useEffect12, useMemo as useMemo10 } from "react";
4777
4826
  var japaneseScript = /[\u3040-\u30ff\u3400-\u4dbf\u4e00-\u9fff\uf900-\ufaff]/u;
4778
4827
  var arabicScript = /[\u0600-\u06ff\u0750-\u077f\u0870-\u08ff\ufb50-\ufdff\ufe70-\ufefc]/u;
4779
4828
  function useUnicodeFallbackFonts(values) {
4780
- const content = useMemo9(() => collectUnicodeContent(values), [values]);
4829
+ const content = useMemo10(() => collectUnicodeContent(values), [values]);
4781
4830
  useEffect12(() => {
4782
4831
  if (content.japanese) {
4783
4832
  void import("./noto-sans-jp-loader.generated-HFGLYBR3.js").then(({ loadNotoSansJp }) => loadNotoSansJp(content.japanese)).catch(() => void 0);
@@ -4901,7 +4950,7 @@ function FileBrowser({
4901
4950
  () => typeof document === "undefined" ? null : document.getElementById(treeId),
4902
4951
  [treeId]
4903
4952
  );
4904
- const visibleTree = useMemo10(
4953
+ const visibleTree = useMemo11(
4905
4954
  () => visibleFileTree(result.tree, isNodeVisible),
4906
4955
  [isNodeVisible, result.tree]
4907
4956
  );
@@ -5002,7 +5051,7 @@ function FileBrowser({
5002
5051
  [expanded, expand]
5003
5052
  );
5004
5053
  const cold = result.source !== "live";
5005
- const renderItems = useMemo10(() => {
5054
+ const renderItems = useMemo11(() => {
5006
5055
  const items = [];
5007
5056
  if (draftCreate && draftCreate.parent === "") {
5008
5057
  items.push({ type: "create", parent: "", kind: draftCreate.kind, depth: 0 });
@@ -5033,22 +5082,22 @@ function FileBrowser({
5033
5082
  walk(visibleTree, 0);
5034
5083
  return items;
5035
5084
  }, [visibleTree, expanded, draftCreate, loadingPaths, result.expandingPaths, cold]);
5036
- const unicodeFontPaths = useMemo10(
5085
+ const unicodeFontPaths = useMemo11(
5037
5086
  () => renderItems.flatMap((item) => item.type === "node" ? [item.node.path] : []),
5038
5087
  [renderItems]
5039
5088
  );
5040
5089
  useUnicodeFallbackFonts(unicodeFontPaths);
5041
- const flatRows = useMemo10(
5090
+ const flatRows = useMemo11(
5042
5091
  () => renderItems.flatMap(
5043
5092
  (item) => item.type === "node" ? [{ node: item.node, depth: item.depth }] : []
5044
5093
  ),
5045
5094
  [renderItems]
5046
5095
  );
5047
- const rowIndexByPath = useMemo10(
5096
+ const rowIndexByPath = useMemo11(
5048
5097
  () => new Map(flatRows.map((row, index) => [row.node.path, index])),
5049
5098
  [flatRows]
5050
5099
  );
5051
- const treePositionByPath = useMemo10(() => {
5100
+ const treePositionByPath = useMemo11(() => {
5052
5101
  const positions = /* @__PURE__ */ new Map();
5053
5102
  const visit = (nodes) => {
5054
5103
  nodes.forEach((node, index) => {
@@ -6009,14 +6058,14 @@ import {
6009
6058
  useEffect as useEffect15,
6010
6059
  useId as useId3,
6011
6060
  useLayoutEffect as useLayoutEffect3,
6012
- useMemo as useMemo12,
6061
+ useMemo as useMemo13,
6013
6062
  useRef as useRef13,
6014
6063
  useState as useState12
6015
6064
  } from "react";
6016
6065
  import { VList as VList2 } from "virtua";
6017
6066
 
6018
6067
  // src/hooks/use-windowed-sections.ts
6019
- import { useCallback as useCallback20, useEffect as useEffect14, useMemo as useMemo11, useRef as useRef12, useState as useState11 } from "react";
6068
+ import { useCallback as useCallback20, useEffect as useEffect14, useMemo as useMemo12, useRef as useRef12, useState as useState11 } from "react";
6020
6069
  function computeWindowRange(heights, scrollTop, viewport, overscan) {
6021
6070
  const n = heights.length;
6022
6071
  if (n === 0) return { start: 0, end: 0 };
@@ -6099,7 +6148,7 @@ function useWindowedSections(opts) {
6099
6148
  return next;
6100
6149
  });
6101
6150
  }, []);
6102
- const offsets = useMemo11(() => {
6151
+ const offsets = useMemo12(() => {
6103
6152
  const out = new Array(count + 1);
6104
6153
  out[0] = 0;
6105
6154
  for (let i = 0; i < count; i++) out[i + 1] = (out[i] ?? 0) + Math.max(heights[i] ?? 0, 0);
@@ -6317,12 +6366,12 @@ function WorkbenchChanges({
6317
6366
  const [layout, setLayout] = useState12("unified");
6318
6367
  const [activePath, setActivePath] = useState12(null);
6319
6368
  const pickerId = useId3();
6320
- const unicodeFontPaths = useMemo12(
6369
+ const unicodeFontPaths = useMemo13(
6321
6370
  () => diff.flatMap((file) => file.oldPath ? [file.path, file.oldPath] : [file.path]),
6322
6371
  [diff]
6323
6372
  );
6324
6373
  useUnicodeFallbackFonts(unicodeFontPaths);
6325
- const { orderedFiles, rows, grouped } = useMemo12(() => buildRail(diff), [diff]);
6374
+ const { orderedFiles, rows, grouped } = useMemo13(() => buildRail(diff), [diff]);
6326
6375
  const selectedIndex = orderedFiles.findIndex((file) => file.path === activePath);
6327
6376
  const activeIndex = selectedIndex >= 0 ? selectedIndex : 0;
6328
6377
  useChangesLayoutEffect(() => {
@@ -6352,8 +6401,8 @@ function WorkbenchChanges({
6352
6401
  return () => query.removeListener(update);
6353
6402
  }
6354
6403
  }, []);
6355
- const additions = useMemo12(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
6356
- const deletions = useMemo12(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
6404
+ const additions = useMemo13(() => diff.reduce((sum, f) => sum + f.additions, 0), [diff]);
6405
+ const deletions = useMemo13(() => diff.reduce((sum, f) => sum + f.deletions, 0), [diff]);
6357
6406
  const estimateHeight = useCallback21(
6358
6407
  (index) => {
6359
6408
  const file = orderedFiles[index];
@@ -6838,7 +6887,7 @@ import { Loader2Icon, SaveIcon } from "lucide-react";
6838
6887
  import {
6839
6888
  useCallback as useCallback22,
6840
6889
  useEffect as useEffect17,
6841
- useMemo as useMemo13,
6890
+ useMemo as useMemo14,
6842
6891
  useRef as useRef14,
6843
6892
  useState as useState14
6844
6893
  } from "react";
@@ -6946,7 +6995,7 @@ function CodeEditor({
6946
6995
  },
6947
6996
  [baseline, onEditIntent]
6948
6997
  );
6949
- const langKey = useMemo13(() => languageForPath(path), [path]);
6998
+ const langKey = useMemo14(() => languageForPath(path), [path]);
6950
6999
  useEffect17(() => {
6951
7000
  let cancelled = false;
6952
7001
  void (async () => {
@@ -7040,7 +7089,7 @@ function CodeEditor({
7040
7089
  return () => clearTimeout(t);
7041
7090
  }, [savedTick]);
7042
7091
  const fileName = path.split("/").filter(Boolean).pop() ?? path;
7043
- const cmExtensions = useMemo13(() => {
7092
+ const cmExtensions = useMemo14(() => {
7044
7093
  if (!bundle) return [];
7045
7094
  const exts = [bundle.saveKeymapExtension(() => saveRef.current())];
7046
7095
  if (bundle.languageExtension) exts.push(bundle.languageExtension);
@@ -8413,7 +8462,7 @@ import {
8413
8462
  Suspense as Suspense2,
8414
8463
  useCallback as useCallback25,
8415
8464
  useEffect as useEffect20,
8416
- useMemo as useMemo14,
8465
+ useMemo as useMemo15,
8417
8466
  useRef as useRef17,
8418
8467
  useState as useState17
8419
8468
  } from "react";
@@ -8453,6 +8502,11 @@ var WORKBENCH_SURFACES = [
8453
8502
  function isWorkbenchSurface(value) {
8454
8503
  return WORKBENCH_SURFACES.includes(value);
8455
8504
  }
8505
+ function isCanonicalAbsoluteSandboxPath(value) {
8506
+ if (!value) return false;
8507
+ const portable = value.replaceAll("\\", "/");
8508
+ return portable.startsWith("/") || /^[A-Za-z]:\//u.test(portable);
8509
+ }
8456
8510
  function sourceDrivenDefaultTab(hasChanges, changesEnabled, filesEnabled) {
8457
8511
  if (hasChanges && changesEnabled) return WORKBENCH_TAB_CHANGES;
8458
8512
  if (filesEnabled) return WORKBENCH_TAB_FILES;
@@ -8647,6 +8701,7 @@ function useSandboxWorkspaceTabs(options) {
8647
8701
  const liveness = capabilities?.liveness;
8648
8702
  const liveIoLiveness = liveness ?? null;
8649
8703
  const fileSystemOn = capabilities?.FileSystem.available ?? false;
8704
+ const fileSystemRoute = capabilities ? { epoch: capabilities.leaseEpoch, root: capabilities.FileSystem.root } : void 0;
8650
8705
  const fsReadOnly = capabilities?.FileSystem.readOnly ?? false;
8651
8706
  const filesEditable = fileSystemOn && !fsReadOnly && sandboxAcceptsLiveIo(liveness);
8652
8707
  const gitOn = capabilities?.Git.available ?? false;
@@ -8678,7 +8733,7 @@ function useSandboxWorkspaceTabs(options) {
8678
8733
  const captureAvailable = captureState.available;
8679
8734
  const liveWorkspaceExpected = sandboxAcceptsLiveIo(liveness);
8680
8735
  const capturePending = !liveWorkspaceExpected && (captureState.fileCount === null || captureState.loading);
8681
- const repoPaths = useMemo14(() => {
8736
+ const repoPaths = useMemo15(() => {
8682
8737
  const advertised = capabilities?.Git.repos ?? [];
8683
8738
  if (advertised.length > 0) return advertised;
8684
8739
  return captureState.capture?.repos.map((repo) => repo.root) ?? [];
@@ -8708,7 +8763,8 @@ function useSandboxWorkspaceTabs(options) {
8708
8763
  active: filesActive && !turnInFlight,
8709
8764
  // A deliberate canonical absolute-path open browses in the selected target's
8710
8765
  // advertised namespace, so the authoritative tree and link share exact paths.
8711
- ...requestedFilePath?.startsWith("/") && capabilities?.FileSystem.root ? { rootPath: capabilities.FileSystem.root } : {},
8766
+ ...isCanonicalAbsoluteSandboxPath(requestedFilePath) && capabilities?.FileSystem.root ? { rootPath: capabilities.FileSystem.root } : {},
8767
+ ...fileSystemRoute ? { route: fileSystemRoute } : {},
8712
8768
  repoPaths,
8713
8769
  liveness: liveIoLiveness,
8714
8770
  capture: captureState.capture,
@@ -8792,7 +8848,7 @@ function useSandboxWorkspaceTabs(options) {
8792
8848
  }
8793
8849
  }
8794
8850
  const defaultTab = defaultTabRef.current.value;
8795
- const tabs = useMemo14(() => {
8851
+ const tabs = useMemo15(() => {
8796
8852
  const list = [];
8797
8853
  if (changesEnabled)
8798
8854
  list.push({