@opengeni/react 1.4.4 → 1.4.5-canary.37485542607001
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 +17 -6
- package/dist/artifacts.d.ts +1 -0
- package/dist/artifacts.js +22 -15
- package/dist/artifacts.js.map +1 -1
- package/dist/{browser-viewer-5EEAS3IU.js → browser-viewer-CV23VIHB.js} +8 -6
- package/dist/{chunk-XS67YG5R.js → chunk-4O7OE7ON.js} +45 -4
- package/dist/chunk-4O7OE7ON.js.map +1 -0
- package/dist/{chunk-JHTIMBOL.js → chunk-5JSUNNQN.js} +26 -4
- package/dist/{chunk-JHTIMBOL.js.map → chunk-5JSUNNQN.js.map} +1 -1
- package/dist/{chunk-QMMCJGFI.js → chunk-6BAMPGBV.js} +6 -3
- package/dist/chunk-6BAMPGBV.js.map +1 -0
- package/dist/{chunk-I6OY3MWC.js → chunk-6FOTSVWU.js} +2 -2
- package/dist/{chunk-LVS3MH5T.js → chunk-ATIRUHBZ.js} +128 -10
- package/dist/chunk-ATIRUHBZ.js.map +1 -0
- package/dist/chunk-BOFEQC6K.js +162 -0
- package/dist/chunk-BOFEQC6K.js.map +1 -0
- package/dist/{chunk-2OQA3CPB.js → chunk-CESBPQP5.js} +19 -5
- package/dist/chunk-CESBPQP5.js.map +1 -0
- package/dist/{chunk-YVFRBXST.js → chunk-GMNGUPMX.js} +2 -2
- package/dist/{chunk-PHBFJCEU.js → chunk-I5EGRXXG.js} +110 -7
- package/dist/chunk-I5EGRXXG.js.map +1 -0
- package/dist/{chunk-PBCZL4JP.js → chunk-INIZSNXT.js} +2 -2
- package/dist/chunk-IZV5FTJC.js +55 -0
- package/dist/chunk-IZV5FTJC.js.map +1 -0
- package/dist/chunk-JDJJMBB3.js +1883 -0
- package/dist/chunk-JDJJMBB3.js.map +1 -0
- package/dist/chunk-LHF6BVM4.js +83 -0
- package/dist/chunk-LHF6BVM4.js.map +1 -0
- package/dist/{chunk-C5BF4L26.js → chunk-LSMWZSCJ.js} +7 -7
- package/dist/chunk-LTKGTXUQ.js +174 -0
- package/dist/chunk-LTKGTXUQ.js.map +1 -0
- package/dist/chunk-N7S72EXK.js +25 -0
- package/dist/chunk-N7S72EXK.js.map +1 -0
- package/dist/{chunk-CVYCLFMH.js → chunk-PEHTX3V7.js} +19 -1
- package/dist/chunk-PEHTX3V7.js.map +1 -0
- package/dist/chunk-R5R5RQQD.js +51 -0
- package/dist/chunk-R5R5RQQD.js.map +1 -0
- package/dist/{chunk-IYQCWGU5.js → chunk-RGTZ2VDR.js} +35 -62
- package/dist/chunk-RGTZ2VDR.js.map +1 -0
- package/dist/chunk-RI42H5IU.js +326 -0
- package/dist/chunk-RI42H5IU.js.map +1 -0
- package/dist/{chunk-EAOSEIXB.js → chunk-RRRS5D5T.js} +727 -1983
- package/dist/chunk-RRRS5D5T.js.map +1 -0
- package/dist/{chunk-JH7V627O.js → chunk-SHLZMAJN.js} +2 -11
- package/dist/chunk-SHLZMAJN.js.map +1 -0
- package/dist/{chunk-IUHHPMAX.js → chunk-SZOCY5VG.js} +18 -87
- package/dist/chunk-SZOCY5VG.js.map +1 -0
- package/dist/{chunk-L2J3PU7A.js → chunk-VYIPNDUZ.js} +29 -100
- package/dist/chunk-VYIPNDUZ.js.map +1 -0
- package/dist/{chunk-G6YRCIYJ.js → chunk-YK6QMGVL.js} +55 -60
- package/dist/chunk-YK6QMGVL.js.map +1 -0
- package/dist/chunk-ZIB4H5LN.js +101 -0
- package/dist/chunk-ZIB4H5LN.js.map +1 -0
- package/dist/client.d.ts +9 -3
- package/dist/clipboard.js +1 -1
- package/dist/components/browser-viewer.d.ts +6 -0
- package/dist/components/composer.d.ts +3 -69
- package/dist/components/human-input-form.d.ts +3 -32
- package/dist/components/markdown.d.ts +8 -0
- package/dist/components/model-policy-picker.d.ts +4 -37
- package/dist/components/opper-mark.d.ts +7 -0
- package/dist/components/provider-recovery-notice.d.ts +15 -0
- package/dist/components/remark-soft-line-breaks.d.ts +38 -0
- package/dist/components/sandbox-workspace.d.ts +13 -0
- package/dist/components/session-chrome.d.ts +5 -3
- package/dist/components/session-conversation.d.ts +15 -3
- package/dist/composer-messages.d.ts +74 -0
- package/dist/composer-messages.js +9 -0
- package/dist/composer.js +15 -10
- package/dist/{computer-viewer-ELNLZGHI.js → computer-viewer-K4CKYRX6.js} +4 -4
- package/dist/connect-setup.d.ts +2 -0
- package/dist/connect.d.ts +1 -1
- package/dist/connect.js +21 -6
- package/dist/connect.js.map +1 -1
- package/dist/{embedded-voice-NIL4RDX3.js → embedded-voice-K7SOZVVZ.js} +6 -4
- package/dist/{embedded-voice-NIL4RDX3.js.map → embedded-voice-K7SOZVVZ.js.map} +1 -1
- package/dist/hooks/shared-machines-list.d.ts +4 -0
- package/dist/hooks/use-client-config-flags.d.ts +6 -0
- package/dist/hooks/use-goal.d.ts +2 -1
- package/dist/hooks/use-new-session-draft.d.ts +74 -0
- package/dist/human-input-model.d.ts +40 -0
- package/dist/index.d.ts +4 -1
- package/dist/index.js +116 -73
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +5 -5
- package/dist/{latest-question-2POFPYNB.js → latest-question-27HGRPVF.js} +3 -3
- package/dist/lib/clipboard.d.ts +7 -0
- package/dist/lib/provider-recovery.d.ts +49 -0
- package/dist/lib/transient-retry.d.ts +25 -0
- package/dist/machines.js +2 -2
- package/dist/model-mark-paths.d.ts +34 -0
- package/dist/model-mark-paths.js +16 -0
- package/dist/model-picker-model.d.ts +111 -0
- package/dist/model-picker-model.js +25 -0
- package/dist/model-picker-model.js.map +1 -0
- package/dist/{model-policy-picker-menu-WRNSRFQC.js → model-policy-picker-menu-I5KPC3D6.js} +6 -4
- package/dist/model-policy-picker-menu-I5KPC3D6.js.map +1 -0
- package/dist/model-policy.d.ts +10 -0
- package/dist/model-policy.js +5 -1
- package/dist/new-session-draft.d.ts +7 -0
- package/dist/new-session-draft.js +639 -0
- package/dist/new-session-draft.js.map +1 -0
- package/dist/organization-model.d.ts +32 -0
- package/dist/organization-model.js +65 -0
- package/dist/organization-model.js.map +1 -0
- package/dist/{platform-activity-row-PJJVATTZ.js → platform-activity-row-NJ47YBXW.js} +7 -84
- package/dist/platform-activity-row-NJ47YBXW.js.map +1 -0
- package/dist/queue-presentation-model.d.ts +72 -0
- package/dist/queue-presentation-model.js +38 -0
- package/dist/queue-presentation-model.js.map +1 -0
- package/dist/{queue-surface-implementation-67FS7FUI.js → queue-surface-implementation-YPJFQ3O6.js} +24 -252
- package/dist/queue-surface-implementation-YPJFQ3O6.js.map +1 -0
- package/dist/realtime.js +5 -3
- package/dist/{sandbox-files-34CJJNOY.js → sandbox-files-2DGDO6NI.js} +5 -5
- package/dist/sandbox-files-2DGDO6NI.js.map +1 -0
- package/dist/sandbox-label-model.d.ts +23 -0
- package/dist/sandbox-label-model.js +28 -0
- package/dist/sandbox-label-model.js.map +1 -0
- package/dist/session-agents-model.d.ts +10 -0
- package/dist/session-agents-model.js +25 -0
- package/dist/session-agents-model.js.map +1 -0
- package/dist/session-attention-model.d.ts +8 -0
- package/dist/session-attention-model.js +46 -0
- package/dist/session-attention-model.js.map +1 -0
- package/dist/session-feedback-model.d.ts +20 -0
- package/dist/session-feedback-model.js +56 -0
- package/dist/session-feedback-model.js.map +1 -0
- package/dist/session-list-model.d.ts +119 -0
- package/dist/session-list-model.js +244 -0
- package/dist/session-list-model.js.map +1 -0
- package/dist/session-status-model.d.ts +19 -0
- package/dist/session-ui.js +26 -17
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +8 -6
- package/dist/testing/session-scenarios.d.ts +10 -0
- package/dist/testing/session-scenarios.js +303 -0
- package/dist/testing/session-scenarios.js.map +1 -0
- package/dist/timeline/genie-copy.d.ts +3 -0
- package/dist/timeline/genie-loading.d.ts +11 -0
- package/dist/timeline/index.d.ts +1 -1
- package/dist/timeline/notice-presentation.d.ts +7 -0
- package/dist/timeline/patch-apply-command.d.ts +18 -0
- package/dist/timeline/platform-activity-presentation.d.ts +7 -0
- package/dist/timeline/presented-image.d.ts +6 -1
- package/dist/timeline/question-nav-model.d.ts +23 -0
- package/dist/timeline/registry.d.ts +10 -1
- package/dist/timeline/retained-loaders.d.ts +1 -1
- package/dist/timeline/tool-presentation.d.ts +172 -0
- package/dist/timeline/turn-summary-model.d.ts +90 -0
- package/dist/timeline/turn-summary.d.ts +4 -74
- package/dist/timeline/work-presentation.d.ts +42 -0
- package/dist/timeline-model.d.ts +24 -0
- package/dist/timeline-model.js +152 -0
- package/dist/timeline-model.js.map +1 -0
- package/package.json +55 -3
- package/src/artifacts.ts +1 -0
- package/src/client.ts +9 -3
- package/src/components/browser-viewer.tsx +23 -2
- package/src/components/claude-mark.tsx +2 -1
- package/src/components/composer.tsx +8 -146
- package/src/components/grok-mark.tsx +2 -1
- package/src/components/human-input-form.tsx +13 -124
- package/src/components/markdown.tsx +19 -1
- package/src/components/message-timeline.tsx +45 -99
- package/src/components/model-mark.tsx +2 -1
- package/src/components/model-policy-picker-menu.tsx +30 -85
- package/src/components/model-policy-picker.tsx +20 -150
- package/src/components/opper-mark.tsx +18 -0
- package/src/components/provider-recovery-notice.tsx +41 -0
- package/src/components/queue-surface-implementation.tsx +20 -349
- package/src/components/remark-soft-line-breaks.ts +166 -0
- package/src/components/sandbox-workspace.tsx +17 -1
- package/src/components/session-chrome.tsx +114 -99
- package/src/components/session-conversation.tsx +55 -6
- package/src/components/session-status.tsx +17 -16
- package/src/composer-messages.ts +150 -0
- package/src/connect-setup.tsx +45 -11
- package/src/connect.ts +1 -1
- package/src/hooks/shared-machines-list.ts +34 -1
- package/src/hooks/use-client-config-flags.ts +8 -0
- package/src/hooks/use-goal.ts +66 -10
- package/src/hooks/use-new-session-draft.ts +813 -0
- package/src/human-input-model.ts +147 -0
- package/src/index.ts +16 -0
- package/src/lib/clipboard.ts +29 -0
- package/src/lib/format.ts +7 -0
- package/src/lib/provider-recovery.ts +202 -0
- package/src/lib/transient-retry.ts +99 -0
- package/src/model-mark-paths.ts +53 -0
- package/src/model-picker-model.ts +330 -0
- package/src/model-policy.ts +75 -5
- package/src/new-session-draft.ts +22 -0
- package/src/organization-model.ts +118 -0
- package/src/queue-presentation-model.ts +464 -0
- package/src/sandbox-label-model.ts +41 -0
- package/src/session-agents-model.ts +33 -0
- package/src/session-attention-model.ts +59 -0
- package/src/session-feedback-model.ts +95 -0
- package/src/session-list-model.ts +377 -0
- package/src/session-status-model.ts +62 -0
- package/src/testing/session-scenarios.ts +326 -0
- package/src/timeline/activity-rail.tsx +54 -24
- package/src/timeline/genie-copy.ts +19 -0
- package/src/timeline/genie-loading.tsx +15 -19
- package/src/timeline/index.ts +1 -0
- package/src/timeline/notice-presentation.ts +22 -0
- package/src/timeline/patch-apply-command.tsx +135 -0
- package/src/timeline/platform-activity-presentation.ts +122 -0
- package/src/timeline/platform-activity-row.tsx +6 -96
- package/src/timeline/presented-image.ts +16 -2
- package/src/timeline/projection.ts +12 -1
- package/src/timeline/question-nav-model.ts +28 -0
- package/src/timeline/registry.ts +11 -1
- package/src/timeline/retained-loaders.ts +9 -4
- package/src/timeline/rolling-activity.tsx +7 -19
- package/src/timeline/tool-presentation.ts +1316 -0
- package/src/timeline/tool-renderers.tsx +254 -1024
- package/src/timeline/turn-summary-model.ts +338 -0
- package/src/timeline/turn-summary.tsx +21 -326
- package/src/timeline/work-presentation.ts +139 -0
- package/src/timeline-model.ts +108 -0
- package/styles/compiled.css +17 -4
- package/styles/connect.css +4 -0
- package/styles/responsive.css +11 -2
- package/dist/chunk-2OQA3CPB.js.map +0 -1
- package/dist/chunk-CVYCLFMH.js.map +0 -1
- package/dist/chunk-EAOSEIXB.js.map +0 -1
- package/dist/chunk-G6YRCIYJ.js.map +0 -1
- package/dist/chunk-HXYOSSQO.js +0 -117
- package/dist/chunk-HXYOSSQO.js.map +0 -1
- package/dist/chunk-IUHHPMAX.js.map +0 -1
- package/dist/chunk-IYQCWGU5.js.map +0 -1
- package/dist/chunk-JH7V627O.js.map +0 -1
- package/dist/chunk-L2J3PU7A.js.map +0 -1
- package/dist/chunk-LVS3MH5T.js.map +0 -1
- package/dist/chunk-PHBFJCEU.js.map +0 -1
- package/dist/chunk-QMMCJGFI.js.map +0 -1
- package/dist/chunk-XS67YG5R.js.map +0 -1
- package/dist/platform-activity-row-PJJVATTZ.js.map +0 -1
- package/dist/queue-surface-implementation-67FS7FUI.js.map +0 -1
- /package/dist/{browser-viewer-5EEAS3IU.js.map → browser-viewer-CV23VIHB.js.map} +0 -0
- /package/dist/{chunk-I6OY3MWC.js.map → chunk-6FOTSVWU.js.map} +0 -0
- /package/dist/{chunk-YVFRBXST.js.map → chunk-GMNGUPMX.js.map} +0 -0
- /package/dist/{chunk-PBCZL4JP.js.map → chunk-INIZSNXT.js.map} +0 -0
- /package/dist/{chunk-C5BF4L26.js.map → chunk-LSMWZSCJ.js.map} +0 -0
- /package/dist/{computer-viewer-ELNLZGHI.js.map → composer-messages.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-WRNSRFQC.js.map → computer-viewer-K4CKYRX6.js.map} +0 -0
- /package/dist/{latest-question-2POFPYNB.js.map → latest-question-27HGRPVF.js.map} +0 -0
- /package/dist/{sandbox-files-34CJJNOY.js.map → model-mark-paths.js.map} +0 -0
|
@@ -0,0 +1,639 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
// src/hooks/use-new-session-draft.ts
|
|
4
|
+
import { OpenGeniApiError as OpenGeniApiError2 } from "@opengeni/sdk";
|
|
5
|
+
import { useCallback, useEffect, useRef, useState } from "react";
|
|
6
|
+
|
|
7
|
+
// src/lib/transient-retry.ts
|
|
8
|
+
import { OpenGeniApiError } from "@opengeni/sdk";
|
|
9
|
+
var TRANSIENT_RETRY_DELAYS_MS = [1e3, 2e3, 3e3, 4e3];
|
|
10
|
+
var TRANSIENT_RECONNECT_INTERVAL_MS = 3e3;
|
|
11
|
+
var OPENGENI_UPDATING_NOTICE = "Opengeni is updating. We'll reconnect automatically.";
|
|
12
|
+
var NETWORK_FAILURE = /failed to fetch|fetch failed|networkerror|load failed|network request failed/iu;
|
|
13
|
+
function isTransientServiceFailure(error) {
|
|
14
|
+
if (isAbort(error)) return false;
|
|
15
|
+
if (error instanceof OpenGeniApiError) {
|
|
16
|
+
if (error.status === 502 || error.status === 503 || error.status === 504) return true;
|
|
17
|
+
if (error.status === 500) return error.retryable;
|
|
18
|
+
return error.status === 0 && error.code === "network_error";
|
|
19
|
+
}
|
|
20
|
+
return error instanceof TypeError && NETWORK_FAILURE.test(error.message);
|
|
21
|
+
}
|
|
22
|
+
async function retryTransient(operation, options = {}) {
|
|
23
|
+
const delays = options.delaysMs ?? TRANSIENT_RETRY_DELAYS_MS;
|
|
24
|
+
const sleep = options.sleep ?? abortableSleep;
|
|
25
|
+
for (let attempt = 0; ; attempt += 1) {
|
|
26
|
+
try {
|
|
27
|
+
return await operation();
|
|
28
|
+
} catch (error) {
|
|
29
|
+
const delay = delays[attempt];
|
|
30
|
+
if (delay === void 0 || !isTransientServiceFailure(error) || options.signal?.aborted || options.shouldContinue?.() === false) {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
await sleep(delay, options.signal);
|
|
34
|
+
if (options.signal?.aborted || options.shouldContinue?.() === false) throw error;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function abortableSleep(ms, signal) {
|
|
39
|
+
return new Promise((resolve) => {
|
|
40
|
+
if (signal?.aborted) {
|
|
41
|
+
resolve();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const done = () => {
|
|
45
|
+
clearTimeout(timer);
|
|
46
|
+
signal?.removeEventListener("abort", done);
|
|
47
|
+
resolve();
|
|
48
|
+
};
|
|
49
|
+
const timer = setTimeout(done, ms);
|
|
50
|
+
signal?.addEventListener("abort", done, { once: true });
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function isAbort(error) {
|
|
54
|
+
return typeof DOMException !== "undefined" && error instanceof DOMException && error.name === "AbortError" || error instanceof Error && error.name === "AbortError";
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// src/hooks/use-new-session-draft.ts
|
|
58
|
+
function useNewSessionDraft(options) {
|
|
59
|
+
const { client, workspaceId } = options;
|
|
60
|
+
const resourceHydrationReady = options.resourceHydrationReady ?? true;
|
|
61
|
+
const suspendAutosave = options.suspendAutosave ?? false;
|
|
62
|
+
const hydrateResources = options.hydrateResources;
|
|
63
|
+
const retryDelaysMs = options.transientRetry?.delaysMs ?? TRANSIENT_RETRY_DELAYS_MS;
|
|
64
|
+
const reconnectIntervalMs = options.transientRetry?.reconnectIntervalMs ?? TRANSIENT_RECONNECT_INTERVAL_MS;
|
|
65
|
+
const [draft, setDraft] = useState(null);
|
|
66
|
+
const [loading, setLoading] = useState(true);
|
|
67
|
+
const [saving, setSaving] = useState(false);
|
|
68
|
+
const [conflict, setConflict] = useState(null);
|
|
69
|
+
const [error, setErrorState] = useState(null);
|
|
70
|
+
const errorRef = useRef(null);
|
|
71
|
+
const setError = useCallback((next) => {
|
|
72
|
+
errorRef.current = next;
|
|
73
|
+
setErrorState(next);
|
|
74
|
+
}, []);
|
|
75
|
+
const unloadedValueSignature = useRef(null);
|
|
76
|
+
const valueRef = useRef(options.value);
|
|
77
|
+
valueRef.current = options.value;
|
|
78
|
+
const onApplyRemoteRef = useRef(options.onApplyRemote);
|
|
79
|
+
onApplyRemoteRef.current = options.onApplyRemote;
|
|
80
|
+
const restoreReadyFilesRef = useRef(options.restoreReadyFiles);
|
|
81
|
+
restoreReadyFilesRef.current = options.restoreReadyFiles;
|
|
82
|
+
const draftRef = useRef(null);
|
|
83
|
+
const lastSavedSignature = useRef(null);
|
|
84
|
+
const passiveProjectionSignature = useRef(null);
|
|
85
|
+
const pendingHydratedBaselineGeneration = useRef(null);
|
|
86
|
+
const targetGeneration = useRef(0);
|
|
87
|
+
const persistenceEpoch = useRef(0);
|
|
88
|
+
const saveChain = useRef(Promise.resolve());
|
|
89
|
+
const autosaveTimer = useRef(null);
|
|
90
|
+
const conflictRef = useRef(null);
|
|
91
|
+
const loadingRef = useRef(true);
|
|
92
|
+
const activeRemoteReads = useRef(/* @__PURE__ */ new Set());
|
|
93
|
+
const targetKey = `${workspaceId}\0${clientIdentity(client)}`;
|
|
94
|
+
const targetKeyRef = useRef(targetKey);
|
|
95
|
+
const setCurrentConflict = useCallback((next) => {
|
|
96
|
+
conflictRef.current = next;
|
|
97
|
+
setConflict(next);
|
|
98
|
+
}, []);
|
|
99
|
+
const captureConflict = useCallback(
|
|
100
|
+
(cause) => {
|
|
101
|
+
if (!isNewSessionDraftConflict(cause)) return false;
|
|
102
|
+
const problem = asError(cause);
|
|
103
|
+
setCurrentConflict(problem);
|
|
104
|
+
setError(problem);
|
|
105
|
+
return true;
|
|
106
|
+
},
|
|
107
|
+
[setCurrentConflict, setError]
|
|
108
|
+
);
|
|
109
|
+
const abortActiveRemoteReads = useCallback(() => {
|
|
110
|
+
for (const controller of activeRemoteReads.current) controller.abort();
|
|
111
|
+
activeRemoteReads.current.clear();
|
|
112
|
+
}, []);
|
|
113
|
+
const validateRemoteDraft = useCallback(
|
|
114
|
+
async (remote, generation, signal) => {
|
|
115
|
+
if (!resourceHydrationReady || signal.aborted) return null;
|
|
116
|
+
const hydratedResources = hydrateResources ? await hydrateResources(remote.resources) : remote.resources;
|
|
117
|
+
if (generation !== targetGeneration.current || signal.aborted) return null;
|
|
118
|
+
const seen = /* @__PURE__ */ new Set();
|
|
119
|
+
const fileRefs = hydratedResources.flatMap((resource) => {
|
|
120
|
+
if (resource.kind !== "file" || seen.has(resource.fileId)) return [];
|
|
121
|
+
seen.add(resource.fileId);
|
|
122
|
+
return [resource];
|
|
123
|
+
});
|
|
124
|
+
const settled = await Promise.allSettled(
|
|
125
|
+
fileRefs.map(
|
|
126
|
+
(resource) => retryTransient(() => client.getFile(workspaceId, resource.fileId, { signal }), {
|
|
127
|
+
signal,
|
|
128
|
+
delaysMs: retryDelaysMs
|
|
129
|
+
})
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
if (generation !== targetGeneration.current || signal.aborted) return null;
|
|
133
|
+
const files = settled.flatMap((result, index) => {
|
|
134
|
+
if (result.status !== "fulfilled") return [];
|
|
135
|
+
const file = result.value;
|
|
136
|
+
const expected = fileRefs[index]?.fileId;
|
|
137
|
+
return file.id === expected && file.workspaceId === workspaceId && file.status === "ready" ? [file] : [];
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
draft: remote,
|
|
141
|
+
editable: {
|
|
142
|
+
text: remote.text,
|
|
143
|
+
resources: [
|
|
144
|
+
...hydratedResources.filter((resource) => resource.kind === "repository"),
|
|
145
|
+
...files.map(
|
|
146
|
+
(file) => ({
|
|
147
|
+
kind: "file",
|
|
148
|
+
fileId: file.id
|
|
149
|
+
})
|
|
150
|
+
)
|
|
151
|
+
],
|
|
152
|
+
tools: remote.tools,
|
|
153
|
+
toolsProvided: remote.toolsProvided,
|
|
154
|
+
model: remote.model,
|
|
155
|
+
reasoningEffort: remote.reasoningEffort,
|
|
156
|
+
latencyMode: remote.latencyMode ?? "standard",
|
|
157
|
+
...remote.modelProvided !== void 0 ? { modelProvided: remote.modelProvided } : {},
|
|
158
|
+
...Object.hasOwn(remote, "selectedProjectChannelId") ? { selectedProjectChannelId: remote.selectedProjectChannelId } : {},
|
|
159
|
+
options: remote.options
|
|
160
|
+
},
|
|
161
|
+
files
|
|
162
|
+
};
|
|
163
|
+
},
|
|
164
|
+
[client, hydrateResources, resourceHydrationReady, retryDelaysMs, workspaceId]
|
|
165
|
+
);
|
|
166
|
+
const readRemote = useCallback(
|
|
167
|
+
async (quietRetries = true) => {
|
|
168
|
+
const controller = new AbortController();
|
|
169
|
+
activeRemoteReads.current.add(controller);
|
|
170
|
+
const generation = targetGeneration.current;
|
|
171
|
+
try {
|
|
172
|
+
const remote = normalizeLegacyNewSessionDraft(
|
|
173
|
+
await retryTransient(
|
|
174
|
+
() => client.getNewSessionDraft(workspaceId, { signal: controller.signal }),
|
|
175
|
+
{
|
|
176
|
+
signal: controller.signal,
|
|
177
|
+
shouldContinue: () => generation === targetGeneration.current,
|
|
178
|
+
delaysMs: quietRetries ? retryDelaysMs : []
|
|
179
|
+
}
|
|
180
|
+
)
|
|
181
|
+
);
|
|
182
|
+
if (generation !== targetGeneration.current || controller.signal.aborted) return null;
|
|
183
|
+
return await validateRemoteDraft(remote, generation, controller.signal);
|
|
184
|
+
} finally {
|
|
185
|
+
activeRemoteReads.current.delete(controller);
|
|
186
|
+
}
|
|
187
|
+
},
|
|
188
|
+
[client, retryDelaysMs, validateRemoteDraft, workspaceId]
|
|
189
|
+
);
|
|
190
|
+
const applyRemote = useCallback(
|
|
191
|
+
(remote) => {
|
|
192
|
+
draftRef.current = remote.draft;
|
|
193
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
194
|
+
passiveProjectionSignature.current = null;
|
|
195
|
+
pendingHydratedBaselineGeneration.current = targetGeneration.current;
|
|
196
|
+
unloadedValueSignature.current = null;
|
|
197
|
+
setDraft(remote.draft);
|
|
198
|
+
setCurrentConflict(null);
|
|
199
|
+
setError(null);
|
|
200
|
+
restoreReadyFilesRef.current(remote.files);
|
|
201
|
+
onApplyRemoteRef.current(remote.editable, remote.draft.selectionHistory);
|
|
202
|
+
},
|
|
203
|
+
[setCurrentConflict, setError]
|
|
204
|
+
);
|
|
205
|
+
const reload = useCallback(async () => {
|
|
206
|
+
if (!resourceHydrationReady) return;
|
|
207
|
+
const generation = targetGeneration.current;
|
|
208
|
+
const valueAtStart = draftSignature(valueRef.current);
|
|
209
|
+
loadingRef.current = true;
|
|
210
|
+
setLoading(true);
|
|
211
|
+
try {
|
|
212
|
+
const remote = await readRemote();
|
|
213
|
+
if (remote && generation === targetGeneration.current) applyRemote(remote);
|
|
214
|
+
} catch (cause) {
|
|
215
|
+
if (generation === targetGeneration.current) {
|
|
216
|
+
if (!draftRef.current) unloadedValueSignature.current = valueAtStart;
|
|
217
|
+
setError(asError(cause));
|
|
218
|
+
}
|
|
219
|
+
} finally {
|
|
220
|
+
if (generation === targetGeneration.current) {
|
|
221
|
+
loadingRef.current = false;
|
|
222
|
+
setLoading(false);
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}, [applyRemote, readRemote, resourceHydrationReady, setError]);
|
|
226
|
+
const reloadRef = useRef(reload);
|
|
227
|
+
reloadRef.current = reload;
|
|
228
|
+
useEffect(() => {
|
|
229
|
+
if (targetKeyRef.current !== targetKey) {
|
|
230
|
+
targetKeyRef.current = targetKey;
|
|
231
|
+
}
|
|
232
|
+
targetGeneration.current += 1;
|
|
233
|
+
abortActiveRemoteReads();
|
|
234
|
+
persistenceEpoch.current += 1;
|
|
235
|
+
draftRef.current = null;
|
|
236
|
+
lastSavedSignature.current = null;
|
|
237
|
+
passiveProjectionSignature.current = null;
|
|
238
|
+
pendingHydratedBaselineGeneration.current = null;
|
|
239
|
+
unloadedValueSignature.current = null;
|
|
240
|
+
saveChain.current = Promise.resolve();
|
|
241
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
242
|
+
autosaveTimer.current = null;
|
|
243
|
+
setDraft(null);
|
|
244
|
+
setCurrentConflict(null);
|
|
245
|
+
setError(null);
|
|
246
|
+
setSaving(false);
|
|
247
|
+
loadingRef.current = true;
|
|
248
|
+
setLoading(true);
|
|
249
|
+
return () => {
|
|
250
|
+
targetGeneration.current += 1;
|
|
251
|
+
abortActiveRemoteReads();
|
|
252
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
253
|
+
autosaveTimer.current = null;
|
|
254
|
+
};
|
|
255
|
+
}, [abortActiveRemoteReads, setCurrentConflict, setError, targetKey]);
|
|
256
|
+
useEffect(() => {
|
|
257
|
+
if (resourceHydrationReady) void reloadRef.current();
|
|
258
|
+
}, [resourceHydrationReady, targetKey]);
|
|
259
|
+
const persistSnapshot = useCallback(
|
|
260
|
+
(snapshot, force = false) => {
|
|
261
|
+
const generation = targetGeneration.current;
|
|
262
|
+
const epoch = persistenceEpoch.current;
|
|
263
|
+
const signature = draftSignature(snapshot);
|
|
264
|
+
const run = async () => {
|
|
265
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
const current = draftRef.current;
|
|
269
|
+
if (!current) return null;
|
|
270
|
+
if (!force && signature === lastSavedSignature.current) {
|
|
271
|
+
return { revision: current.revision, signature };
|
|
272
|
+
}
|
|
273
|
+
setSaving(true);
|
|
274
|
+
try {
|
|
275
|
+
const save = () => client.saveNewSessionDraft(workspaceId, {
|
|
276
|
+
...snapshot,
|
|
277
|
+
expectedRevision: current.revision
|
|
278
|
+
});
|
|
279
|
+
const saved = force ? await retryTransient(save, {
|
|
280
|
+
shouldContinue: () => generation === targetGeneration.current && epoch === persistenceEpoch.current,
|
|
281
|
+
delaysMs: retryDelaysMs
|
|
282
|
+
}) : await save();
|
|
283
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
284
|
+
return null;
|
|
285
|
+
}
|
|
286
|
+
draftRef.current = saved;
|
|
287
|
+
lastSavedSignature.current = signature;
|
|
288
|
+
passiveProjectionSignature.current = null;
|
|
289
|
+
setDraft(saved);
|
|
290
|
+
setCurrentConflict(null);
|
|
291
|
+
setError(null);
|
|
292
|
+
return { revision: saved.revision, signature };
|
|
293
|
+
} catch (cause) {
|
|
294
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
295
|
+
return null;
|
|
296
|
+
}
|
|
297
|
+
const problem = asError(cause);
|
|
298
|
+
if (isNewSessionDraftConflict(cause)) setCurrentConflict(problem);
|
|
299
|
+
setError(problem);
|
|
300
|
+
return null;
|
|
301
|
+
} finally {
|
|
302
|
+
if (generation === targetGeneration.current && epoch === persistenceEpoch.current) {
|
|
303
|
+
setSaving(false);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const operation = saveChain.current.then(run, run);
|
|
308
|
+
saveChain.current = operation.then(
|
|
309
|
+
() => void 0,
|
|
310
|
+
() => void 0
|
|
311
|
+
);
|
|
312
|
+
return operation;
|
|
313
|
+
},
|
|
314
|
+
[client, retryDelaysMs, setCurrentConflict, setError, workspaceId]
|
|
315
|
+
);
|
|
316
|
+
const valueSignature = draftSignature(options.value);
|
|
317
|
+
useEffect(() => {
|
|
318
|
+
if (!loading && draftRef.current && pendingHydratedBaselineGeneration.current === targetGeneration.current) {
|
|
319
|
+
pendingHydratedBaselineGeneration.current = null;
|
|
320
|
+
passiveProjectionSignature.current = valueSignature;
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
if (loading || suspendAutosave || !draftRef.current || conflictRef.current || valueSignature === lastSavedSignature.current || valueSignature === passiveProjectionSignature.current) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
passiveProjectionSignature.current = null;
|
|
327
|
+
const snapshot = cloneEditable(valueRef.current);
|
|
328
|
+
autosaveTimer.current = setTimeout(() => {
|
|
329
|
+
autosaveTimer.current = null;
|
|
330
|
+
void persistSnapshot(snapshot);
|
|
331
|
+
}, 500);
|
|
332
|
+
return () => {
|
|
333
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
334
|
+
autosaveTimer.current = null;
|
|
335
|
+
};
|
|
336
|
+
}, [loading, persistSnapshot, suspendAutosave, valueSignature]);
|
|
337
|
+
const flush = useCallback(async () => {
|
|
338
|
+
if (loadingRef.current || conflictRef.current || !draftRef.current) return null;
|
|
339
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
340
|
+
autosaveTimer.current = null;
|
|
341
|
+
return await persistSnapshot(cloneEditable(valueRef.current));
|
|
342
|
+
}, [persistSnapshot]);
|
|
343
|
+
const flushForSend = useCallback(
|
|
344
|
+
async (sendSnapshot) => {
|
|
345
|
+
if (loadingRef.current) return null;
|
|
346
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
347
|
+
autosaveTimer.current = null;
|
|
348
|
+
const snapshot = cloneEditable(sendSnapshot ?? valueRef.current);
|
|
349
|
+
const generation = targetGeneration.current;
|
|
350
|
+
if (!draftRef.current) {
|
|
351
|
+
try {
|
|
352
|
+
const remote = await readRemote();
|
|
353
|
+
if (!remote || generation !== targetGeneration.current) return null;
|
|
354
|
+
draftRef.current = remote.draft;
|
|
355
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
356
|
+
setDraft(remote.draft);
|
|
357
|
+
} catch (cause) {
|
|
358
|
+
if (generation === targetGeneration.current) setError(asError(cause));
|
|
359
|
+
return null;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
363
|
+
if (generation !== targetGeneration.current) return null;
|
|
364
|
+
if (conflictRef.current) {
|
|
365
|
+
await saveChain.current;
|
|
366
|
+
try {
|
|
367
|
+
const remote = await readRemote();
|
|
368
|
+
if (!remote || generation !== targetGeneration.current) return null;
|
|
369
|
+
draftRef.current = remote.draft;
|
|
370
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
371
|
+
passiveProjectionSignature.current = null;
|
|
372
|
+
setDraft(remote.draft);
|
|
373
|
+
setCurrentConflict(null);
|
|
374
|
+
setError(null);
|
|
375
|
+
} catch (cause) {
|
|
376
|
+
if (generation === targetGeneration.current) setError(asError(cause));
|
|
377
|
+
return null;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
const flushed = await persistSnapshot(snapshot, true);
|
|
381
|
+
if (flushed) return flushed;
|
|
382
|
+
if (!conflictRef.current) return null;
|
|
383
|
+
}
|
|
384
|
+
return null;
|
|
385
|
+
},
|
|
386
|
+
[persistSnapshot, readRemote, setCurrentConflict, setError]
|
|
387
|
+
);
|
|
388
|
+
const isCurrentSignature = useCallback(
|
|
389
|
+
(signature) => draftSignature(valueRef.current) === signature,
|
|
390
|
+
[]
|
|
391
|
+
);
|
|
392
|
+
const acknowledgeConsumed = useCallback(
|
|
393
|
+
async (flushed, visibleSignature = flushed.signature) => {
|
|
394
|
+
const current = draftRef.current;
|
|
395
|
+
if (!current || current.revision !== flushed.revision || lastSavedSignature.current !== flushed.signature) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
if (autosaveTimer.current) clearTimeout(autosaveTimer.current);
|
|
399
|
+
autosaveTimer.current = null;
|
|
400
|
+
const generation = targetGeneration.current;
|
|
401
|
+
const epoch = persistenceEpoch.current + 1;
|
|
402
|
+
persistenceEpoch.current = epoch;
|
|
403
|
+
const priorSaves = saveChain.current;
|
|
404
|
+
setCurrentConflict(null);
|
|
405
|
+
setError(null);
|
|
406
|
+
const run = async () => {
|
|
407
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
408
|
+
return null;
|
|
409
|
+
}
|
|
410
|
+
setSaving(true);
|
|
411
|
+
try {
|
|
412
|
+
await priorSaves;
|
|
413
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
414
|
+
return null;
|
|
415
|
+
}
|
|
416
|
+
const remote = await readRemote();
|
|
417
|
+
if (!remote || generation !== targetGeneration.current) return null;
|
|
418
|
+
const expectedSeedRevision = flushed.revision + 1;
|
|
419
|
+
if (remote.draft.revision > expectedSeedRevision) {
|
|
420
|
+
const problem = new Error(
|
|
421
|
+
"New-session defaults changed in another client while the session was created"
|
|
422
|
+
);
|
|
423
|
+
draftRef.current = remote.draft;
|
|
424
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
425
|
+
passiveProjectionSignature.current = null;
|
|
426
|
+
setDraft(remote.draft);
|
|
427
|
+
setCurrentConflict(problem);
|
|
428
|
+
setError(problem);
|
|
429
|
+
return null;
|
|
430
|
+
}
|
|
431
|
+
if (remote.draft.revision < expectedSeedRevision) {
|
|
432
|
+
const problem = new Error("New-session defaults were not seeded yet");
|
|
433
|
+
setError(problem);
|
|
434
|
+
return null;
|
|
435
|
+
}
|
|
436
|
+
draftRef.current = remote.draft;
|
|
437
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
438
|
+
passiveProjectionSignature.current = null;
|
|
439
|
+
setDraft(remote.draft);
|
|
440
|
+
const snapshot = cloneEditable(valueRef.current);
|
|
441
|
+
const signature = draftSignature(snapshot);
|
|
442
|
+
if (signature === visibleSignature) {
|
|
443
|
+
draftRef.current = null;
|
|
444
|
+
lastSavedSignature.current = null;
|
|
445
|
+
passiveProjectionSignature.current = null;
|
|
446
|
+
setDraft(null);
|
|
447
|
+
return { kind: "consumed" };
|
|
448
|
+
}
|
|
449
|
+
const saved = await client.saveNewSessionDraft(workspaceId, {
|
|
450
|
+
...snapshot,
|
|
451
|
+
expectedRevision: remote.draft.revision
|
|
452
|
+
});
|
|
453
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
454
|
+
return null;
|
|
455
|
+
}
|
|
456
|
+
draftRef.current = saved;
|
|
457
|
+
lastSavedSignature.current = signature;
|
|
458
|
+
passiveProjectionSignature.current = null;
|
|
459
|
+
setDraft(saved);
|
|
460
|
+
setCurrentConflict(null);
|
|
461
|
+
setError(null);
|
|
462
|
+
return {
|
|
463
|
+
kind: "preserved",
|
|
464
|
+
flushed: { revision: saved.revision, signature }
|
|
465
|
+
};
|
|
466
|
+
} catch (cause) {
|
|
467
|
+
if (generation !== targetGeneration.current || epoch !== persistenceEpoch.current) {
|
|
468
|
+
return null;
|
|
469
|
+
}
|
|
470
|
+
const problem = asError(cause);
|
|
471
|
+
if (isNewSessionDraftConflict(cause)) setCurrentConflict(problem);
|
|
472
|
+
setError(problem);
|
|
473
|
+
return null;
|
|
474
|
+
} finally {
|
|
475
|
+
if (generation === targetGeneration.current && epoch === persistenceEpoch.current) {
|
|
476
|
+
setSaving(false);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
};
|
|
480
|
+
const operation = saveChain.current.then(run, run);
|
|
481
|
+
saveChain.current = operation.then(
|
|
482
|
+
() => void 0,
|
|
483
|
+
() => void 0
|
|
484
|
+
);
|
|
485
|
+
return await operation;
|
|
486
|
+
},
|
|
487
|
+
[client, readRemote, setCurrentConflict, setError, workspaceId]
|
|
488
|
+
);
|
|
489
|
+
const resolveConflict = useCallback(
|
|
490
|
+
async (choice) => {
|
|
491
|
+
if (choice === "use_remote") {
|
|
492
|
+
await reload();
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
const mine = cloneEditable(valueRef.current);
|
|
496
|
+
const generation = targetGeneration.current;
|
|
497
|
+
loadingRef.current = true;
|
|
498
|
+
setLoading(true);
|
|
499
|
+
try {
|
|
500
|
+
const remote = await readRemote();
|
|
501
|
+
if (!remote || generation !== targetGeneration.current) return;
|
|
502
|
+
draftRef.current = remote.draft;
|
|
503
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
504
|
+
passiveProjectionSignature.current = null;
|
|
505
|
+
setDraft(remote.draft);
|
|
506
|
+
setCurrentConflict(null);
|
|
507
|
+
await persistSnapshot(mine);
|
|
508
|
+
} catch (cause) {
|
|
509
|
+
if (generation === targetGeneration.current) setError(asError(cause));
|
|
510
|
+
} finally {
|
|
511
|
+
if (generation === targetGeneration.current) {
|
|
512
|
+
loadingRef.current = false;
|
|
513
|
+
setLoading(false);
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
},
|
|
517
|
+
[persistSnapshot, readRemote, reload, setCurrentConflict, setError]
|
|
518
|
+
);
|
|
519
|
+
const unavailable = error !== null && conflict === null && isTransientServiceFailure(error);
|
|
520
|
+
const reconnect = useCallback(async () => {
|
|
521
|
+
if (!resourceHydrationReady) return;
|
|
522
|
+
const generation = targetGeneration.current;
|
|
523
|
+
try {
|
|
524
|
+
const remote = await readRemote(false);
|
|
525
|
+
if (!remote || generation !== targetGeneration.current) return;
|
|
526
|
+
if (!draftRef.current && unloadedValueSignature.current !== null && unloadedValueSignature.current === draftSignature(valueRef.current)) {
|
|
527
|
+
applyRemote(remote);
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
530
|
+
unloadedValueSignature.current = null;
|
|
531
|
+
draftRef.current = remote.draft;
|
|
532
|
+
lastSavedSignature.current = draftSignature(remote.editable);
|
|
533
|
+
passiveProjectionSignature.current = null;
|
|
534
|
+
setDraft(remote.draft);
|
|
535
|
+
setError(null);
|
|
536
|
+
} catch (cause) {
|
|
537
|
+
if (generation === targetGeneration.current) setError(asError(cause));
|
|
538
|
+
}
|
|
539
|
+
}, [applyRemote, readRemote, resourceHydrationReady, setError]);
|
|
540
|
+
const reconnectRef = useRef(reconnect);
|
|
541
|
+
reconnectRef.current = reconnect;
|
|
542
|
+
useEffect(() => {
|
|
543
|
+
if (!unavailable) return;
|
|
544
|
+
let stopped = false;
|
|
545
|
+
let timer;
|
|
546
|
+
const tick = () => {
|
|
547
|
+
timer = setTimeout(() => {
|
|
548
|
+
if (stopped) return;
|
|
549
|
+
void reconnectRef.current().finally(() => {
|
|
550
|
+
if (!stopped) tick();
|
|
551
|
+
});
|
|
552
|
+
}, reconnectIntervalMs);
|
|
553
|
+
};
|
|
554
|
+
tick();
|
|
555
|
+
return () => {
|
|
556
|
+
stopped = true;
|
|
557
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
558
|
+
};
|
|
559
|
+
}, [unavailable, reconnectIntervalMs, targetKey]);
|
|
560
|
+
return {
|
|
561
|
+
draft,
|
|
562
|
+
revision: draft?.revision ?? 0,
|
|
563
|
+
loading,
|
|
564
|
+
saving,
|
|
565
|
+
conflict,
|
|
566
|
+
error,
|
|
567
|
+
unavailable,
|
|
568
|
+
currentError: useCallback(() => errorRef.current, []),
|
|
569
|
+
reportUnavailable: useCallback(
|
|
570
|
+
(cause) => {
|
|
571
|
+
if (!isTransientServiceFailure(cause)) return false;
|
|
572
|
+
setError(asError(cause));
|
|
573
|
+
return true;
|
|
574
|
+
},
|
|
575
|
+
[setError]
|
|
576
|
+
),
|
|
577
|
+
flush,
|
|
578
|
+
flushForSend,
|
|
579
|
+
isCurrentSignature,
|
|
580
|
+
acknowledgeConsumed,
|
|
581
|
+
reload,
|
|
582
|
+
captureConflict,
|
|
583
|
+
resolveConflict,
|
|
584
|
+
clearError: useCallback(() => {
|
|
585
|
+
setError(null);
|
|
586
|
+
setCurrentConflict(null);
|
|
587
|
+
}, [setCurrentConflict, setError])
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
function cloneEditable(value) {
|
|
591
|
+
return structuredClone(value);
|
|
592
|
+
}
|
|
593
|
+
function normalizeLegacyNewSessionDraft(remote) {
|
|
594
|
+
return {
|
|
595
|
+
...remote,
|
|
596
|
+
toolsProvided: Object.hasOwn(remote, "toolsProvided") ? remote.toolsProvided : true,
|
|
597
|
+
latencyMode: remote.latencyMode ?? "standard",
|
|
598
|
+
selectionHistory: remote.selectionHistory ?? { projects: [] }
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
function draftSignature(value) {
|
|
602
|
+
return JSON.stringify(sortedJson(value));
|
|
603
|
+
}
|
|
604
|
+
function sortedJson(value) {
|
|
605
|
+
if (Array.isArray(value)) return value.map(sortedJson);
|
|
606
|
+
if (value && typeof value === "object") {
|
|
607
|
+
return Object.fromEntries(
|
|
608
|
+
Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, nested]) => [key, sortedJson(nested)])
|
|
609
|
+
);
|
|
610
|
+
}
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
function clientIdentity(client) {
|
|
614
|
+
let identity = clientIdentities.get(client);
|
|
615
|
+
if (identity === void 0) {
|
|
616
|
+
identity = nextClientIdentity;
|
|
617
|
+
nextClientIdentity += 1;
|
|
618
|
+
clientIdentities.set(client, identity);
|
|
619
|
+
}
|
|
620
|
+
return identity;
|
|
621
|
+
}
|
|
622
|
+
var clientIdentities = /* @__PURE__ */ new WeakMap();
|
|
623
|
+
var nextClientIdentity = 1;
|
|
624
|
+
function isNewSessionDraftConflict(cause) {
|
|
625
|
+
return cause instanceof OpenGeniApiError2 && cause.status === 409 && cause.code === "NEW_SESSION_DRAFT_CONFLICT";
|
|
626
|
+
}
|
|
627
|
+
function asError(cause) {
|
|
628
|
+
return cause instanceof Error ? cause : new Error(String(cause));
|
|
629
|
+
}
|
|
630
|
+
export {
|
|
631
|
+
OPENGENI_UPDATING_NOTICE,
|
|
632
|
+
TRANSIENT_RECONNECT_INTERVAL_MS,
|
|
633
|
+
TRANSIENT_RETRY_DELAYS_MS,
|
|
634
|
+
isNewSessionDraftConflict,
|
|
635
|
+
isTransientServiceFailure,
|
|
636
|
+
retryTransient,
|
|
637
|
+
useNewSessionDraft
|
|
638
|
+
};
|
|
639
|
+
//# sourceMappingURL=new-session-draft.js.map
|