@opengeni/react 3.0.0-canary.0 → 3.3.2-canary.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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-W6NFJXSA.js → chunk-2DZVYHVO.js} +20 -4
- package/dist/chunk-2DZVYHVO.js.map +1 -0
- package/dist/{chunk-YJFGAFBD.js → chunk-3AGBGTRT.js} +226 -95
- package/dist/chunk-3AGBGTRT.js.map +1 -0
- package/dist/{chunk-YFKJ7VZH.js → chunk-7EAFGICR.js} +45 -11
- package/dist/chunk-7EAFGICR.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-RGALCTWO.js → chunk-XYRDDSXF.js} +4 -4
- 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-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 +5 -5
- package/dist/hooks/use-session-events.d.ts +3 -3
- package/dist/index.js +76 -68
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +2 -2
- package/dist/machines.js +2 -2
- package/dist/realtime.js +46 -7
- 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/fleet-tile.tsx +4 -15
- package/src/components/message-timeline.tsx +237 -101
- package/src/components/model-policy-picker.tsx +24 -3
- 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-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/realtime/realtime-control.tsx +55 -6
- package/src/timeline/entrance.tsx +28 -3
- package/src/timeline/projection.ts +4 -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-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-RGALCTWO.js.map → chunk-XYRDDSXF.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
package/dist/session-ui.js
CHANGED
|
@@ -11,15 +11,15 @@ import {
|
|
|
11
11
|
sessionChromeGoalPillLabel,
|
|
12
12
|
sessionChromeGoalPillState,
|
|
13
13
|
userMessageLikelyNeedsDisclosure
|
|
14
|
-
} from "./chunk-
|
|
14
|
+
} from "./chunk-YG4QEQF5.js";
|
|
15
15
|
import {
|
|
16
16
|
createOlderHistoryLoadReceipt
|
|
17
|
-
} from "./chunk-
|
|
17
|
+
} from "./chunk-JAQ5K7W6.js";
|
|
18
18
|
import "./chunk-WSK7G5LE.js";
|
|
19
|
-
import "./chunk-
|
|
19
|
+
import "./chunk-7EAFGICR.js";
|
|
20
20
|
import "./chunk-VZTQ73XT.js";
|
|
21
21
|
import "./chunk-JVYQRRC4.js";
|
|
22
|
-
import "./chunk-
|
|
22
|
+
import "./chunk-VTOREEXF.js";
|
|
23
23
|
import "./chunk-22KP6LR5.js";
|
|
24
24
|
export {
|
|
25
25
|
BUILT_IN_TURN_SUMMARY_FACET_IDS,
|
package/dist/session.js
CHANGED
|
@@ -17,14 +17,14 @@ import {
|
|
|
17
17
|
useSessionLineage,
|
|
18
18
|
useSessionMcpApprovalPolicy,
|
|
19
19
|
useTurnQueue
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-3AGBGTRT.js";
|
|
21
21
|
import {
|
|
22
22
|
FILE_ONLY_MESSAGE_TEXT,
|
|
23
23
|
composeSendInput,
|
|
24
24
|
shouldSteerOnKey,
|
|
25
25
|
shouldSubmitOnKey,
|
|
26
26
|
useComposer
|
|
27
|
-
} from "./chunk-
|
|
27
|
+
} from "./chunk-IOLVN74D.js";
|
|
28
28
|
import {
|
|
29
29
|
buildTimeline,
|
|
30
30
|
createOlderHistoryLoadReceipt,
|
|
@@ -35,10 +35,10 @@ import {
|
|
|
35
35
|
projectPendingHumanInputRequests,
|
|
36
36
|
sessionStatusFromEvents,
|
|
37
37
|
toolDisplayName
|
|
38
|
-
} from "./chunk-
|
|
38
|
+
} from "./chunk-JAQ5K7W6.js";
|
|
39
39
|
import "./chunk-KZ73RL76.js";
|
|
40
40
|
import "./chunk-NEXLLTP4.js";
|
|
41
|
-
import "./chunk-
|
|
41
|
+
import "./chunk-VTOREEXF.js";
|
|
42
42
|
export {
|
|
43
43
|
FILE_ONLY_MESSAGE_TEXT,
|
|
44
44
|
approvalsFromRequiresAction,
|
|
@@ -1,4 +1,16 @@
|
|
|
1
|
-
|
|
1
|
+
import { type ReactNode } from "react";
|
|
2
|
+
/**
|
|
3
|
+
* Freeze the mount-time gate for this subtree while publishing a separate live
|
|
4
|
+
* gate to activity rails that need later appends to animate after a bulk paint.
|
|
5
|
+
* MessageTimeline mounts one provider per durable group, so existing groups do
|
|
6
|
+
* not receive a context invalidation when a prepend toggles bulk mode and new
|
|
7
|
+
* groups still capture the value from the commit that created them.
|
|
8
|
+
*/
|
|
9
|
+
export declare function EntranceAnimationProvider({ value, liveValue, children, }: {
|
|
10
|
+
value: boolean;
|
|
11
|
+
liveValue?: boolean | undefined;
|
|
12
|
+
children: ReactNode;
|
|
13
|
+
}): import("react").JSX.Element;
|
|
2
14
|
/**
|
|
3
15
|
* Live entrance gate from the nearest provider. Prefer
|
|
4
16
|
* {@link useEntranceAnimation} for elements that must freeze the decision at
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.3.2-canary.0",
|
|
4
4
|
"description": "React hooks and styled components for OpenGeni: live session streaming, chat composer, message timeline, session status, and fleet views — token-themed (CSS variables), dark-first, built on Tailwind v4 + Radix + Motion.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -120,8 +120,9 @@
|
|
|
120
120
|
"@dnd-kit/utilities": "3.2.2",
|
|
121
121
|
"@fontsource-variable/noto-sans-arabic": "5.2.10",
|
|
122
122
|
"@fontsource-variable/noto-sans-jp": "5.2.10",
|
|
123
|
-
"@opengeni/sdk": "^
|
|
123
|
+
"@opengeni/sdk": "^3.3.2-canary.0",
|
|
124
124
|
"clsx": "^2.1.1",
|
|
125
|
+
"dequal": "2.0.3",
|
|
125
126
|
"lucide-react": "^1.8.0",
|
|
126
127
|
"motion": "^12.0.0",
|
|
127
128
|
"radix-ui": "^1.4.3",
|
package/src/accounts.tsx
CHANGED
|
@@ -197,6 +197,7 @@ export function BrowserAccountsProvider({
|
|
|
197
197
|
expectedGeneration: string;
|
|
198
198
|
} | null>(null);
|
|
199
199
|
const pendingCommitSequenceRef = useRef<number | null>(null);
|
|
200
|
+
const pendingServerMutationSequenceRef = useRef<number | null>(null);
|
|
200
201
|
const invalidatedDuringPendingCommitRef = useRef(false);
|
|
201
202
|
const sequenceRef = useRef(0);
|
|
202
203
|
const transitionAbortRef = useRef<AbortController | null>(null);
|
|
@@ -262,6 +263,7 @@ export function BrowserAccountsProvider({
|
|
|
262
263
|
sequence: number,
|
|
263
264
|
publish: boolean,
|
|
264
265
|
authorityResetConfirmed = false,
|
|
266
|
+
initiatingSurfaceFenced = false,
|
|
265
267
|
): Promise<ManagedAuthSessionSetProjection | null> => {
|
|
266
268
|
let source = from;
|
|
267
269
|
let target = accepted;
|
|
@@ -272,7 +274,7 @@ export function BrowserAccountsProvider({
|
|
|
272
274
|
throw new Error("Browser account response regressed the accepted actor epoch");
|
|
273
275
|
}
|
|
274
276
|
}
|
|
275
|
-
if (sameSelectedActor(source, target)) {
|
|
277
|
+
if (!initiatingSurfaceFenced && sameSelectedActor(source, target)) {
|
|
276
278
|
setProjection(target);
|
|
277
279
|
setPhase("ready");
|
|
278
280
|
setError(null);
|
|
@@ -287,7 +289,13 @@ export function BrowserAccountsProvider({
|
|
|
287
289
|
// and abort old-actor work throughout that entire window.
|
|
288
290
|
if (publish) publishActorHint(target);
|
|
289
291
|
setPhase("loading");
|
|
290
|
-
await onActorTransition({
|
|
292
|
+
await onActorTransition({
|
|
293
|
+
kind,
|
|
294
|
+
from: initiatingSurfaceFenced ? null : source,
|
|
295
|
+
to: target,
|
|
296
|
+
signal: controller.signal,
|
|
297
|
+
});
|
|
298
|
+
initiatingSurfaceFenced = false;
|
|
291
299
|
if (controller.signal.aborted || sequenceRef.current !== sequence) return null;
|
|
292
300
|
if (!target) {
|
|
293
301
|
setProjection(null);
|
|
@@ -393,21 +401,44 @@ export function BrowserAccountsProvider({
|
|
|
393
401
|
pending.expectedGeneration = expectedGeneration;
|
|
394
402
|
const sequence = ++sequenceRef.current;
|
|
395
403
|
pendingCommitSequenceRef.current = sequence;
|
|
404
|
+
pendingServerMutationSequenceRef.current = null;
|
|
396
405
|
invalidatedDuringPendingCommitRef.current = false;
|
|
397
406
|
pendingRef.current = pending;
|
|
398
407
|
setPhase("committing");
|
|
399
408
|
setError(null);
|
|
400
409
|
const changesActor = pending.changesActor(current);
|
|
401
|
-
|
|
402
|
-
// Peers must hide and abort the old actor before this request can be
|
|
403
|
-
// accepted. Yielding one task after the secret-free hold lets queued
|
|
404
|
-
// BroadcastChannel delivery run before the network mutation starts;
|
|
405
|
-
// a peer that was already dispatching remains an ordinary pre-commit
|
|
406
|
-
// in-flight request and is still fenced by the server epoch.
|
|
407
|
-
publishActorHold(pending.operationId, current);
|
|
408
|
-
await yieldToCrossTabActorHold();
|
|
409
|
-
}
|
|
410
|
+
let initiatingActorFenced = false;
|
|
410
411
|
try {
|
|
412
|
+
if (changesActor) {
|
|
413
|
+
// Every tab, including the initiator, must hide and abort the old actor
|
|
414
|
+
// before this request can be accepted. Otherwise a response-lost
|
|
415
|
+
// logout can leave the initiating route alive long enough for its
|
|
416
|
+
// event stream to reconnect with the newly revoked cookie.
|
|
417
|
+
publishActorHold(pending.operationId, current);
|
|
418
|
+
transitionAbortRef.current?.abort();
|
|
419
|
+
const controller = new AbortController();
|
|
420
|
+
transitionAbortRef.current = controller;
|
|
421
|
+
projectionRef.current = null;
|
|
422
|
+
setProjection(null);
|
|
423
|
+
setPhase("loading");
|
|
424
|
+
// The host can synchronously clear its actor surface before its
|
|
425
|
+
// transition promise later rejects. From this point on, every
|
|
426
|
+
// failure must reconcile and restore authority even though the
|
|
427
|
+
// account mutation has not started yet.
|
|
428
|
+
initiatingActorFenced = true;
|
|
429
|
+
await onActorTransition({
|
|
430
|
+
kind: pending.kind,
|
|
431
|
+
from: current,
|
|
432
|
+
to: null,
|
|
433
|
+
signal: controller.signal,
|
|
434
|
+
});
|
|
435
|
+
if (controller.signal.aborted || sequenceRef.current !== sequence) return false;
|
|
436
|
+
// Yield one task after the secret-free hold so queued BroadcastChannel
|
|
437
|
+
// delivery can fence peers before the network mutation starts.
|
|
438
|
+
await yieldToCrossTabActorHold();
|
|
439
|
+
if (controller.signal.aborted || sequenceRef.current !== sequence) return false;
|
|
440
|
+
}
|
|
441
|
+
pendingServerMutationSequenceRef.current = sequence;
|
|
411
442
|
let accepted: ManagedAuthSessionSetProjection;
|
|
412
443
|
try {
|
|
413
444
|
accepted = await pending.execute(expectedGeneration);
|
|
@@ -429,7 +460,22 @@ export function BrowserAccountsProvider({
|
|
|
429
460
|
if (sequenceRef.current !== sequence) return false;
|
|
430
461
|
}
|
|
431
462
|
pendingRef.current = null;
|
|
432
|
-
|
|
463
|
+
// The server response and any invalidation reconciliation are now
|
|
464
|
+
// authoritative. Release commit ownership before host settlement so a
|
|
465
|
+
// later invalidation starts its own reconciliation instead of relying
|
|
466
|
+
// on the already-consumed check above.
|
|
467
|
+
if (pendingServerMutationSequenceRef.current === sequence) {
|
|
468
|
+
pendingServerMutationSequenceRef.current = null;
|
|
469
|
+
}
|
|
470
|
+
if (pendingCommitSequenceRef.current === sequence) {
|
|
471
|
+
pendingCommitSequenceRef.current = null;
|
|
472
|
+
invalidatedDuringPendingCommitRef.current = false;
|
|
473
|
+
}
|
|
474
|
+
if (
|
|
475
|
+
!initiatingActorFenced &&
|
|
476
|
+
!authorityResetConfirmed &&
|
|
477
|
+
sameSelectedActor(current, accepted)
|
|
478
|
+
) {
|
|
433
479
|
setProjection(accepted);
|
|
434
480
|
setPhase("ready");
|
|
435
481
|
setError(null);
|
|
@@ -441,20 +487,62 @@ export function BrowserAccountsProvider({
|
|
|
441
487
|
sequence,
|
|
442
488
|
true,
|
|
443
489
|
authorityResetConfirmed,
|
|
490
|
+
initiatingActorFenced,
|
|
444
491
|
);
|
|
445
492
|
}
|
|
446
493
|
return true;
|
|
447
494
|
} catch (caught) {
|
|
495
|
+
if (initiatingActorFenced) {
|
|
496
|
+
let authorityRestored = false;
|
|
497
|
+
try {
|
|
498
|
+
const restored = await client.reconcileSessionSetAuthority();
|
|
499
|
+
if (sequenceRef.current !== sequence) return false;
|
|
500
|
+
await settleActorTransition(
|
|
501
|
+
pending.kind,
|
|
502
|
+
current,
|
|
503
|
+
restored,
|
|
504
|
+
sequence,
|
|
505
|
+
false,
|
|
506
|
+
true,
|
|
507
|
+
true,
|
|
508
|
+
);
|
|
509
|
+
if (sequenceRef.current !== sequence) return false;
|
|
510
|
+
authorityRestored = true;
|
|
511
|
+
} catch {
|
|
512
|
+
// If authority itself cannot be reconciled, retain the existing
|
|
513
|
+
// fail-closed provider error path and its manual retry surface.
|
|
514
|
+
}
|
|
515
|
+
if (authorityRestored) {
|
|
516
|
+
// Restore transport provenance before retaining the existing
|
|
517
|
+
// fail-closed retry surface. The retry screen keeps tenant data
|
|
518
|
+
// hidden, while refresh can safely reveal this reconciled actor.
|
|
519
|
+
const restoredError = accountError(caught);
|
|
520
|
+
setError(restoredError);
|
|
521
|
+
setPhase("recoverable_error");
|
|
522
|
+
throw restoredError;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
448
525
|
return await fail(caught, pending.kind);
|
|
449
526
|
} finally {
|
|
450
527
|
if (changesActor) publishActorRelease(pending.operationId);
|
|
528
|
+
if (pendingServerMutationSequenceRef.current === sequence) {
|
|
529
|
+
pendingServerMutationSequenceRef.current = null;
|
|
530
|
+
}
|
|
451
531
|
if (pendingCommitSequenceRef.current === sequence) {
|
|
452
532
|
pendingCommitSequenceRef.current = null;
|
|
453
533
|
invalidatedDuringPendingCommitRef.current = false;
|
|
454
534
|
}
|
|
455
535
|
}
|
|
456
536
|
},
|
|
457
|
-
[
|
|
537
|
+
[
|
|
538
|
+
client,
|
|
539
|
+
fail,
|
|
540
|
+
inspectBlockers,
|
|
541
|
+
onActorTransition,
|
|
542
|
+
publishActorHold,
|
|
543
|
+
publishActorRelease,
|
|
544
|
+
settleActorTransition,
|
|
545
|
+
],
|
|
458
546
|
);
|
|
459
547
|
|
|
460
548
|
const refresh = useCallback(async () => {
|
|
@@ -493,11 +581,25 @@ export function BrowserAccountsProvider({
|
|
|
493
581
|
);
|
|
494
582
|
|
|
495
583
|
const invalidateActor = useCallback(async () => {
|
|
496
|
-
|
|
584
|
+
const pendingCommitSequence = pendingCommitSequenceRef.current;
|
|
585
|
+
if (
|
|
586
|
+
pendingCommitSequence !== null &&
|
|
587
|
+
pendingServerMutationSequenceRef.current === pendingCommitSequence
|
|
588
|
+
) {
|
|
497
589
|
invalidatedDuringPendingCommitRef.current = true;
|
|
498
590
|
await beginNeutralActorInvalidation(projectionRef.current);
|
|
499
591
|
return null;
|
|
500
592
|
}
|
|
593
|
+
// An authority hint that arrives while the initiating host fence is still
|
|
594
|
+
// pending supersedes the not-yet-dispatched mutation. Reconcile it here;
|
|
595
|
+
// otherwise aborting that fence makes executePending return early while no
|
|
596
|
+
// operation remains to restore the neutral actor surface.
|
|
597
|
+
if (pendingCommitSequence !== null) {
|
|
598
|
+
pendingCommitSequenceRef.current = null;
|
|
599
|
+
pendingServerMutationSequenceRef.current = null;
|
|
600
|
+
invalidatedDuringPendingCommitRef.current = false;
|
|
601
|
+
pendingRef.current = null;
|
|
602
|
+
}
|
|
501
603
|
const sequence = ++sequenceRef.current;
|
|
502
604
|
const before = projectionRef.current;
|
|
503
605
|
try {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import type
|
|
1
|
+
import { deriveSessionDisplayTitle, type Session } from "@opengeni/sdk";
|
|
2
2
|
import { cn } from "../lib/cn";
|
|
3
3
|
import { formatRelativeTime } from "../lib/format";
|
|
4
4
|
import { SessionStatus } from "./session-status";
|
|
5
5
|
|
|
6
|
-
const
|
|
6
|
+
const SESSION_METADATA_TITLE_KEYS = ["title", "name"] as const;
|
|
7
7
|
|
|
8
8
|
export type FleetTileProps = {
|
|
9
9
|
session: Session;
|
|
10
|
-
/** Overrides the derived title (
|
|
10
|
+
/** Overrides the derived title (durable title, metadata, prompt preview, or safe fallback). */
|
|
11
11
|
title?: string | undefined;
|
|
12
12
|
/** Extra line under the title — e.g. "drift check" or the worker's task. */
|
|
13
13
|
subtitle?: string | undefined;
|
|
@@ -17,18 +17,7 @@ export type FleetTileProps = {
|
|
|
17
17
|
|
|
18
18
|
/** Best-effort display title for a session. */
|
|
19
19
|
export function sessionDisplayTitle(session: Session): string {
|
|
20
|
-
|
|
21
|
-
// prompt-free fallback.
|
|
22
|
-
if (typeof session.title === "string" && session.title.trim().length > 0) {
|
|
23
|
-
return session.title;
|
|
24
|
-
}
|
|
25
|
-
for (const key of ["title", "name"] as const) {
|
|
26
|
-
const value = session.metadata[key];
|
|
27
|
-
if (typeof value === "string" && value.trim().length > 0) {
|
|
28
|
-
return value;
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
return AUTOMATIC_SESSION_TITLE_FALLBACK;
|
|
20
|
+
return deriveSessionDisplayTitle(session, { metadataKeys: SESSION_METADATA_TITLE_KEYS });
|
|
32
21
|
}
|
|
33
22
|
|
|
34
23
|
/**
|