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