@opengeni/react 7.6.1 → 7.7.0-canary.37090756480001
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 +19 -2
- package/dist/{browser-viewer-5X47I6T2.js → browser-viewer-63TXMINB.js} +6 -5
- package/dist/{chunk-UINDK6C3.js → chunk-2QUSQVF2.js} +3 -3
- package/dist/{chunk-CHPKSLPN.js → chunk-6ZSWSH4I.js} +3 -1
- package/dist/chunk-6ZSWSH4I.js.map +1 -0
- package/dist/{chunk-KGENSZKS.js → chunk-AAG4R3QN.js} +4 -3
- package/dist/chunk-AAG4R3QN.js.map +1 -0
- package/dist/{chunk-Z3DISEQN.js → chunk-CWWKTNKK.js} +35 -18
- package/dist/chunk-CWWKTNKK.js.map +1 -0
- package/dist/{chunk-5EXGU7UF.js → chunk-DOJWJQ3F.js} +3 -3
- package/dist/{chunk-WZ55DVGK.js → chunk-ELCE5SXV.js} +8 -4
- package/dist/chunk-ELCE5SXV.js.map +1 -0
- package/dist/{chunk-YSMMDOOE.js → chunk-GISEVGS6.js} +10 -2
- package/dist/chunk-GISEVGS6.js.map +1 -0
- package/dist/{chunk-YRYF2GLG.js → chunk-HNFCAQ5R.js} +39 -31
- package/dist/chunk-HNFCAQ5R.js.map +1 -0
- package/dist/{chunk-BI57CQUV.js → chunk-I7IWRQLN.js} +2 -2
- package/dist/{chunk-XA6R7DJQ.js → chunk-IQUCRAT7.js} +3 -3
- package/dist/{chunk-DV2DBNPQ.js → chunk-Q5QBEZIQ.js} +48 -29
- package/dist/chunk-Q5QBEZIQ.js.map +1 -0
- package/dist/{chunk-FGBCYQOT.js → chunk-RDKBXMWX.js} +120 -74
- package/dist/chunk-RDKBXMWX.js.map +1 -0
- package/dist/{chunk-45GE35O7.js → chunk-TKB4SB44.js} +10 -2
- package/dist/chunk-TKB4SB44.js.map +1 -0
- package/dist/{chunk-YCGBREUV.js → chunk-TOAH2BKA.js} +6 -2
- package/dist/{chunk-YCGBREUV.js.map → chunk-TOAH2BKA.js.map} +1 -1
- package/dist/chunk-UGLEYEMP.js +33 -0
- package/dist/chunk-UGLEYEMP.js.map +1 -0
- package/dist/{chunk-UJRYEWZI.js → chunk-UTRTG6LH.js} +48 -8
- package/dist/chunk-UTRTG6LH.js.map +1 -0
- package/dist/{chunk-VGIKFIO2.js → chunk-XOAMRDDK.js} +6 -3
- package/dist/chunk-XOAMRDDK.js.map +1 -0
- package/dist/{chunk-6NEZPF53.js → chunk-YN7I3STW.js} +153 -32
- package/dist/chunk-YN7I3STW.js.map +1 -0
- package/dist/{chunk-KZ73RL76.js → chunk-YQEAA6LT.js} +7 -3
- package/dist/chunk-YQEAA6LT.js.map +1 -0
- package/dist/{chunk-LZ3KHA55.js → chunk-ZYALTAEQ.js} +329 -339
- package/dist/chunk-ZYALTAEQ.js.map +1 -0
- package/dist/client.d.ts +4 -1
- package/dist/commands/types.d.ts +2 -0
- package/dist/components/approval-surface.d.ts +15 -0
- package/dist/composer.js +8 -7
- package/dist/{computer-viewer-7GF35SNY.js → computer-viewer-X2MBRZY4.js} +5 -4
- package/dist/connect.js +3 -3
- package/dist/hooks/use-browser-session.d.ts +4 -0
- package/dist/hooks/use-composer.d.ts +2 -0
- package/dist/hooks/use-computer-session.d.ts +2 -0
- package/dist/hooks/use-file-attachments.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/hooks/use-workspace-sessions.d.ts +15 -5
- package/dist/index.d.ts +2 -2
- package/dist/index.js +28 -21
- package/dist/index.js.map +1 -1
- package/dist/interaction.d.ts +1 -1
- package/dist/interaction.js +6 -5
- package/dist/interaction.js.map +1 -1
- package/dist/{latest-question-3BJGKQQ6.js → latest-question-CNEZ5KAN.js} +3 -3
- package/dist/lib/error-message.d.ts +7 -0
- package/dist/lib/format.d.ts +27 -3
- package/dist/lib/interaction-errors.d.ts +3 -0
- package/dist/machines.js +5 -4
- package/dist/{model-policy-picker-menu-PUTA5JOB.js → model-policy-picker-menu-JSA64TNN.js} +4 -4
- package/dist/model-policy.js +1 -1
- package/dist/provider.d.ts +5 -1
- package/dist/{queue-surface-implementation-2CAQAIHM.js → queue-surface-implementation-HENWYFWA.js} +3 -2
- package/dist/{queue-surface-implementation-2CAQAIHM.js.map → queue-surface-implementation-HENWYFWA.js.map} +1 -1
- package/dist/realtime.js +2 -2
- package/dist/{sandbox-files-AEJTWGT4.js → sandbox-files-BBC47SLD.js} +4 -3
- package/dist/session-ui.d.ts +1 -1
- package/dist/session-ui.js +13 -12
- package/dist/session-ui.js.map +1 -1
- package/dist/session.js +7 -6
- package/dist/usage.js +2 -1
- package/dist/usage.js.map +1 -1
- package/package.json +3 -3
- package/src/client.ts +5 -1
- package/src/commands/registry.ts +18 -15
- package/src/commands/types.ts +2 -0
- package/src/components/approval-surface.tsx +166 -42
- package/src/components/browser-viewer.tsx +38 -9
- package/src/components/composer.tsx +9 -2
- package/src/components/computer-viewer.tsx +160 -30
- package/src/components/generated-video-player.tsx +4 -2
- package/src/components/human-input-form.tsx +18 -5
- package/src/components/message-timeline.tsx +120 -168
- package/src/components/open-geni-chat.tsx +11 -4
- package/src/components/queue-surface-state.tsx +3 -1
- package/src/components/session-chrome.tsx +5 -2
- package/src/components/session-commands-panel.tsx +3 -1
- package/src/components/session-conversation.tsx +4 -2
- package/src/components/session-list.tsx +7 -5
- package/src/components/session-mcp-capability-card.tsx +11 -1
- package/src/hooks/internal.ts +3 -2
- package/src/hooks/use-browser-session.ts +115 -79
- package/src/hooks/use-composer.ts +70 -31
- package/src/hooks/use-computer-session.ts +57 -6
- package/src/hooks/use-file-attachments.ts +16 -3
- package/src/hooks/use-goal.ts +2 -1
- package/src/hooks/use-session-capabilities.ts +3 -2
- package/src/hooks/use-session-events.ts +15 -8
- package/src/hooks/use-slash-commands.ts +6 -11
- package/src/hooks/use-turn-queue.ts +2 -1
- package/src/hooks/use-workspace-sessions.ts +40 -7
- package/src/index.ts +7 -2
- package/src/interaction.ts +1 -0
- package/src/lib/error-message.ts +37 -0
- package/src/lib/format.ts +73 -9
- package/src/lib/interaction-errors.ts +15 -0
- package/src/model-policy.ts +5 -0
- package/src/provider.tsx +12 -4
- package/src/session-ui.ts +1 -1
- package/src/timeline/projection.ts +12 -1
- package/styles/compiled.css +14 -30
- package/dist/chunk-45GE35O7.js.map +0 -1
- package/dist/chunk-6NEZPF53.js.map +0 -1
- package/dist/chunk-CHPKSLPN.js.map +0 -1
- package/dist/chunk-DV2DBNPQ.js.map +0 -1
- package/dist/chunk-FGBCYQOT.js.map +0 -1
- package/dist/chunk-KGENSZKS.js.map +0 -1
- package/dist/chunk-KZ73RL76.js.map +0 -1
- package/dist/chunk-LZ3KHA55.js.map +0 -1
- package/dist/chunk-UJRYEWZI.js.map +0 -1
- package/dist/chunk-VGIKFIO2.js.map +0 -1
- package/dist/chunk-WZ55DVGK.js.map +0 -1
- package/dist/chunk-YRYF2GLG.js.map +0 -1
- package/dist/chunk-YSMMDOOE.js.map +0 -1
- package/dist/chunk-Z3DISEQN.js.map +0 -1
- package/dist/components/timeline-pill-geometry.d.ts +0 -8
- package/dist/components/timeline-pill-placement.d.ts +0 -24
- package/dist/timeline-pill-geometry-Z23V243M.js +0 -110
- package/dist/timeline-pill-geometry-Z23V243M.js.map +0 -1
- package/src/components/timeline-pill-geometry.ts +0 -169
- package/src/components/timeline-pill-placement.ts +0 -99
- /package/dist/{browser-viewer-5X47I6T2.js.map → browser-viewer-63TXMINB.js.map} +0 -0
- /package/dist/{chunk-UINDK6C3.js.map → chunk-2QUSQVF2.js.map} +0 -0
- /package/dist/{chunk-5EXGU7UF.js.map → chunk-DOJWJQ3F.js.map} +0 -0
- /package/dist/{chunk-BI57CQUV.js.map → chunk-I7IWRQLN.js.map} +0 -0
- /package/dist/{chunk-XA6R7DJQ.js.map → chunk-IQUCRAT7.js.map} +0 -0
- /package/dist/{computer-viewer-7GF35SNY.js.map → computer-viewer-X2MBRZY4.js.map} +0 -0
- /package/dist/{latest-question-3BJGKQQ6.js.map → latest-question-CNEZ5KAN.js.map} +0 -0
- /package/dist/{model-policy-picker-menu-PUTA5JOB.js.map → model-policy-picker-menu-JSA64TNN.js.map} +0 -0
- /package/dist/{sandbox-files-AEJTWGT4.js.map → sandbox-files-BBC47SLD.js.map} +0 -0
|
@@ -8,7 +8,10 @@ import type {
|
|
|
8
8
|
ComputerTarget,
|
|
9
9
|
} from "@opengeni/sdk/interaction";
|
|
10
10
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
isInteractionControlUnavailable,
|
|
13
|
+
isNonRetryableInteractionError,
|
|
14
|
+
} from "../lib/interaction-errors";
|
|
12
15
|
import {
|
|
13
16
|
type EmbeddedComputerInteractionClientOverride,
|
|
14
17
|
useEmbeddedComputerInteraction,
|
|
@@ -29,6 +32,8 @@ export type UseComputerSessionResult = {
|
|
|
29
32
|
loading: boolean;
|
|
30
33
|
mutating: boolean;
|
|
31
34
|
error: Error | null;
|
|
35
|
+
/** Control service loss, independent of target accessibility inspection and media. */
|
|
36
|
+
controlError: Error | null;
|
|
32
37
|
refresh: () => Promise<void>;
|
|
33
38
|
readClipboard: () => Promise<ComputerClipboard>;
|
|
34
39
|
/** Changes only this viewer's target cursor; it never takes ownership or
|
|
@@ -105,6 +110,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
105
110
|
requestRef.current.controller?.abort();
|
|
106
111
|
const controller = new AbortController();
|
|
107
112
|
requestRef.current = { id, controller };
|
|
113
|
+
let discoveryCompleted = false;
|
|
108
114
|
try {
|
|
109
115
|
const [session, targetResponse] = await Promise.all([
|
|
110
116
|
client.getComputerSession(workspaceId, computerSessionId, {
|
|
@@ -114,6 +120,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
114
120
|
signal: controller.signal,
|
|
115
121
|
}),
|
|
116
122
|
]);
|
|
123
|
+
discoveryCompleted = true;
|
|
117
124
|
if (!mountedRef.current || requestRef.current.id !== id) return;
|
|
118
125
|
const targets = sortComputerTargets(targetResponse.targets);
|
|
119
126
|
const selected = chooseTarget(targets, selectedTargetIdRef.current, session.platform);
|
|
@@ -141,6 +148,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
141
148
|
observation: retainedObservation,
|
|
142
149
|
loading: false,
|
|
143
150
|
error: null,
|
|
151
|
+
controlError: null,
|
|
144
152
|
}
|
|
145
153
|
: current,
|
|
146
154
|
);
|
|
@@ -168,12 +176,19 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
168
176
|
);
|
|
169
177
|
} catch (cause) {
|
|
170
178
|
if (controller.signal.aborted || !mountedRef.current || requestRef.current.id !== id) return;
|
|
179
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
171
180
|
setState((current) =>
|
|
172
181
|
current.computerSessionId === computerSessionId
|
|
173
182
|
? {
|
|
174
183
|
...current,
|
|
175
184
|
loading: false,
|
|
176
|
-
error
|
|
185
|
+
error,
|
|
186
|
+
controlError: isInteractionControlUnavailable(
|
|
187
|
+
error,
|
|
188
|
+
discoveryCompleted ? "observation" : "control",
|
|
189
|
+
)
|
|
190
|
+
? error
|
|
191
|
+
: current.controlError,
|
|
177
192
|
}
|
|
178
193
|
: current,
|
|
179
194
|
);
|
|
@@ -239,11 +254,23 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
239
254
|
: current,
|
|
240
255
|
);
|
|
241
256
|
try {
|
|
242
|
-
|
|
257
|
+
const result = await operation();
|
|
258
|
+
setState((current) =>
|
|
259
|
+
current.computerSessionId === scopeComputerSessionId
|
|
260
|
+
? { ...current, controlError: null }
|
|
261
|
+
: current,
|
|
262
|
+
);
|
|
263
|
+
return result;
|
|
243
264
|
} catch (cause) {
|
|
244
265
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
245
266
|
setState((current) =>
|
|
246
|
-
current.computerSessionId === scopeComputerSessionId
|
|
267
|
+
current.computerSessionId === scopeComputerSessionId
|
|
268
|
+
? {
|
|
269
|
+
...current,
|
|
270
|
+
error,
|
|
271
|
+
controlError: isInteractionControlUnavailable(error) ? error : current.controlError,
|
|
272
|
+
}
|
|
273
|
+
: current,
|
|
247
274
|
);
|
|
248
275
|
throw error;
|
|
249
276
|
} finally {
|
|
@@ -298,7 +325,14 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
298
325
|
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
299
326
|
setState((current) =>
|
|
300
327
|
current.computerSessionId === computerSessionId && current.selectedTargetId === target.id
|
|
301
|
-
? {
|
|
328
|
+
? {
|
|
329
|
+
...current,
|
|
330
|
+
loading: false,
|
|
331
|
+
error,
|
|
332
|
+
controlError: isInteractionControlUnavailable(error, "observation")
|
|
333
|
+
? error
|
|
334
|
+
: current.controlError,
|
|
335
|
+
}
|
|
302
336
|
: current,
|
|
303
337
|
);
|
|
304
338
|
throw error;
|
|
@@ -405,7 +439,21 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
405
439
|
|
|
406
440
|
const readClipboard = useCallback(async (): Promise<ComputerClipboard> => {
|
|
407
441
|
if (!computerSessionId) throw new Error("No desktop is selected.");
|
|
408
|
-
|
|
442
|
+
try {
|
|
443
|
+
return await client.readComputerClipboard(workspaceId, computerSessionId);
|
|
444
|
+
} catch (cause) {
|
|
445
|
+
const error = cause instanceof Error ? cause : new Error(String(cause));
|
|
446
|
+
setState((current) =>
|
|
447
|
+
current.computerSessionId === computerSessionId
|
|
448
|
+
? {
|
|
449
|
+
...current,
|
|
450
|
+
error,
|
|
451
|
+
controlError: isInteractionControlUnavailable(error) ? error : current.controlError,
|
|
452
|
+
}
|
|
453
|
+
: current,
|
|
454
|
+
);
|
|
455
|
+
throw error;
|
|
456
|
+
}
|
|
409
457
|
}, [client, computerSessionId, workspaceId]);
|
|
410
458
|
|
|
411
459
|
return {
|
|
@@ -417,6 +465,7 @@ export function useComputerSession(options: UseComputerSessionOptions): UseCompu
|
|
|
417
465
|
loading: visible.loading,
|
|
418
466
|
mutating: visible.mutating,
|
|
419
467
|
error: visible.error,
|
|
468
|
+
controlError: visible.controlError,
|
|
420
469
|
refresh,
|
|
421
470
|
readClipboard,
|
|
422
471
|
selectTarget,
|
|
@@ -434,6 +483,7 @@ type ComputerControlState = {
|
|
|
434
483
|
loading: boolean;
|
|
435
484
|
mutating: boolean;
|
|
436
485
|
error: Error | null;
|
|
486
|
+
controlError: Error | null;
|
|
437
487
|
};
|
|
438
488
|
|
|
439
489
|
function emptyState(computerSessionId: string | null, loading: boolean): ComputerControlState {
|
|
@@ -446,6 +496,7 @@ function emptyState(computerSessionId: string | null, loading: boolean): Compute
|
|
|
446
496
|
loading,
|
|
447
497
|
mutating: false,
|
|
448
498
|
error: null,
|
|
499
|
+
controlError: null,
|
|
449
500
|
};
|
|
450
501
|
}
|
|
451
502
|
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
useEmbeddedFileAttachments,
|
|
5
5
|
type EmbeddedFileAttachmentClientOverride,
|
|
6
6
|
} from "../session-context";
|
|
7
|
+
import { useErrorMessage } from "../lib/error-message";
|
|
7
8
|
|
|
8
9
|
export type UseFileAttachmentsOptions = EmbeddedFileAttachmentClientOverride & {
|
|
9
10
|
/**
|
|
@@ -31,6 +32,8 @@ export type FileAttachment = {
|
|
|
31
32
|
/** Stable SDK failure code for UI behavior that must not parse error copy. */
|
|
32
33
|
errorCode?: "secure_context_required" | undefined;
|
|
33
34
|
error?: string | undefined;
|
|
35
|
+
/** Original diagnostic failure; presentation copy is kept separately in `error`. */
|
|
36
|
+
errorCause?: unknown;
|
|
34
37
|
};
|
|
35
38
|
|
|
36
39
|
export type UseFileAttachmentsResult = {
|
|
@@ -120,6 +123,7 @@ export function useFileAttachments(
|
|
|
120
123
|
options: UseFileAttachmentsOptions = {},
|
|
121
124
|
): UseFileAttachmentsResult {
|
|
122
125
|
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
126
|
+
const formatError = useErrorMessage();
|
|
123
127
|
const pasteFilter = options.pasteFilter ?? isImage;
|
|
124
128
|
const [attachments, setAttachments] = useState<FileAttachment[]>([]);
|
|
125
129
|
const attachmentsRef = useRef(attachments);
|
|
@@ -234,6 +238,7 @@ export function useFileAttachments(
|
|
|
234
238
|
sizeBytes: asset.sizeBytes,
|
|
235
239
|
errorCode: undefined,
|
|
236
240
|
error: undefined,
|
|
241
|
+
errorCause: undefined,
|
|
237
242
|
}
|
|
238
243
|
: attachment,
|
|
239
244
|
),
|
|
@@ -248,14 +253,15 @@ export function useFileAttachments(
|
|
|
248
253
|
...attachment,
|
|
249
254
|
status: "failed",
|
|
250
255
|
errorCode: secureContextRequiredErrorCode(error),
|
|
251
|
-
error:
|
|
256
|
+
error: formatError(error),
|
|
257
|
+
errorCause: error,
|
|
252
258
|
}
|
|
253
259
|
: attachment,
|
|
254
260
|
),
|
|
255
261
|
);
|
|
256
262
|
});
|
|
257
263
|
},
|
|
258
|
-
[client, workspaceId, options.scope],
|
|
264
|
+
[client, workspaceId, options.scope, formatError],
|
|
259
265
|
);
|
|
260
266
|
|
|
261
267
|
const addFiles = useCallback(
|
|
@@ -291,7 +297,13 @@ export function useFileAttachments(
|
|
|
291
297
|
setAttachments((current) =>
|
|
292
298
|
current.map((attachment) =>
|
|
293
299
|
attachment.id === id
|
|
294
|
-
? {
|
|
300
|
+
? {
|
|
301
|
+
...attachment,
|
|
302
|
+
status: "uploading",
|
|
303
|
+
errorCode: undefined,
|
|
304
|
+
error: undefined,
|
|
305
|
+
errorCause: undefined,
|
|
306
|
+
}
|
|
295
307
|
: attachment,
|
|
296
308
|
),
|
|
297
309
|
);
|
|
@@ -343,6 +355,7 @@ export function useFileAttachments(
|
|
|
343
355
|
file,
|
|
344
356
|
errorCode: undefined,
|
|
345
357
|
error: undefined,
|
|
358
|
+
errorCause: undefined,
|
|
346
359
|
}
|
|
347
360
|
: {
|
|
348
361
|
id: `restored:${file.id}`,
|
package/src/hooks/use-goal.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { OpenGeniApiError, type SessionEvent, type SessionGoal } from "@opengeni/sdk";
|
|
2
2
|
import { useCallback, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { useEmbeddedGoal, type EmbeddedGoalClientOverride } from "../session-context";
|
|
4
|
+
import { normalizeError } from "../lib/error-message";
|
|
4
5
|
import {
|
|
5
6
|
useDebouncedCallback,
|
|
6
7
|
useMutationRunner,
|
|
@@ -117,7 +118,7 @@ export function useGoal(
|
|
|
117
118
|
setGoal(null);
|
|
118
119
|
setError(null);
|
|
119
120
|
} else {
|
|
120
|
-
setError(
|
|
121
|
+
setError(normalizeError(cause));
|
|
121
122
|
}
|
|
122
123
|
setLoading(false);
|
|
123
124
|
} finally {
|
|
@@ -9,6 +9,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
|
|
9
9
|
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
10
10
|
import { sandboxAcceptsLiveIo } from "../lib/sandbox-liveness";
|
|
11
11
|
import { terminalCanAcquirePty } from "../lib/terminal-capability";
|
|
12
|
+
import { normalizeError } from "../lib/error-message";
|
|
12
13
|
import { usePageLiveActivity } from "./internal";
|
|
13
14
|
|
|
14
15
|
// "on-demand" is the boxless-benign resting state: the lease is not currently
|
|
@@ -289,7 +290,7 @@ export function useSessionCapabilities(
|
|
|
289
290
|
return;
|
|
290
291
|
}
|
|
291
292
|
setState("error");
|
|
292
|
-
setError(
|
|
293
|
+
setError(normalizeError(cause));
|
|
293
294
|
}
|
|
294
295
|
});
|
|
295
296
|
}, warmingPollMs);
|
|
@@ -469,7 +470,7 @@ export function useSessionCapabilities(
|
|
|
469
470
|
return;
|
|
470
471
|
}
|
|
471
472
|
setState("error");
|
|
472
|
-
setError(
|
|
473
|
+
setError(normalizeError(cause));
|
|
473
474
|
}
|
|
474
475
|
})();
|
|
475
476
|
|
|
@@ -10,6 +10,7 @@ import { createOlderHistoryLoadReceipt, type OlderHistoryLoadReceipt } from "../
|
|
|
10
10
|
import { buildTimeline, groupTimeline, sessionStatusFromEvents } from "../timeline/projection";
|
|
11
11
|
import type { TimelineItem } from "../timeline/types";
|
|
12
12
|
import type { EmbeddedSessionClientLike } from "../client";
|
|
13
|
+
import { normalizeError } from "../lib/error-message";
|
|
13
14
|
import { usePageLiveActivity } from "./internal";
|
|
14
15
|
import type { LatestQuestionOptions } from "./latest-question";
|
|
15
16
|
|
|
@@ -114,8 +115,11 @@ const FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
|
|
|
114
115
|
const FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
|
|
115
116
|
const EMPTY_EVENTS: SessionEvent[] = [];
|
|
116
117
|
const encoder = new TextEncoder();
|
|
117
|
-
|
|
118
|
-
|
|
118
|
+
// Durable history remains on the server and is available through bounded
|
|
119
|
+
// navigation. Keep the live browser working set small across multiple tabs;
|
|
120
|
+
// serialized payloads also expand into objects and timeline projections.
|
|
121
|
+
export const SESSION_EVENT_BROWSER_MAX_BYTES = 16 * 1024 * 1024;
|
|
122
|
+
export const SESSION_EVENT_BROWSER_MAX_COUNT = 20_000;
|
|
119
123
|
export const SESSION_EVENT_BROWSER_PENDING_MAX_BYTES = 1024 * 1024;
|
|
120
124
|
export const SESSION_EVENT_BROWSER_PENDING_MAX_COUNT = 256;
|
|
121
125
|
|
|
@@ -495,7 +499,7 @@ export function useSessionEvents(
|
|
|
495
499
|
.then(() => reconcileSession(sessionId))
|
|
496
500
|
.catch((cause) => {
|
|
497
501
|
if (isCurrent()) {
|
|
498
|
-
setError(
|
|
502
|
+
setError(normalizeError(cause));
|
|
499
503
|
}
|
|
500
504
|
});
|
|
501
505
|
},
|
|
@@ -536,7 +540,7 @@ export function useSessionEvents(
|
|
|
536
540
|
} catch (cause) {
|
|
537
541
|
if (isCurrent()) {
|
|
538
542
|
flush();
|
|
539
|
-
setError(
|
|
543
|
+
setError(normalizeError(cause));
|
|
540
544
|
setConnectionState("error");
|
|
541
545
|
// A failed first compact-tail request has no later success path in
|
|
542
546
|
// this effect instance. End the loading gate so hosts can render the
|
|
@@ -878,7 +882,7 @@ export function useSessionEvents(
|
|
|
878
882
|
if (!isCurrent()) {
|
|
879
883
|
return false;
|
|
880
884
|
}
|
|
881
|
-
setNewerError(
|
|
885
|
+
setNewerError(normalizeError(reason));
|
|
882
886
|
// Keep authorization and integrity failures actionable for callers;
|
|
883
887
|
// timeline-owned invocations attach their own explicit recovery UI.
|
|
884
888
|
throw reason;
|
|
@@ -1078,10 +1082,13 @@ export function useSessionEvents(
|
|
|
1078
1082
|
visibleEvents.some((event) => event.sequence === questionEvidence.anchor)
|
|
1079
1083
|
? questionEvidence.events
|
|
1080
1084
|
: EMPTY_EVENTS;
|
|
1085
|
+
const timelineOptions = { partialStart: hasOlder || eventWindow.truncated || after > 0 };
|
|
1086
|
+
if (witness.length === 0) return buildTimeline(visibleEvents, timelineOptions);
|
|
1081
1087
|
const ids = new Set(visibleEvents.map((event) => event.id));
|
|
1082
|
-
return buildTimeline(
|
|
1083
|
-
|
|
1084
|
-
|
|
1088
|
+
return buildTimeline(
|
|
1089
|
+
[...visibleEvents, ...witness.filter((event) => !ids.has(event.id))],
|
|
1090
|
+
timelineOptions,
|
|
1091
|
+
);
|
|
1085
1092
|
}, [visibleEvents, hasOlder, eventWindow.truncated, after, questionEvidence, client, streamKey]);
|
|
1086
1093
|
|
|
1087
1094
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { KeyboardEvent } from "react";
|
|
2
2
|
import { useCallback, useMemo, useRef, useState } from "react";
|
|
3
|
+
import { useErrorMessage } from "../lib/error-message";
|
|
3
4
|
import {
|
|
4
5
|
argHint,
|
|
5
6
|
filterCommands,
|
|
@@ -81,6 +82,7 @@ export type UseSlashCommandsResult = {
|
|
|
81
82
|
};
|
|
82
83
|
|
|
83
84
|
export function useSlashCommands(options: UseSlashCommandsOptions): UseSlashCommandsResult {
|
|
85
|
+
const formatError = useErrorMessage();
|
|
84
86
|
const { commands, context, handlers, value, setValue } = options;
|
|
85
87
|
const [highlight, setHighlight] = useState(0);
|
|
86
88
|
// Escape closes the palette but keeps the draft. We remember the dismissed
|
|
@@ -153,9 +155,9 @@ export function useSlashCommands(options: UseSlashCommandsOptions): UseSlashComm
|
|
|
153
155
|
if (!context) {
|
|
154
156
|
return null;
|
|
155
157
|
}
|
|
156
|
-
return { ...context, ...handlers, confirm: () => handlers.confirm(command) };
|
|
158
|
+
return { ...context, ...handlers, formatError, confirm: () => handlers.confirm(command) };
|
|
157
159
|
},
|
|
158
|
-
[context, handlers],
|
|
160
|
+
[context, handlers, formatError],
|
|
159
161
|
);
|
|
160
162
|
|
|
161
163
|
const execute = useCallback(
|
|
@@ -173,10 +175,10 @@ export function useSlashCommands(options: UseSlashCommandsOptions): UseSlashComm
|
|
|
173
175
|
setValue("");
|
|
174
176
|
}
|
|
175
177
|
} catch (cause) {
|
|
176
|
-
ctx.notice({ tone: "error", message:
|
|
178
|
+
ctx.notice({ tone: "error", message: formatError(cause) });
|
|
177
179
|
}
|
|
178
180
|
},
|
|
179
|
-
[buildContext, setValue],
|
|
181
|
+
[buildContext, setValue, formatError],
|
|
180
182
|
);
|
|
181
183
|
|
|
182
184
|
const autocomplete = useCallback(
|
|
@@ -359,10 +361,3 @@ export function useSlashCommands(options: UseSlashCommandsOptions): UseSlashComm
|
|
|
359
361
|
autocompleteHighlighted,
|
|
360
362
|
};
|
|
361
363
|
}
|
|
362
|
-
|
|
363
|
-
function errorMessage(cause: unknown): string {
|
|
364
|
-
if (cause instanceof Error) {
|
|
365
|
-
return cause.message;
|
|
366
|
-
}
|
|
367
|
-
return String(cause);
|
|
368
|
-
}
|
|
@@ -10,6 +10,7 @@ import type {
|
|
|
10
10
|
} from "@opengeni/sdk";
|
|
11
11
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
|
|
12
12
|
import { useEmbeddedSession, type EmbeddedSessionClientOverride } from "../session-context";
|
|
13
|
+
import { normalizeError } from "../lib/error-message";
|
|
13
14
|
import {
|
|
14
15
|
useDebouncedCallback,
|
|
15
16
|
usePageLiveActivity,
|
|
@@ -463,7 +464,7 @@ function operationKey(): string {
|
|
|
463
464
|
}
|
|
464
465
|
|
|
465
466
|
function asError(cause: unknown): Error {
|
|
466
|
-
return
|
|
467
|
+
return normalizeError(cause);
|
|
467
468
|
}
|
|
468
469
|
|
|
469
470
|
function isOutcomeUnknownError(cause: unknown): boolean {
|
|
@@ -1,15 +1,19 @@
|
|
|
1
|
-
import type { Session } from "@opengeni/sdk";
|
|
1
|
+
import type { Session, SessionListEntry } from "@opengeni/sdk";
|
|
2
2
|
import { useCallback, useEffect, useRef } from "react";
|
|
3
3
|
import { useOpenGeni, type ClientOverride } from "../provider";
|
|
4
4
|
import { usePolledValue } from "./internal";
|
|
5
5
|
|
|
6
6
|
export type UseWorkspaceSessionsOptions = ClientOverride & {
|
|
7
|
+
/** Compact list records; detail reads remain full sessions. */
|
|
8
|
+
projection?: "summary" | undefined;
|
|
7
9
|
limit?: number | undefined;
|
|
8
10
|
parentSessionId?: string | null | undefined;
|
|
9
11
|
cursor?: string | undefined;
|
|
10
12
|
search?: string | undefined;
|
|
11
13
|
/** Return only the complete personal pinned projection. */
|
|
12
14
|
pinsOnly?: boolean | undefined;
|
|
15
|
+
/** Skip pinned details when the pinned section is loaded separately. */
|
|
16
|
+
includePinned?: boolean | undefined;
|
|
13
17
|
archivedOnly?: boolean | undefined;
|
|
14
18
|
sortBy?: "updatedAt" | "createdAt" | "name" | undefined;
|
|
15
19
|
archiveStatus?: "active" | "archived" | "all" | undefined;
|
|
@@ -20,14 +24,14 @@ export type UseWorkspaceSessionsOptions = ClientOverride & {
|
|
|
20
24
|
beginRead?: (() => number) | undefined;
|
|
21
25
|
};
|
|
22
26
|
|
|
23
|
-
export type UseWorkspaceSessionsResult = {
|
|
27
|
+
export type UseWorkspaceSessionsResult<T extends Session | SessionListEntry = Session> = {
|
|
24
28
|
/**
|
|
25
29
|
* All visible rows, with pins first. This preserves the pre-pinning hook
|
|
26
30
|
* contract for consumers that only read `sessions`.
|
|
27
31
|
*/
|
|
28
|
-
sessions:
|
|
32
|
+
sessions: T[];
|
|
29
33
|
/** The complete personal pinned section, also present in `sessions`. */
|
|
30
|
-
pinned:
|
|
34
|
+
pinned: T[];
|
|
31
35
|
/** True when the server omitted older pins from its bounded pinned section. */
|
|
32
36
|
pinnedTruncated: boolean;
|
|
33
37
|
nextCursor: string | null;
|
|
@@ -41,15 +45,26 @@ export type UseWorkspaceSessionsResult = {
|
|
|
41
45
|
};
|
|
42
46
|
|
|
43
47
|
/** List the workspace's sessions — the data behind fleet and manager views. */
|
|
48
|
+
export function useWorkspaceSessions(
|
|
49
|
+
options: UseWorkspaceSessionsOptions & { projection: "summary" },
|
|
50
|
+
): UseWorkspaceSessionsResult<SessionListEntry>;
|
|
51
|
+
export function useWorkspaceSessions(
|
|
52
|
+
options?: UseWorkspaceSessionsOptions & { projection?: undefined },
|
|
53
|
+
): UseWorkspaceSessionsResult;
|
|
54
|
+
export function useWorkspaceSessions(
|
|
55
|
+
options: UseWorkspaceSessionsOptions,
|
|
56
|
+
): UseWorkspaceSessionsResult<Session | SessionListEntry>;
|
|
44
57
|
export function useWorkspaceSessions(
|
|
45
58
|
options: UseWorkspaceSessionsOptions = {},
|
|
46
|
-
): UseWorkspaceSessionsResult {
|
|
59
|
+
): UseWorkspaceSessionsResult<Session | SessionListEntry> {
|
|
47
60
|
const { client, workspaceId } = useOpenGeni(options);
|
|
61
|
+
const projection = options.projection;
|
|
48
62
|
const limit = options.limit;
|
|
49
63
|
const parentSessionId = options.parentSessionId;
|
|
50
64
|
const cursor = options.cursor;
|
|
51
65
|
const search = options.search;
|
|
52
66
|
const pinsOnly = options.pinsOnly;
|
|
67
|
+
const includePinned = options.includePinned;
|
|
53
68
|
const archivedOnly = options.archivedOnly;
|
|
54
69
|
const sortBy = options.sortBy;
|
|
55
70
|
const archiveStatus = options.archiveStatus;
|
|
@@ -59,11 +74,13 @@ export function useWorkspaceSessions(
|
|
|
59
74
|
const beginRead = options.beginRead;
|
|
60
75
|
const queryKey = [
|
|
61
76
|
workspaceId,
|
|
77
|
+
projection ?? "full",
|
|
62
78
|
limit ?? "",
|
|
63
79
|
parentSessionId === null ? "null" : (parentSessionId ?? ""),
|
|
64
80
|
cursor ?? "",
|
|
65
81
|
search ?? "",
|
|
66
82
|
pinsOnly ? "1" : "",
|
|
83
|
+
includePinned === false ? "no-pins" : "",
|
|
67
84
|
archivedOnly ? "archived" : "active",
|
|
68
85
|
sortBy ?? "",
|
|
69
86
|
archiveStatus ?? "",
|
|
@@ -76,17 +93,31 @@ export function useWorkspaceSessions(
|
|
|
76
93
|
const load = useCallback(
|
|
77
94
|
async (signal?: AbortSignal) => {
|
|
78
95
|
const readGeneration = beginRead?.() ?? ++nextReadGeneration.current;
|
|
79
|
-
const
|
|
96
|
+
const query = {
|
|
80
97
|
...(limit !== undefined ? { limit } : {}),
|
|
81
98
|
...(parentSessionId !== undefined ? { parentSessionId } : {}),
|
|
82
99
|
...(cursor !== undefined ? { cursor } : {}),
|
|
83
100
|
...(search !== undefined ? { search } : {}),
|
|
84
101
|
...(pinsOnly ? { pinsOnly: true } : {}),
|
|
102
|
+
...(includePinned === false ? { includePinned: false } : {}),
|
|
85
103
|
...(archivedOnly ? { archivedOnly: true } : {}),
|
|
86
104
|
...(sortBy ? { sortBy } : {}),
|
|
87
105
|
...(archiveStatus ? { archiveStatus } : {}),
|
|
88
106
|
signal,
|
|
89
|
-
}
|
|
107
|
+
};
|
|
108
|
+
const page =
|
|
109
|
+
projection === "summary"
|
|
110
|
+
? client.listSessionSummaryPage
|
|
111
|
+
? await client.listSessionSummaryPage(workspaceId, query)
|
|
112
|
+
: await client.listSessionPage(workspaceId, query).then(async (full) => {
|
|
113
|
+
const { sessionListEntry } = await import("@opengeni/sdk/session-list-entries");
|
|
114
|
+
return {
|
|
115
|
+
...full,
|
|
116
|
+
pinned: full.pinned.map(sessionListEntry),
|
|
117
|
+
sessions: full.sessions.map(sessionListEntry),
|
|
118
|
+
};
|
|
119
|
+
})
|
|
120
|
+
: await client.listSessionPage(workspaceId, query);
|
|
90
121
|
return {
|
|
91
122
|
queryKey,
|
|
92
123
|
page,
|
|
@@ -98,11 +129,13 @@ export function useWorkspaceSessions(
|
|
|
98
129
|
beginRead,
|
|
99
130
|
client,
|
|
100
131
|
workspaceId,
|
|
132
|
+
projection,
|
|
101
133
|
limit,
|
|
102
134
|
parentSessionId,
|
|
103
135
|
cursor,
|
|
104
136
|
search,
|
|
105
137
|
pinsOnly,
|
|
138
|
+
includePinned,
|
|
106
139
|
archivedOnly,
|
|
107
140
|
sortBy,
|
|
108
141
|
archiveStatus,
|
package/src/index.ts
CHANGED
|
@@ -43,7 +43,7 @@ export type {
|
|
|
43
43
|
SessionClientLike,
|
|
44
44
|
} from "./client";
|
|
45
45
|
export { OpenGeniProvider } from "./provider";
|
|
46
|
-
export type { OpenGeniProviderProps } from "./provider";
|
|
46
|
+
export type { OpenGeniProviderProps, ErrorMessageFormatter } from "./provider";
|
|
47
47
|
export { useOpenGeni, useOpenGeniClient } from "./session-context";
|
|
48
48
|
export { useVideoArtifactPlaybackLoader } from "./hooks/use-video-artifact-playback";
|
|
49
49
|
export type { ClientOverride, OpenGeniContextValue } from "./session-context";
|
|
@@ -130,7 +130,12 @@ export type {
|
|
|
130
130
|
VoiceRecordingTranscriptionState,
|
|
131
131
|
VoiceRecordingUploadState,
|
|
132
132
|
} from "./voice-recording-store";
|
|
133
|
-
export {
|
|
133
|
+
export {
|
|
134
|
+
COMPOSER_MODEL_UNAVAILABLE_MESSAGE,
|
|
135
|
+
COMPOSER_PAYMENT_REQUIRED_MESSAGE,
|
|
136
|
+
composerSubmissionErrorMessage,
|
|
137
|
+
isModelUnavailableSubmissionError,
|
|
138
|
+
} from "./lib/format";
|
|
134
139
|
export {
|
|
135
140
|
INITIAL_TRANSCRIPTION_CONTROL_STATE,
|
|
136
141
|
appendFinalTranscript,
|
package/src/interaction.ts
CHANGED
|
@@ -58,6 +58,7 @@ export type {
|
|
|
58
58
|
} from "./hooks/use-browser-identities";
|
|
59
59
|
export { useBrowserSession } from "./hooks/use-browser-session";
|
|
60
60
|
export type {
|
|
61
|
+
BrowserInputFailure,
|
|
61
62
|
UseBrowserSessionOptions,
|
|
62
63
|
UseBrowserSessionResult,
|
|
63
64
|
} from "./hooks/use-browser-session";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { formatErrorMessage } from "@opengeni/sdk";
|
|
2
|
+
import { createContext, useCallback, useContext, useRef } from "react";
|
|
3
|
+
|
|
4
|
+
/** Original diagnostic error and the library's neutral, state-aware display copy. */
|
|
5
|
+
export type ErrorMessageFormatter = (error: unknown, defaultMessage: string) => string | undefined;
|
|
6
|
+
|
|
7
|
+
export const ErrorMessageContext = createContext<ErrorMessageFormatter | undefined>(undefined);
|
|
8
|
+
|
|
9
|
+
const originalCauses = new WeakMap<Error, unknown>();
|
|
10
|
+
|
|
11
|
+
/** Keep existing Error-typed state while retaining non-Error rejections privately. */
|
|
12
|
+
export function normalizeError(cause: unknown): Error {
|
|
13
|
+
if (cause instanceof Error) return cause;
|
|
14
|
+
const error = new Error(String(cause));
|
|
15
|
+
originalCauses.set(error, cause);
|
|
16
|
+
return error;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Presentation only: callers retain their errors and delivery/retry state. */
|
|
20
|
+
export function useErrorMessage(): (error: unknown, defaultMessage?: string) => string {
|
|
21
|
+
const formatter = useContext(ErrorMessageContext);
|
|
22
|
+
const formatterRef = useRef(formatter);
|
|
23
|
+
formatterRef.current = formatter;
|
|
24
|
+
return useCallback((error, fallback) => {
|
|
25
|
+
const original =
|
|
26
|
+
error instanceof Error && originalCauses.has(error) ? originalCauses.get(error) : error;
|
|
27
|
+
const defaultMessage = fallback ?? formatErrorMessage(original);
|
|
28
|
+
try {
|
|
29
|
+
const message = formatterRef.current?.(original, defaultMessage);
|
|
30
|
+
return typeof message === "string" && message ? message : defaultMessage;
|
|
31
|
+
} catch {
|
|
32
|
+
// Host presentation must never interrupt delivery-state settlement or
|
|
33
|
+
// cause an already-started mutation to be retried.
|
|
34
|
+
return defaultMessage;
|
|
35
|
+
}
|
|
36
|
+
}, []);
|
|
37
|
+
}
|