@opengeni/react 3.1.0 → 3.3.2-canary.1
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/dist/accounts.js +87 -11
- package/dist/accounts.js.map +1 -1
- package/dist/{browser-viewer-AEFHUYZK.js → browser-viewer-MKKFRRRV.js} +3 -3
- package/dist/{chunk-YFKJ7VZH.js → chunk-7EAFGICR.js} +45 -11
- package/dist/chunk-7EAFGICR.js.map +1 -0
- package/dist/{chunk-W6NFJXSA.js → chunk-7KBJLTKS.js} +52 -12
- package/dist/chunk-7KBJLTKS.js.map +1 -0
- package/dist/{chunk-YJFGAFBD.js → chunk-E5KISKFG.js} +241 -97
- package/dist/chunk-E5KISKFG.js.map +1 -0
- package/dist/{chunk-MZ2G2XBA.js → chunk-IOLVN74D.js} +31 -4
- package/dist/chunk-IOLVN74D.js.map +1 -0
- package/dist/{chunk-D3UIAJKY.js → chunk-JAQ5K7W6.js} +3 -2
- package/dist/{chunk-D3UIAJKY.js.map → chunk-JAQ5K7W6.js.map} +1 -1
- package/dist/{chunk-LAKLF4PA.js → chunk-VTOREEXF.js} +8 -7
- package/dist/chunk-VTOREEXF.js.map +1 -0
- package/dist/{chunk-X4A66EWZ.js → chunk-X5S426G2.js} +38 -44
- package/dist/chunk-X5S426G2.js.map +1 -0
- package/dist/{chunk-HQVCKCLG.js → chunk-YG4QEQF5.js} +354 -255
- package/dist/chunk-YG4QEQF5.js.map +1 -0
- package/dist/{chunk-XXWH2JLC.js → chunk-YVCHAQZY.js} +2 -2
- package/dist/{chunk-RGALCTWO.js → chunk-ZKNTK7IA.js} +13 -9
- package/dist/{chunk-RGALCTWO.js.map → chunk-ZKNTK7IA.js.map} +1 -1
- package/dist/{chunk-OAM4WAAM.js → chunk-ZPBTI3TE.js} +2 -2
- package/dist/components/fleet-tile.d.ts +2 -2
- package/dist/components/model-policy-picker.d.ts +9 -0
- package/dist/components/tip-follow.d.ts +16 -0
- package/dist/composer.js +6 -6
- package/dist/hooks/use-file-attachments.d.ts +2 -0
- package/dist/hooks/use-session-events.d.ts +3 -3
- package/dist/index.js +77 -69
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/model-policy.d.ts +2 -1
- package/dist/model-policy.js +1 -1
- package/dist/realtime.js +47 -8
- package/dist/realtime.js.map +1 -1
- package/dist/session-ui.js +4 -4
- package/dist/session.js +4 -4
- package/dist/timeline/entrance.d.ts +13 -1
- package/package.json +3 -2
- package/src/accounts.tsx +116 -14
- package/src/components/composer.tsx +15 -5
- package/src/components/fleet-tile.tsx +4 -15
- package/src/components/message-timeline.tsx +237 -101
- package/src/components/model-policy-picker.tsx +59 -13
- package/src/components/tip-follow.ts +52 -0
- package/src/components/tooltip.tsx +38 -5
- package/src/hooks/use-available-models.ts +20 -14
- package/src/hooks/use-composer.ts +49 -3
- package/src/hooks/use-file-attachments.ts +28 -2
- package/src/hooks/use-goal.ts +31 -7
- package/src/hooks/use-last-started-turn-policy.ts +11 -5
- package/src/hooks/use-session-events.ts +186 -61
- package/src/hooks/use-session-lineage.ts +20 -16
- package/src/hooks/use-session.ts +23 -19
- package/src/hooks/use-workspace-sessions.ts +31 -27
- package/src/lib/format.ts +8 -6
- package/src/model-policy.ts +45 -54
- package/src/realtime/realtime-control.tsx +55 -6
- package/src/timeline/entrance.tsx +28 -3
- package/src/timeline/projection.ts +4 -0
- package/styles/compiled.css +5 -0
- package/dist/chunk-HQVCKCLG.js.map +0 -1
- package/dist/chunk-LAKLF4PA.js.map +0 -1
- package/dist/chunk-MZ2G2XBA.js.map +0 -1
- package/dist/chunk-W6NFJXSA.js.map +0 -1
- package/dist/chunk-X4A66EWZ.js.map +0 -1
- package/dist/chunk-YFKJ7VZH.js.map +0 -1
- package/dist/chunk-YJFGAFBD.js.map +0 -1
- /package/dist/{browser-viewer-AEFHUYZK.js.map → browser-viewer-MKKFRRRV.js.map} +0 -0
- /package/dist/{chunk-XXWH2JLC.js.map → chunk-YVCHAQZY.js.map} +0 -0
- /package/dist/{chunk-OAM4WAAM.js.map → chunk-ZPBTI3TE.js.map} +0 -0
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
groupTimeline,
|
|
5
5
|
isActionableHumanInputRequest,
|
|
6
6
|
sessionStatusFromEvents
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-JAQ5K7W6.js";
|
|
8
8
|
import {
|
|
9
9
|
useDebouncedCallback,
|
|
10
10
|
useMutationRunner,
|
|
@@ -102,24 +102,28 @@ function useSession(sessionId, options = {}) {
|
|
|
102
102
|
const nextReadGeneration = useRef(0);
|
|
103
103
|
const beginRead = options.beginRead;
|
|
104
104
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
105
|
-
const load = useCallback(
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
let readGeneration2 = 0;
|
|
110
|
-
const fetched = await client.getSession(workspaceId, sessionId, {
|
|
111
|
-
fresh: true,
|
|
112
|
-
onRequestStart: () => {
|
|
113
|
-
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
105
|
+
const load = useCallback(
|
|
106
|
+
async (signal) => {
|
|
107
|
+
if (!sessionId) {
|
|
108
|
+
return null;
|
|
114
109
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
let readGeneration2 = 0;
|
|
111
|
+
const fetched = await client.getSession(workspaceId, sessionId, {
|
|
112
|
+
fresh: true,
|
|
113
|
+
signal,
|
|
114
|
+
onRequestStart: () => {
|
|
115
|
+
readGeneration2 = beginRead?.() ?? ++nextReadGeneration.current;
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
setOverride(null);
|
|
119
|
+
return {
|
|
120
|
+
session: fetched,
|
|
121
|
+
revision: ++nextReadRevision.current,
|
|
122
|
+
readGeneration: readGeneration2
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
[beginRead, client, workspaceId, sessionId]
|
|
126
|
+
);
|
|
123
127
|
const { data, loading, error, refresh } = usePolledValue(load, {
|
|
124
128
|
pollIntervalMs: options.pollIntervalMs,
|
|
125
129
|
enabled
|
|
@@ -188,7 +192,7 @@ function useSession(sessionId, options = {}) {
|
|
|
188
192
|
}
|
|
189
193
|
|
|
190
194
|
// src/hooks/use-session-events.ts
|
|
191
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
195
|
+
import { startTransition, useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
192
196
|
var INITIAL_TAIL_PAGE_SIZE = 1e3;
|
|
193
197
|
var OLDER_PAGE_SIZE = 5e3;
|
|
194
198
|
var NEWER_PAGE_SIZE = 5e3;
|
|
@@ -357,7 +361,10 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
357
361
|
batchResumeSequence
|
|
358
362
|
);
|
|
359
363
|
}
|
|
360
|
-
observeSessionStatus(batch, sessionStatusRef
|
|
364
|
+
const status = observeSessionStatus(batch, sessionStatusRef);
|
|
365
|
+
if (status !== void 0) {
|
|
366
|
+
setSessionStatusProjection(status);
|
|
367
|
+
}
|
|
361
368
|
const current = eventWindowRef.current;
|
|
362
369
|
const next = boundBrowserSessionEventWindow([...current.events, ...batch]);
|
|
363
370
|
const retained = {
|
|
@@ -367,7 +374,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
367
374
|
eventWindowRef.current = retained;
|
|
368
375
|
setEventWindow(retained);
|
|
369
376
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
370
|
-
newestSequenceRef.current = retained.events
|
|
377
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
371
378
|
if (retained.truncated) {
|
|
372
379
|
hasOlderRef.current = true;
|
|
373
380
|
setHasOlder(true);
|
|
@@ -391,17 +398,20 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
391
398
|
if (!isCurrent()) {
|
|
392
399
|
return;
|
|
393
400
|
}
|
|
394
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
401
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
395
402
|
const retained = boundBrowserSessionEventWindow(window.events);
|
|
396
403
|
eventWindowRef.current = retained;
|
|
397
|
-
setEventWindow(retained);
|
|
398
404
|
oldestSequenceRef.current = retained.events[0]?.sequence ?? window.oldestSequence;
|
|
399
|
-
newestSequenceRef.current = retained.events
|
|
405
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events) ?? window.newestSequence;
|
|
400
406
|
hasOlderRef.current = window.hasOlder || retained.truncated;
|
|
401
407
|
hasNewerRef.current = false;
|
|
402
408
|
lastSequenceRef.current = window.newestSequence;
|
|
403
409
|
streamResumeSequenceRef.current = window.newestSequence;
|
|
404
410
|
initialWindowLoadedRef.current = true;
|
|
411
|
+
if (status !== void 0) {
|
|
412
|
+
setSessionStatusProjection(status);
|
|
413
|
+
}
|
|
414
|
+
setEventWindow(retained);
|
|
405
415
|
setHasOlder(window.hasOlder || retained.truncated);
|
|
406
416
|
setHasNewer(false);
|
|
407
417
|
setInitialLoading(false);
|
|
@@ -450,6 +460,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
450
460
|
flush();
|
|
451
461
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
452
462
|
setConnectionState("error");
|
|
463
|
+
setInitialLoading(false);
|
|
453
464
|
}
|
|
454
465
|
}
|
|
455
466
|
})();
|
|
@@ -493,6 +504,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
493
504
|
const generation = generationRef.current;
|
|
494
505
|
loadingOlderRef.current = true;
|
|
495
506
|
setLoadingOlder(true);
|
|
507
|
+
let published = false;
|
|
496
508
|
try {
|
|
497
509
|
const window = await loadEventWindow(client, workspaceId, sessionId, {
|
|
498
510
|
before,
|
|
@@ -512,7 +524,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
512
524
|
const current = eventWindowRef.current;
|
|
513
525
|
assertPrependOrder(current.events, window.events);
|
|
514
526
|
streamAbortRef.current?.abort();
|
|
515
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
527
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
516
528
|
const next = boundBrowserSessionEventWindow([...window.events, ...current.events], {
|
|
517
529
|
direction: "oldest"
|
|
518
530
|
});
|
|
@@ -524,28 +536,54 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
524
536
|
if (retainedOldest === null || retainedOldest >= before) {
|
|
525
537
|
throw new Error("@opengeni/react: loadOlder made no durable sequence progress");
|
|
526
538
|
}
|
|
527
|
-
const retainedNewest = retained.events
|
|
539
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
540
|
+
const previousNewest = maxResumeSequenceOrNull(current.events);
|
|
528
541
|
eventWindowRef.current = retained;
|
|
529
542
|
markCommitted();
|
|
530
|
-
setEventWindow(retained);
|
|
531
543
|
oldestSequenceRef.current = retainedOldest;
|
|
532
544
|
newestSequenceRef.current = retainedNewest;
|
|
533
545
|
streamResumeSequenceRef.current = maxResumeSequence(retained.events);
|
|
534
546
|
const olderStillAvailable = window.hasOlder;
|
|
535
547
|
hasOlderRef.current = olderStillAvailable;
|
|
536
|
-
|
|
537
|
-
|
|
548
|
+
const evictedLiveTail = previousNewest !== null && retainedNewest !== null && retainedNewest < previousNewest;
|
|
549
|
+
let newer = null;
|
|
550
|
+
let enterHistory = false;
|
|
551
|
+
let reconnectLive = false;
|
|
552
|
+
if (viewModeRef.current === "history" || evictedLiveTail) {
|
|
538
553
|
const highWater = lastSequenceRef.current;
|
|
539
|
-
|
|
554
|
+
newer = retainedNewest !== null && (retainedNewest < highWater || retained.truncated);
|
|
540
555
|
hasNewerRef.current = newer;
|
|
541
|
-
|
|
556
|
+
if (evictedLiveTail) {
|
|
557
|
+
viewModeRef.current = "history";
|
|
558
|
+
enterHistory = true;
|
|
559
|
+
}
|
|
542
560
|
} else {
|
|
543
|
-
|
|
561
|
+
reconnectLive = true;
|
|
544
562
|
}
|
|
563
|
+
loadingOlderRef.current = false;
|
|
564
|
+
startTransition(() => {
|
|
565
|
+
if (status !== void 0) {
|
|
566
|
+
setSessionStatusProjection(status);
|
|
567
|
+
}
|
|
568
|
+
setEventWindow(retained);
|
|
569
|
+
setHasOlder(olderStillAvailable);
|
|
570
|
+
if (newer !== null) {
|
|
571
|
+
setHasNewer(newer);
|
|
572
|
+
}
|
|
573
|
+
if (enterHistory) {
|
|
574
|
+
setViewMode("history");
|
|
575
|
+
} else if (reconnectLive) {
|
|
576
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
577
|
+
}
|
|
578
|
+
setLoadingOlder(false);
|
|
579
|
+
});
|
|
580
|
+
published = true;
|
|
545
581
|
return olderStillAvailable;
|
|
546
582
|
} finally {
|
|
547
|
-
|
|
548
|
-
|
|
583
|
+
if (!published) {
|
|
584
|
+
loadingOlderRef.current = false;
|
|
585
|
+
setLoadingOlder(false);
|
|
586
|
+
}
|
|
549
587
|
}
|
|
550
588
|
}),
|
|
551
589
|
[client, workspaceId, sessionId]
|
|
@@ -557,6 +595,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
557
595
|
const generation = generationRef.current;
|
|
558
596
|
loadingOldestRef.current = true;
|
|
559
597
|
setLoadingOldest(true);
|
|
598
|
+
let published = false;
|
|
560
599
|
try {
|
|
561
600
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
562
601
|
after: 0,
|
|
@@ -573,28 +612,38 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
573
612
|
return false;
|
|
574
613
|
}
|
|
575
614
|
streamAbortRef.current?.abort();
|
|
576
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
615
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
577
616
|
const retained = boundBrowserSessionEventWindow(window.events, { direction: "oldest" });
|
|
578
617
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
579
|
-
const retainedNewest = retained.events
|
|
618
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
580
619
|
eventWindowRef.current = retained;
|
|
581
|
-
setEventWindow(retained);
|
|
582
620
|
oldestSequenceRef.current = retainedOldest;
|
|
583
621
|
newestSequenceRef.current = retainedNewest;
|
|
584
622
|
hasOlderRef.current = false;
|
|
585
|
-
setHasOlder(false);
|
|
586
623
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest ?? 0);
|
|
587
624
|
lastSequenceRef.current = highWater;
|
|
588
625
|
const newer = window.hasNewer || retained.truncated || retainedNewest !== null && retainedNewest < highWater;
|
|
589
626
|
hasNewerRef.current = newer;
|
|
590
|
-
setHasNewer(newer);
|
|
591
627
|
initialWindowLoadedRef.current = true;
|
|
592
628
|
viewModeRef.current = "history";
|
|
593
|
-
|
|
629
|
+
loadingOldestRef.current = false;
|
|
630
|
+
startTransition(() => {
|
|
631
|
+
if (status !== void 0) {
|
|
632
|
+
setSessionStatusProjection(status);
|
|
633
|
+
}
|
|
634
|
+
setEventWindow(retained);
|
|
635
|
+
setHasOlder(false);
|
|
636
|
+
setHasNewer(newer);
|
|
637
|
+
setViewMode("history");
|
|
638
|
+
setLoadingOldest(false);
|
|
639
|
+
});
|
|
640
|
+
published = true;
|
|
594
641
|
return newer;
|
|
595
642
|
} finally {
|
|
596
|
-
|
|
597
|
-
|
|
643
|
+
if (!published) {
|
|
644
|
+
loadingOldestRef.current = false;
|
|
645
|
+
setLoadingOldest(false);
|
|
646
|
+
}
|
|
598
647
|
}
|
|
599
648
|
}, [client, workspaceId, sessionId]);
|
|
600
649
|
const loadNewer = useCallback2(async () => {
|
|
@@ -610,6 +659,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
610
659
|
const generation = generationRef.current;
|
|
611
660
|
loadingNewerRef.current = true;
|
|
612
661
|
setLoadingNewer(true);
|
|
662
|
+
let published = false;
|
|
613
663
|
try {
|
|
614
664
|
const window = await loadForwardEventWindow(client, workspaceId, sessionId, {
|
|
615
665
|
after: afterSequence,
|
|
@@ -631,7 +681,7 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
631
681
|
}
|
|
632
682
|
const current = eventWindowRef.current;
|
|
633
683
|
assertAppendOrder(current.events, window.events);
|
|
634
|
-
observeSessionStatus(window.events, sessionStatusRef
|
|
684
|
+
const status = observeSessionStatus(window.events, sessionStatusRef);
|
|
635
685
|
const previousOldest = current.events[0]?.sequence ?? null;
|
|
636
686
|
const next = boundBrowserSessionEventWindow([...current.events, ...window.events], {
|
|
637
687
|
direction: "newest"
|
|
@@ -641,33 +691,48 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
641
691
|
truncated: current.truncated || next.truncated || window.hasNewer
|
|
642
692
|
};
|
|
643
693
|
const retainedOldest = retained.events[0]?.sequence ?? null;
|
|
644
|
-
const retainedNewest = retained.events
|
|
694
|
+
const retainedNewest = maxResumeSequenceOrNull(retained.events);
|
|
645
695
|
if (retainedNewest === null || retainedNewest <= afterSequence) {
|
|
646
696
|
throw new Error("@opengeni/react: loadNewer made no durable sequence progress");
|
|
647
697
|
}
|
|
648
698
|
eventWindowRef.current = retained;
|
|
649
|
-
setEventWindow(retained);
|
|
650
699
|
oldestSequenceRef.current = retainedOldest;
|
|
651
700
|
newestSequenceRef.current = retainedNewest;
|
|
652
701
|
if (retainedOldest !== null && (previousOldest === null || retainedOldest > previousOldest || retained.truncated && retained.events[0]?.type !== "session.created" && retainedOldest > 1)) {
|
|
653
702
|
hasOlderRef.current = true;
|
|
654
|
-
setHasOlder(true);
|
|
655
703
|
}
|
|
704
|
+
const rearmedOlder = hasOlderRef.current;
|
|
656
705
|
const highWater = Math.max(lastSequenceRef.current, retainedNewest);
|
|
657
706
|
lastSequenceRef.current = highWater;
|
|
658
707
|
const newer = window.hasNewer || retainedNewest < highWater;
|
|
659
708
|
hasNewerRef.current = newer;
|
|
660
|
-
|
|
709
|
+
let resumeLive = false;
|
|
661
710
|
if (!newer) {
|
|
662
711
|
streamResumeSequenceRef.current = retainedNewest;
|
|
663
712
|
viewModeRef.current = "live";
|
|
664
|
-
|
|
665
|
-
setStreamEpoch((epoch) => epoch + 1);
|
|
713
|
+
resumeLive = true;
|
|
666
714
|
}
|
|
715
|
+
loadingNewerRef.current = false;
|
|
716
|
+
startTransition(() => {
|
|
717
|
+
if (status !== void 0) {
|
|
718
|
+
setSessionStatusProjection(status);
|
|
719
|
+
}
|
|
720
|
+
setEventWindow(retained);
|
|
721
|
+
setHasOlder(rearmedOlder);
|
|
722
|
+
setHasNewer(newer);
|
|
723
|
+
if (resumeLive) {
|
|
724
|
+
setViewMode("live");
|
|
725
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
726
|
+
}
|
|
727
|
+
setLoadingNewer(false);
|
|
728
|
+
});
|
|
729
|
+
published = true;
|
|
667
730
|
return newer;
|
|
668
731
|
} finally {
|
|
669
|
-
|
|
670
|
-
|
|
732
|
+
if (!published) {
|
|
733
|
+
loadingNewerRef.current = false;
|
|
734
|
+
setLoadingNewer(false);
|
|
735
|
+
}
|
|
671
736
|
}
|
|
672
737
|
}, [client, workspaceId, sessionId]);
|
|
673
738
|
const jumpToLatest = useCallback2(async () => {
|
|
@@ -676,26 +741,34 @@ function useSessionEvents(sessionId, options = {}) {
|
|
|
676
741
|
}
|
|
677
742
|
loadingLatestRef.current = true;
|
|
678
743
|
setLoadingLatest(true);
|
|
744
|
+
let published = false;
|
|
679
745
|
try {
|
|
680
746
|
streamAbortRef.current?.abort();
|
|
681
747
|
hasNewerRef.current = false;
|
|
682
|
-
setHasNewer(false);
|
|
683
748
|
hasOlderRef.current = false;
|
|
684
|
-
setHasOlder(false);
|
|
685
749
|
newestSequenceRef.current = null;
|
|
686
750
|
oldestSequenceRef.current = null;
|
|
687
751
|
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
688
|
-
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
689
752
|
initialWindowLoadedRef.current = false;
|
|
690
753
|
streamResumeSequenceRef.current = after;
|
|
691
|
-
setInitialLoading(true);
|
|
692
754
|
viewModeRef.current = "live";
|
|
693
|
-
|
|
694
|
-
|
|
755
|
+
loadingLatestRef.current = false;
|
|
756
|
+
startTransition(() => {
|
|
757
|
+
setHasNewer(false);
|
|
758
|
+
setHasOlder(false);
|
|
759
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
760
|
+
setInitialLoading(true);
|
|
761
|
+
setViewMode("live");
|
|
762
|
+
setStreamEpoch((epoch) => epoch + 1);
|
|
763
|
+
setLoadingLatest(false);
|
|
764
|
+
});
|
|
765
|
+
published = true;
|
|
695
766
|
await Promise.resolve();
|
|
696
767
|
} finally {
|
|
697
|
-
|
|
698
|
-
|
|
768
|
+
if (!published) {
|
|
769
|
+
loadingLatestRef.current = false;
|
|
770
|
+
setLoadingLatest(false);
|
|
771
|
+
}
|
|
699
772
|
}
|
|
700
773
|
}, [after, sessionId]);
|
|
701
774
|
const identityMatches = stateStreamKey === streamKey;
|
|
@@ -925,7 +998,7 @@ function browserUtf8Bytes(value) {
|
|
|
925
998
|
function isBrowserUtf8Continuation(value) {
|
|
926
999
|
return (value & 192) === 128;
|
|
927
1000
|
}
|
|
928
|
-
function observeSessionStatus(events, ref
|
|
1001
|
+
function observeSessionStatus(events, ref) {
|
|
929
1002
|
let latest = null;
|
|
930
1003
|
for (const event of events) {
|
|
931
1004
|
const status = sessionStatusFromEvents([event]);
|
|
@@ -933,9 +1006,9 @@ function observeSessionStatus(events, ref, setStatus) {
|
|
|
933
1006
|
latest = { sequence: event.sequence, status };
|
|
934
1007
|
}
|
|
935
1008
|
}
|
|
936
|
-
if (!latest || latest.sequence < ref.current.sequence) return;
|
|
1009
|
+
if (!latest || latest.sequence < ref.current.sequence) return void 0;
|
|
937
1010
|
ref.current = latest;
|
|
938
|
-
|
|
1011
|
+
return latest.status;
|
|
939
1012
|
}
|
|
940
1013
|
async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
941
1014
|
let cursor = options.before;
|
|
@@ -989,24 +1062,28 @@ async function loadEventWindow(client, workspaceId, sessionId, options) {
|
|
|
989
1062
|
buffer = buffer.slice(boundary);
|
|
990
1063
|
}
|
|
991
1064
|
}
|
|
1065
|
+
const densityWindow = trimBackwardToGroupTarget(buffer, options.targetGroups);
|
|
1066
|
+
buffer = densityWindow.events;
|
|
992
1067
|
const oldest = buffer[0] ?? null;
|
|
993
1068
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
994
1069
|
return {
|
|
995
1070
|
events: buffer,
|
|
996
1071
|
oldestSequence: oldest?.sequence ?? null,
|
|
997
1072
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
998
|
-
hasOlder: buffer.length > 0 && !reachedStart && oldest?.type !== "session.created"
|
|
1073
|
+
hasOlder: buffer.length > 0 && (densityWindow.trimmed || !reachedStart && oldest?.type !== "session.created")
|
|
999
1074
|
};
|
|
1000
1075
|
}
|
|
1001
1076
|
function findBoundaryIndex(events) {
|
|
1002
1077
|
for (let index = 0; index < events.length; index += 1) {
|
|
1003
|
-
|
|
1004
|
-
if (type === "session.created" || type === "user.message") {
|
|
1078
|
+
if (isTurnBoundary(events[index])) {
|
|
1005
1079
|
return index;
|
|
1006
1080
|
}
|
|
1007
1081
|
}
|
|
1008
1082
|
return -1;
|
|
1009
1083
|
}
|
|
1084
|
+
function isTurnBoundary(event) {
|
|
1085
|
+
return event.type === "session.created" || event.type === "user.message";
|
|
1086
|
+
}
|
|
1010
1087
|
async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
1011
1088
|
let cursor = options.after;
|
|
1012
1089
|
let buffer = [];
|
|
@@ -1027,21 +1104,45 @@ async function loadForwardEventWindow(client, workspaceId, sessionId, options) {
|
|
|
1027
1104
|
}
|
|
1028
1105
|
assertAscending(page);
|
|
1029
1106
|
buffer = [...buffer, ...page];
|
|
1030
|
-
cursor = page
|
|
1031
|
-
if (page.length < options.pageSize) {
|
|
1032
|
-
reachedEnd = true;
|
|
1033
|
-
break;
|
|
1034
|
-
}
|
|
1107
|
+
cursor = maxResumeSequence(page);
|
|
1035
1108
|
}
|
|
1109
|
+
const densityWindow = trimForwardToGroupTarget(buffer, options.targetGroups);
|
|
1110
|
+
buffer = densityWindow.events;
|
|
1036
1111
|
const oldest = buffer[0] ?? null;
|
|
1037
1112
|
const newest = buffer[buffer.length - 1] ?? null;
|
|
1038
1113
|
return {
|
|
1039
1114
|
events: buffer,
|
|
1040
1115
|
oldestSequence: oldest?.sequence ?? null,
|
|
1041
1116
|
newestSequence: newest ? maxResumeSequence(buffer) : 0,
|
|
1042
|
-
hasNewer: buffer.length > 0 && !reachedEnd
|
|
1117
|
+
hasNewer: buffer.length > 0 && (densityWindow.trimmed || !reachedEnd)
|
|
1043
1118
|
};
|
|
1044
1119
|
}
|
|
1120
|
+
function trimBackwardToGroupTarget(events, targetGroups) {
|
|
1121
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1122
|
+
return { events, trimmed: false };
|
|
1123
|
+
}
|
|
1124
|
+
for (let index = events.length - 1; index > 0; index -= 1) {
|
|
1125
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1126
|
+
const candidate = events.slice(index);
|
|
1127
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1128
|
+
return { events: candidate, trimmed: true };
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
return { events, trimmed: false };
|
|
1132
|
+
}
|
|
1133
|
+
function trimForwardToGroupTarget(events, targetGroups) {
|
|
1134
|
+
if (!Number.isFinite(targetGroups) || targetGroups <= 0 || events.length === 0) {
|
|
1135
|
+
return { events, trimmed: false };
|
|
1136
|
+
}
|
|
1137
|
+
for (let index = 1; index < events.length; index += 1) {
|
|
1138
|
+
if (!isTurnBoundary(events[index])) continue;
|
|
1139
|
+
const candidate = events.slice(0, index);
|
|
1140
|
+
if (groupCount(candidate) >= targetGroups) {
|
|
1141
|
+
return { events: candidate, trimmed: true };
|
|
1142
|
+
}
|
|
1143
|
+
}
|
|
1144
|
+
return { events, trimmed: false };
|
|
1145
|
+
}
|
|
1045
1146
|
async function loadPreviousPage(client, workspaceId, sessionId, before, options) {
|
|
1046
1147
|
if (options.signal?.aborted) {
|
|
1047
1148
|
throw abortError();
|
|
@@ -1097,6 +1198,9 @@ function isLogStart(event) {
|
|
|
1097
1198
|
function maxResumeSequence(events) {
|
|
1098
1199
|
return events.reduce((max, event) => Math.max(max, eventResumeSequence(event)), 0);
|
|
1099
1200
|
}
|
|
1201
|
+
function maxResumeSequenceOrNull(events) {
|
|
1202
|
+
return events.length > 0 ? maxResumeSequence(events) : null;
|
|
1203
|
+
}
|
|
1100
1204
|
function eventResumeSequence(event) {
|
|
1101
1205
|
const payload = asRecord(event.payload);
|
|
1102
1206
|
const coalescedUntil = Number(payload.coalescedUntil);
|
|
@@ -1141,6 +1245,16 @@ function abortError() {
|
|
|
1141
1245
|
// src/hooks/use-file-attachments.ts
|
|
1142
1246
|
import { useCallback as useCallback3, useEffect as useEffect3, useRef as useRef3, useState as useState3 } from "react";
|
|
1143
1247
|
var isImage = (file) => file.type.startsWith("image/");
|
|
1248
|
+
var fallbackAttachmentId = 0;
|
|
1249
|
+
function createAttachmentId() {
|
|
1250
|
+
const cryptoSource = globalThis.crypto;
|
|
1251
|
+
if (typeof cryptoSource?.randomUUID === "function") return cryptoSource.randomUUID();
|
|
1252
|
+
fallbackAttachmentId += 1;
|
|
1253
|
+
return `attachment:${Date.now().toString(36)}:${fallbackAttachmentId.toString(36)}`;
|
|
1254
|
+
}
|
|
1255
|
+
function secureContextRequiredErrorCode(error) {
|
|
1256
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "secure_context_required" ? error.code : void 0;
|
|
1257
|
+
}
|
|
1144
1258
|
function useFileAttachments(options = {}) {
|
|
1145
1259
|
const { client, workspaceId } = useEmbeddedFileAttachments(options);
|
|
1146
1260
|
const pasteFilter = options.pasteFilter ?? isImage;
|
|
@@ -1228,6 +1342,7 @@ function useFileAttachments(options = {}) {
|
|
|
1228
1342
|
name: asset.filename,
|
|
1229
1343
|
contentType: asset.contentType,
|
|
1230
1344
|
sizeBytes: asset.sizeBytes,
|
|
1345
|
+
errorCode: void 0,
|
|
1231
1346
|
error: void 0
|
|
1232
1347
|
} : attachment
|
|
1233
1348
|
)
|
|
@@ -1239,6 +1354,7 @@ function useFileAttachments(options = {}) {
|
|
|
1239
1354
|
(attachment) => attachment.id === id ? {
|
|
1240
1355
|
...attachment,
|
|
1241
1356
|
status: "failed",
|
|
1357
|
+
errorCode: secureContextRequiredErrorCode(error),
|
|
1242
1358
|
error: error instanceof Error ? error.message : String(error)
|
|
1243
1359
|
} : attachment
|
|
1244
1360
|
)
|
|
@@ -1250,7 +1366,7 @@ function useFileAttachments(options = {}) {
|
|
|
1250
1366
|
const addFiles = useCallback3(
|
|
1251
1367
|
(files) => {
|
|
1252
1368
|
for (const file of files) {
|
|
1253
|
-
const id =
|
|
1369
|
+
const id = createAttachmentId();
|
|
1254
1370
|
sources.current.set(id, file);
|
|
1255
1371
|
const previewUrl = isImage(file) ? URL.createObjectURL(file) : void 0;
|
|
1256
1372
|
if (previewUrl) previewUrls.current.set(id, previewUrl);
|
|
@@ -1278,7 +1394,7 @@ function useFileAttachments(options = {}) {
|
|
|
1278
1394
|
}
|
|
1279
1395
|
setAttachments(
|
|
1280
1396
|
(current) => current.map(
|
|
1281
|
-
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", error: void 0 } : attachment
|
|
1397
|
+
(attachment) => attachment.id === id ? { ...attachment, status: "uploading", errorCode: void 0, error: void 0 } : attachment
|
|
1282
1398
|
)
|
|
1283
1399
|
);
|
|
1284
1400
|
startUpload(id, file);
|
|
@@ -1322,6 +1438,7 @@ function useFileAttachments(options = {}) {
|
|
|
1322
1438
|
sizeBytes: file.sizeBytes,
|
|
1323
1439
|
status: "ready",
|
|
1324
1440
|
file,
|
|
1441
|
+
errorCode: void 0,
|
|
1325
1442
|
error: void 0
|
|
1326
1443
|
} : {
|
|
1327
1444
|
id: `restored:${file.id}`,
|
|
@@ -1734,13 +1851,24 @@ function useGoal(sessionId, options = {}) {
|
|
|
1734
1851
|
const { run, mutating, mutationError, clearMutationError } = useMutationRunner();
|
|
1735
1852
|
const generation = useRef5(0);
|
|
1736
1853
|
const targetKeyRef = useRef5(null);
|
|
1854
|
+
const loadAbort = useRef5(null);
|
|
1855
|
+
const retireLoads = useCallback5(() => {
|
|
1856
|
+
generation.current += 1;
|
|
1857
|
+
loadAbort.current?.abort();
|
|
1858
|
+
loadAbort.current = null;
|
|
1859
|
+
}, []);
|
|
1737
1860
|
const load = useCallback5(async () => {
|
|
1738
1861
|
if (!sessionId) {
|
|
1739
1862
|
return;
|
|
1740
1863
|
}
|
|
1741
1864
|
const ticket = ++generation.current;
|
|
1865
|
+
loadAbort.current?.abort();
|
|
1866
|
+
const controller = new AbortController();
|
|
1867
|
+
loadAbort.current = controller;
|
|
1742
1868
|
try {
|
|
1743
|
-
const fetched = await client.getGoal(workspaceId, sessionId
|
|
1869
|
+
const fetched = await client.getGoal(workspaceId, sessionId, {
|
|
1870
|
+
signal: controller.signal
|
|
1871
|
+
});
|
|
1744
1872
|
if (ticket === generation.current) {
|
|
1745
1873
|
setGoal(fetched);
|
|
1746
1874
|
setError(null);
|
|
@@ -1757,6 +1885,8 @@ function useGoal(sessionId, options = {}) {
|
|
|
1757
1885
|
setError(cause instanceof Error ? cause : new Error(String(cause)));
|
|
1758
1886
|
}
|
|
1759
1887
|
setLoading(false);
|
|
1888
|
+
} finally {
|
|
1889
|
+
if (loadAbort.current === controller) loadAbort.current = null;
|
|
1760
1890
|
}
|
|
1761
1891
|
}, [client, workspaceId, sessionId]);
|
|
1762
1892
|
useEffect5(() => {
|
|
@@ -1767,13 +1897,14 @@ function useGoal(sessionId, options = {}) {
|
|
|
1767
1897
|
setError(null);
|
|
1768
1898
|
}
|
|
1769
1899
|
if (!enabled || !pageLive) {
|
|
1900
|
+
retireLoads();
|
|
1770
1901
|
setLoading(false);
|
|
1771
1902
|
return;
|
|
1772
1903
|
}
|
|
1773
1904
|
if (sharedFeed) {
|
|
1774
1905
|
setLoading(false);
|
|
1775
1906
|
return () => {
|
|
1776
|
-
|
|
1907
|
+
retireLoads();
|
|
1777
1908
|
};
|
|
1778
1909
|
}
|
|
1779
1910
|
setLoading(true);
|
|
@@ -1781,7 +1912,7 @@ function useGoal(sessionId, options = {}) {
|
|
|
1781
1912
|
if (pollIntervalMs === void 0 || pollIntervalMs <= 0) {
|
|
1782
1913
|
void load();
|
|
1783
1914
|
return () => {
|
|
1784
|
-
|
|
1915
|
+
retireLoads();
|
|
1785
1916
|
};
|
|
1786
1917
|
}
|
|
1787
1918
|
let cancelled = false;
|
|
@@ -1797,9 +1928,18 @@ function useGoal(sessionId, options = {}) {
|
|
|
1797
1928
|
return () => {
|
|
1798
1929
|
cancelled = true;
|
|
1799
1930
|
if (timer !== null) clearTimeout(timer);
|
|
1800
|
-
|
|
1931
|
+
retireLoads();
|
|
1801
1932
|
};
|
|
1802
|
-
}, [
|
|
1933
|
+
}, [
|
|
1934
|
+
load,
|
|
1935
|
+
enabled,
|
|
1936
|
+
pageLive,
|
|
1937
|
+
workspaceId,
|
|
1938
|
+
sessionId,
|
|
1939
|
+
options.pollIntervalMs,
|
|
1940
|
+
retireLoads,
|
|
1941
|
+
sharedFeed
|
|
1942
|
+
]);
|
|
1803
1943
|
const scheduleRefresh = useDebouncedCallback(() => void load());
|
|
1804
1944
|
useSessionEventTrigger(client, workspaceId, sessionId, isGoalRefreshEvent, scheduleRefresh, {
|
|
1805
1945
|
enabled,
|
|
@@ -1842,11 +1982,11 @@ function useGoal(sessionId, options = {}) {
|
|
|
1842
1982
|
return true;
|
|
1843
1983
|
});
|
|
1844
1984
|
if (ok) {
|
|
1845
|
-
|
|
1985
|
+
retireLoads();
|
|
1846
1986
|
setGoal(null);
|
|
1847
1987
|
setError(null);
|
|
1848
1988
|
}
|
|
1849
|
-
}, [client, workspaceId, sessionId, run]);
|
|
1989
|
+
}, [client, workspaceId, sessionId, run, retireLoads]);
|
|
1850
1990
|
return {
|
|
1851
1991
|
goal,
|
|
1852
1992
|
isActive: goal?.status === "active",
|
|
@@ -2089,24 +2229,28 @@ function useSessionLineage(sessionId, options = {}) {
|
|
|
2089
2229
|
const enabled = (options.enabled ?? true) && Boolean(sessionId);
|
|
2090
2230
|
const nextReadGeneration = useRef8(0);
|
|
2091
2231
|
const beginRead = options.beginRead;
|
|
2092
|
-
const load = useCallback8(
|
|
2093
|
-
|
|
2232
|
+
const load = useCallback8(
|
|
2233
|
+
async (signal) => {
|
|
2234
|
+
if (!sessionId) {
|
|
2235
|
+
return {
|
|
2236
|
+
lineage: { ancestors: [], children: [], truncated: false },
|
|
2237
|
+
readGeneration: 0
|
|
2238
|
+
};
|
|
2239
|
+
}
|
|
2240
|
+
let readGeneration = 0;
|
|
2241
|
+
const lineage = await client.getSessionLineage(workspaceId, sessionId, {
|
|
2242
|
+
signal,
|
|
2243
|
+
onRequestStart: (sharedReadGeneration) => {
|
|
2244
|
+
readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;
|
|
2245
|
+
}
|
|
2246
|
+
});
|
|
2094
2247
|
return {
|
|
2095
|
-
lineage
|
|
2096
|
-
readGeneration
|
|
2248
|
+
lineage,
|
|
2249
|
+
readGeneration
|
|
2097
2250
|
};
|
|
2098
|
-
}
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
onRequestStart: (sharedReadGeneration) => {
|
|
2102
|
-
readGeneration = sharedReadGeneration ?? beginRead?.() ?? ++nextReadGeneration.current;
|
|
2103
|
-
}
|
|
2104
|
-
});
|
|
2105
|
-
return {
|
|
2106
|
-
lineage,
|
|
2107
|
-
readGeneration
|
|
2108
|
-
};
|
|
2109
|
-
}, [beginRead, client, workspaceId, sessionId]);
|
|
2251
|
+
},
|
|
2252
|
+
[beginRead, client, workspaceId, sessionId]
|
|
2253
|
+
);
|
|
2110
2254
|
const state = usePolledValue(load, { pollIntervalMs: options.pollIntervalMs, enabled });
|
|
2111
2255
|
const refresh = state.refresh;
|
|
2112
2256
|
useEffect7(() => {
|
|
@@ -2334,4 +2478,4 @@ export {
|
|
|
2334
2478
|
isHumanInputEvent,
|
|
2335
2479
|
useHumanInputRequests
|
|
2336
2480
|
};
|
|
2337
|
-
//# sourceMappingURL=chunk-
|
|
2481
|
+
//# sourceMappingURL=chunk-E5KISKFG.js.map
|