@opengeni/react 3.3.2-canary.1 → 3.5.1-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/artifacts.js +12 -12
- package/dist/{browser-viewer-MKKFRRRV.js → browser-viewer-E5DDEFQ4.js} +3 -3
- package/dist/{chunk-7KBJLTKS.js → chunk-4JFUT22W.js} +10 -8
- package/dist/chunk-4JFUT22W.js.map +1 -0
- package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
- package/dist/{chunk-E5KISKFG.js → chunk-IB27C53Y.js} +115 -14
- package/dist/chunk-IB27C53Y.js.map +1 -0
- package/dist/{chunk-JAQ5K7W6.js → chunk-TJFMMDQU.js} +56 -53
- package/dist/chunk-TJFMMDQU.js.map +1 -0
- package/dist/{chunk-ZKNTK7IA.js → chunk-UNCXXIQR.js} +8 -8
- package/dist/{chunk-ZPBTI3TE.js → chunk-W2RGYBCV.js} +4 -4
- package/dist/{chunk-WSK7G5LE.js → chunk-W3OUAH6M.js} +80 -80
- package/dist/chunk-W3OUAH6M.js.map +1 -0
- package/dist/{chunk-X5S426G2.js → chunk-WMGF4Z7X.js} +6 -2
- package/dist/chunk-WMGF4Z7X.js.map +1 -0
- package/dist/{chunk-YG4QEQF5.js → chunk-WY3MELVJ.js} +7769 -7753
- package/dist/chunk-WY3MELVJ.js.map +1 -0
- package/dist/composer.js +4 -4
- package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
- package/dist/hooks/use-session-events.d.ts +1 -1
- package/dist/index.js +85 -87
- package/dist/index.js.map +1 -1
- package/dist/interaction.js +3 -3
- package/dist/model-policy.js +1 -1
- package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js} +2 -2
- package/dist/realtime.js +2 -2
- package/dist/session-ui.js +6 -6
- package/dist/session.js +4 -4
- package/dist/timeline/types.d.ts +2 -0
- package/package.json +2 -2
- package/src/components/message-timeline.tsx +16 -2
- package/src/components/model-policy-picker.tsx +2 -0
- package/src/components/sandbox-workspace.tsx +7 -6
- package/src/hooks/use-session-events.ts +168 -8
- package/src/model-policy.ts +4 -0
- package/src/timeline/projection.ts +6 -1
- package/src/timeline/types.ts +2 -0
- package/dist/chunk-7KBJLTKS.js.map +0 -1
- package/dist/chunk-E5KISKFG.js.map +0 -1
- package/dist/chunk-JAQ5K7W6.js.map +0 -1
- package/dist/chunk-WSK7G5LE.js.map +0 -1
- package/dist/chunk-X5S426G2.js.map +0 -1
- package/dist/chunk-YG4QEQF5.js.map +0 -1
- /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-E5DDEFQ4.js.map} +0 -0
- /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
- /package/dist/{chunk-ZKNTK7IA.js.map → chunk-UNCXXIQR.js.map} +0 -0
- /package/dist/{chunk-ZPBTI3TE.js.map → chunk-W2RGYBCV.js.map} +0 -0
- /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
- /package/dist/{queue-surface-implementation-BSKUWHRI.js.map → queue-surface-implementation-XL2A6PBM.js.map} +0 -0
package/dist/interaction.js
CHANGED
|
@@ -7,19 +7,20 @@ import {
|
|
|
7
7
|
useBrowserSession,
|
|
8
8
|
useBrowserSessions,
|
|
9
9
|
useSiteAuthConnections
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-W2RGYBCV.js";
|
|
11
11
|
import {
|
|
12
12
|
ComputerViewer,
|
|
13
13
|
useComputerFrameStream,
|
|
14
14
|
useComputerSession,
|
|
15
15
|
useComputerSessions
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-BV5A4OTE.js";
|
|
17
17
|
import {
|
|
18
18
|
useInteractionInterventions,
|
|
19
19
|
useInteractionInvalidation
|
|
20
20
|
} from "./chunk-H5YGYNXS.js";
|
|
21
21
|
import "./chunk-4MDLEHXL.js";
|
|
22
22
|
import "./chunk-5YK2ZDIG.js";
|
|
23
|
+
import "./chunk-JVYQRRC4.js";
|
|
23
24
|
import "./chunk-KZ73RL76.js";
|
|
24
25
|
import {
|
|
25
26
|
useEmbeddedBrowserInteraction,
|
|
@@ -27,7 +28,6 @@ import {
|
|
|
27
28
|
useEmbeddedInteraction,
|
|
28
29
|
useEmbeddedInterventions
|
|
29
30
|
} from "./chunk-NEXLLTP4.js";
|
|
30
|
-
import "./chunk-JVYQRRC4.js";
|
|
31
31
|
import "./chunk-VTOREEXF.js";
|
|
32
32
|
import "./chunk-22KP6LR5.js";
|
|
33
33
|
|
package/dist/model-policy.js
CHANGED
package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js}
RENAMED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
QueueStoppingStatus,
|
|
4
4
|
TimelineAnnotationsChip,
|
|
5
5
|
requestQueueDraftEdit
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-W3OUAH6M.js";
|
|
7
7
|
import {
|
|
8
8
|
usePortalTokenSource,
|
|
9
9
|
usePortalTokenStyle
|
|
@@ -1047,4 +1047,4 @@ export {
|
|
|
1047
1047
|
QueueSurface,
|
|
1048
1048
|
queueComposerCheckoutEnabled
|
|
1049
1049
|
};
|
|
1050
|
-
//# sourceMappingURL=queue-surface-implementation-
|
|
1050
|
+
//# sourceMappingURL=queue-surface-implementation-XL2A6PBM.js.map
|
package/dist/realtime.js
CHANGED
|
@@ -3,8 +3,7 @@ import {
|
|
|
3
3
|
PickerAnimatedPage,
|
|
4
4
|
PickerBackHeader,
|
|
5
5
|
PickerNavRow
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-X5S426G2.js";
|
|
6
|
+
} from "./chunk-4JFUT22W.js";
|
|
8
7
|
import {
|
|
9
8
|
usePortalTokenSource,
|
|
10
9
|
usePortalTokenStyle
|
|
@@ -12,6 +11,7 @@ import {
|
|
|
12
11
|
import {
|
|
13
12
|
useEmbeddedRealtimeSession
|
|
14
13
|
} from "./chunk-NEXLLTP4.js";
|
|
14
|
+
import "./chunk-WMGF4Z7X.js";
|
|
15
15
|
import {
|
|
16
16
|
cn
|
|
17
17
|
} from "./chunk-22KP6LR5.js";
|
package/dist/session-ui.js
CHANGED
|
@@ -11,14 +11,14 @@ import {
|
|
|
11
11
|
sessionChromeGoalPillLabel,
|
|
12
12
|
sessionChromeGoalPillState,
|
|
13
13
|
userMessageLikelyNeedsDisclosure
|
|
14
|
-
} from "./chunk-
|
|
15
|
-
import
|
|
16
|
-
|
|
17
|
-
} from "./chunk-JAQ5K7W6.js";
|
|
18
|
-
import "./chunk-WSK7G5LE.js";
|
|
14
|
+
} from "./chunk-WY3MELVJ.js";
|
|
15
|
+
import "./chunk-W3OUAH6M.js";
|
|
16
|
+
import "./chunk-JVYQRRC4.js";
|
|
19
17
|
import "./chunk-7EAFGICR.js";
|
|
20
18
|
import "./chunk-VZTQ73XT.js";
|
|
21
|
-
import
|
|
19
|
+
import {
|
|
20
|
+
createOlderHistoryLoadReceipt
|
|
21
|
+
} from "./chunk-TJFMMDQU.js";
|
|
22
22
|
import "./chunk-VTOREEXF.js";
|
|
23
23
|
import "./chunk-22KP6LR5.js";
|
|
24
24
|
export {
|
package/dist/session.js
CHANGED
|
@@ -17,7 +17,7 @@ import {
|
|
|
17
17
|
useSessionLineage,
|
|
18
18
|
useSessionMcpApprovalPolicy,
|
|
19
19
|
useTurnQueue
|
|
20
|
-
} from "./chunk-
|
|
20
|
+
} from "./chunk-IB27C53Y.js";
|
|
21
21
|
import {
|
|
22
22
|
FILE_ONLY_MESSAGE_TEXT,
|
|
23
23
|
composeSendInput,
|
|
@@ -25,6 +25,8 @@ import {
|
|
|
25
25
|
shouldSubmitOnKey,
|
|
26
26
|
useComposer
|
|
27
27
|
} from "./chunk-IOLVN74D.js";
|
|
28
|
+
import "./chunk-KZ73RL76.js";
|
|
29
|
+
import "./chunk-NEXLLTP4.js";
|
|
28
30
|
import {
|
|
29
31
|
buildTimeline,
|
|
30
32
|
createOlderHistoryLoadReceipt,
|
|
@@ -35,9 +37,7 @@ import {
|
|
|
35
37
|
projectPendingHumanInputRequests,
|
|
36
38
|
sessionStatusFromEvents,
|
|
37
39
|
toolDisplayName
|
|
38
|
-
} from "./chunk-
|
|
39
|
-
import "./chunk-KZ73RL76.js";
|
|
40
|
-
import "./chunk-NEXLLTP4.js";
|
|
40
|
+
} from "./chunk-TJFMMDQU.js";
|
|
41
41
|
import "./chunk-VTOREEXF.js";
|
|
42
42
|
export {
|
|
43
43
|
FILE_ONLY_MESSAGE_TEXT,
|
package/dist/timeline/types.d.ts
CHANGED
|
@@ -332,6 +332,8 @@ export type AuthNeededItem = {
|
|
|
332
332
|
providerDomain: string;
|
|
333
333
|
/** The lapsed connection to reconnect, when the row survived. */
|
|
334
334
|
connectionId: string | null;
|
|
335
|
+
/** Host-owned bindings must never be routed into OpenGeni's native reconnect flow. */
|
|
336
|
+
authoritySource?: ToolAuthNeededPayload["authoritySource"] | null | undefined;
|
|
335
337
|
reason: ToolAuthNeededPayload["reason"] | null;
|
|
336
338
|
/** Scopes the provider now needs; may inform the copy, never shown as a raw label. */
|
|
337
339
|
scopes: string[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opengeni/react",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.5.1-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,7 +120,7 @@
|
|
|
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": "^3.
|
|
123
|
+
"@opengeni/sdk": "^3.5.1-canary.0",
|
|
124
124
|
"clsx": "^2.1.1",
|
|
125
125
|
"dequal": "2.0.3",
|
|
126
126
|
"lucide-react": "^1.8.0",
|
|
@@ -3581,6 +3581,7 @@ function AuthNeededRow({
|
|
|
3581
3581
|
? `${provider} tools unavailable`
|
|
3582
3582
|
: `${actionLabel} ${provider}`;
|
|
3583
3583
|
const reasonLine = recommendation?.rationale ?? authReasonLine(item.reason);
|
|
3584
|
+
const hostAuthorizationUrl = item.authoritySource === "host" ? item.authorizationUrl : null;
|
|
3584
3585
|
|
|
3585
3586
|
const start = async () => {
|
|
3586
3587
|
if (!onReconnect || busy) {
|
|
@@ -3623,7 +3624,20 @@ function AuthNeededRow({
|
|
|
3623
3624
|
) : null}
|
|
3624
3625
|
</div>
|
|
3625
3626
|
</div>
|
|
3626
|
-
{
|
|
3627
|
+
{hostAuthorizationUrl ? (
|
|
3628
|
+
<a
|
|
3629
|
+
href={hostAuthorizationUrl}
|
|
3630
|
+
rel="noreferrer"
|
|
3631
|
+
target="_blank"
|
|
3632
|
+
className={cn(
|
|
3633
|
+
PRIMARY_ACTION_CLASS,
|
|
3634
|
+
"transition-colors hover:bg-og-accent-strong pointer-coarse:min-h-9",
|
|
3635
|
+
)}
|
|
3636
|
+
>
|
|
3637
|
+
<RefreshCwIcon className="size-3.5" aria-hidden />
|
|
3638
|
+
{actionLabel}
|
|
3639
|
+
</a>
|
|
3640
|
+
) : item.authoritySource !== "host" && !unavailable && onReconnect ? (
|
|
3627
3641
|
<button
|
|
3628
3642
|
type="button"
|
|
3629
3643
|
onClick={() => void start()}
|
|
@@ -3636,7 +3650,7 @@ function AuthNeededRow({
|
|
|
3636
3650
|
<RefreshCwIcon className={cn("size-3.5", busy && "animate-og-spin")} aria-hidden />
|
|
3637
3651
|
{busy ? "Opening…" : actionLabel}
|
|
3638
3652
|
</button>
|
|
3639
|
-
) : !unavailable && item.authorizationUrl ? (
|
|
3653
|
+
) : item.authoritySource !== "host" && !unavailable && item.authorizationUrl ? (
|
|
3640
3654
|
<a
|
|
3641
3655
|
href={item.authorizationUrl}
|
|
3642
3656
|
rel="noreferrer"
|
|
@@ -70,6 +70,7 @@ export const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {
|
|
|
70
70
|
codex_subscription: "ChatGPT / Codex plan",
|
|
71
71
|
supergrok_subscription: "SuperGrok / xAI plan",
|
|
72
72
|
byok: "Billed to the workspace provider account",
|
|
73
|
+
organization_byok: "Billed to the organization provider account",
|
|
73
74
|
},
|
|
74
75
|
};
|
|
75
76
|
|
|
@@ -160,6 +161,7 @@ export function BillingClassMark(props: {
|
|
|
160
161
|
codex_subscription: "Codex",
|
|
161
162
|
supergrok_subscription: "SuperGrok",
|
|
162
163
|
byok: "Workspace provider account",
|
|
164
|
+
organization_byok: "Organization provider account",
|
|
163
165
|
};
|
|
164
166
|
const label = props["aria-label"] ?? labels[props.billingClass];
|
|
165
167
|
const accessibility =
|
|
@@ -424,6 +424,11 @@ export function useSandboxWorkspaceTabs(
|
|
|
424
424
|
const warmIntents =
|
|
425
425
|
storedWarmIntents.sessionId === sessionId ? storedWarmIntents : emptyWarmIntents(sessionId);
|
|
426
426
|
const { warmTerminal, warmFiles } = warmIntents;
|
|
427
|
+
// A host-requested file is itself explicit live-file intent. Derive the wake
|
|
428
|
+
// directly from the request instead of relying on a follow-up effect to copy
|
|
429
|
+
// it into local state; otherwise a missing or delayed effect leaves the file
|
|
430
|
+
// waiting for a sandbox that this hook never asks to start.
|
|
431
|
+
const filesWarmRequested = warmFiles || Boolean(requestedFilePath);
|
|
427
432
|
const requestWarmIntent = useCallback(
|
|
428
433
|
(intent: Exclude<keyof SessionWarmIntents, "sessionId">) => {
|
|
429
434
|
setStoredWarmIntents((previous) => {
|
|
@@ -433,10 +438,6 @@ export function useSandboxWorkspaceTabs(
|
|
|
433
438
|
},
|
|
434
439
|
[sessionId],
|
|
435
440
|
);
|
|
436
|
-
useEffect(() => {
|
|
437
|
-
if (requestedFilePath) requestWarmIntent("warmFiles");
|
|
438
|
-
}, [requestWarmIntent, requestedFilePath, requestedFileRequestId]);
|
|
439
|
-
|
|
440
441
|
// The session's machine fleet + the active-sandbox pointer. Drives the header
|
|
441
442
|
// chip (which machine + its connection state). Shares the session list poll.
|
|
442
443
|
const machines = useMachines({
|
|
@@ -457,7 +458,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
457
458
|
attachTerminal: terminalEnabled && warmTerminal,
|
|
458
459
|
// Explicit live-file intent only — NOT "the Files tab is open". A cold edit
|
|
459
460
|
// or guarded-file open wakes the box; a glance at the tree/diff does not.
|
|
460
|
-
attachFiles: workspaceDataEnabled &&
|
|
461
|
+
attachFiles: workspaceDataEnabled && filesWarmRequested,
|
|
461
462
|
});
|
|
462
463
|
const capabilities = caps.capabilities;
|
|
463
464
|
const liveness = capabilities?.liveness;
|
|
@@ -665,7 +666,7 @@ export function useSandboxWorkspaceTabs(
|
|
|
665
666
|
capabilitiesState: caps.state,
|
|
666
667
|
activeMachineState: activeMachine?.state ?? null,
|
|
667
668
|
activeIsSelfhosted: activeMachine?.kind === "selfhosted",
|
|
668
|
-
wantsWarm: (terminalEnabled && warmTerminal) || (workspaceDataEnabled &&
|
|
669
|
+
wantsWarm: (terminalEnabled && warmTerminal) || (workspaceDataEnabled && filesWarmRequested),
|
|
669
670
|
capturedAt: captureState.capturedAt,
|
|
670
671
|
});
|
|
671
672
|
const workspaceWaking = chip.state === "waking";
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import
|
|
1
|
+
import {
|
|
2
|
+
sessionEventStreamCoveredThrough,
|
|
3
|
+
type SessionEvent,
|
|
4
|
+
type SessionStatus,
|
|
5
|
+
type StreamConnectionState,
|
|
6
|
+
} from "@opengeni/sdk";
|
|
2
7
|
import { startTransition, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
8
|
import { useEmbeddedSession, type EmbeddedSessionClientOverride } from "../session-context";
|
|
4
9
|
import { createOlderHistoryLoadReceipt, type OlderHistoryLoadReceipt } from "../older-history";
|
|
@@ -81,6 +86,15 @@ const NEWER_FETCH_CAP = 2;
|
|
|
81
86
|
const OLDEST_GROUP_TARGET = 32;
|
|
82
87
|
const OLDEST_FETCH_CAP = 2;
|
|
83
88
|
const BOUNDARY_PAGE_CAP = 4;
|
|
89
|
+
// Foreground reconciliation is intentionally semantic, not merely time-based:
|
|
90
|
+
// tiny raw gaps can stay on SSE; medium raw gaps get one compact probe so a
|
|
91
|
+
// token-heavy single answer is not mistaken for hundreds of visible messages;
|
|
92
|
+
// only a large/complex missed window reloads the latest tail.
|
|
93
|
+
const FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES = 16;
|
|
94
|
+
const FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES = 5_000;
|
|
95
|
+
const FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS = 128;
|
|
96
|
+
const FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS = 16;
|
|
97
|
+
const FOREGROUND_COMPACT_CATCHUP_MAX_BYTES = 512 * 1024;
|
|
84
98
|
const EMPTY_EVENTS: SessionEvent[] = [];
|
|
85
99
|
const encoder = new TextEncoder();
|
|
86
100
|
const decoder = new TextDecoder();
|
|
@@ -172,6 +186,7 @@ export function useSessionEvents(
|
|
|
172
186
|
const loadingLatestRef = useRef(false);
|
|
173
187
|
const viewModeRef = useRef<"live" | "history">("live");
|
|
174
188
|
const initialWindowLoadedRef = useRef(false);
|
|
189
|
+
const reconcileAfterPageResumeRef = useRef(false);
|
|
175
190
|
const streamAbortRef = useRef<AbortController | null>(null);
|
|
176
191
|
const streamKeyRef = useRef<string | null>(null);
|
|
177
192
|
const generationRef = useRef(0);
|
|
@@ -218,6 +233,7 @@ export function useSessionEvents(
|
|
|
218
233
|
viewModeRef.current = "live";
|
|
219
234
|
setViewMode("live");
|
|
220
235
|
initialWindowLoadedRef.current = false;
|
|
236
|
+
reconcileAfterPageResumeRef.current = false;
|
|
221
237
|
}
|
|
222
238
|
// AbortController is advisory: custom SDK clients and async iterators may
|
|
223
239
|
// ignore it and resolve/yield after cleanup. Fence every effect instance so
|
|
@@ -229,16 +245,35 @@ export function useSessionEvents(
|
|
|
229
245
|
setLoadingOlder(false);
|
|
230
246
|
}
|
|
231
247
|
if (!sessionId || !streamEnabled) {
|
|
248
|
+
// A page that stayed hidden beyond the live-activity grace deliberately
|
|
249
|
+
// closed its SSE connection. Replaying from the old cursor on return can
|
|
250
|
+
// drip a large background backlog through React in many fast batches and
|
|
251
|
+
// leave the pinned camera catching up long after the user is foregrounded.
|
|
252
|
+
// Remember that suspension so the next live effect replaces the browser
|
|
253
|
+
// window with one compact durable tail instead.
|
|
254
|
+
if (
|
|
255
|
+
sessionId &&
|
|
256
|
+
enabled &&
|
|
257
|
+
!pageLive &&
|
|
258
|
+
!fullReplay &&
|
|
259
|
+
viewModeRef.current === "live" &&
|
|
260
|
+
initialWindowLoadedRef.current
|
|
261
|
+
) {
|
|
262
|
+
reconcileAfterPageResumeRef.current = true;
|
|
263
|
+
}
|
|
232
264
|
setConnectionState("idle");
|
|
233
265
|
return;
|
|
234
266
|
}
|
|
235
267
|
// History view owns a non-tip window; do not open SSE (it would replay the
|
|
236
268
|
// entire gap into the browser). jumpToLatest / catching loadNewer resume live.
|
|
237
269
|
if (viewMode === "history") {
|
|
270
|
+
reconcileAfterPageResumeRef.current = false;
|
|
238
271
|
setConnectionState("idle");
|
|
239
272
|
setInitialLoading(false);
|
|
240
273
|
return;
|
|
241
274
|
}
|
|
275
|
+
const reconcileForegroundResume = reconcileAfterPageResumeRef.current && !fullReplay;
|
|
276
|
+
reconcileAfterPageResumeRef.current = false;
|
|
242
277
|
const controller = new AbortController();
|
|
243
278
|
const isCurrent = () => generationRef.current === generation && !controller.signal.aborted;
|
|
244
279
|
streamAbortRef.current = controller;
|
|
@@ -309,6 +344,65 @@ export function useSessionEvents(
|
|
|
309
344
|
|
|
310
345
|
void (async () => {
|
|
311
346
|
try {
|
|
347
|
+
if (reconcileForegroundResume) {
|
|
348
|
+
setConnectionState("connecting");
|
|
349
|
+
const cursor = streamResumeSequenceRef.current;
|
|
350
|
+
let plan: ForegroundCatchupPlan = { kind: "resume" };
|
|
351
|
+
try {
|
|
352
|
+
plan = await planForegroundCatchup(client, workspaceId, sessionId, cursor, {
|
|
353
|
+
signal: controller.signal,
|
|
354
|
+
});
|
|
355
|
+
} catch {
|
|
356
|
+
if (controller.signal.aborted) return;
|
|
357
|
+
// This read is an optimization gate, not a new availability
|
|
358
|
+
// dependency. If it fails, preserve the SDK's exact cursor replay
|
|
359
|
+
// rather than blanking a usable timeline or failing the stream.
|
|
360
|
+
plan = { kind: "resume" };
|
|
361
|
+
}
|
|
362
|
+
if (!isCurrent()) return;
|
|
363
|
+
if (plan.kind === "append") {
|
|
364
|
+
const current = eventWindowRef.current;
|
|
365
|
+
assertAppendOrder(current.events, plan.events);
|
|
366
|
+
const status = observeSessionStatus(plan.events, sessionStatusRef);
|
|
367
|
+
const next = boundBrowserSessionEventWindow([...current.events, ...plan.events]);
|
|
368
|
+
const retained = {
|
|
369
|
+
...next,
|
|
370
|
+
truncated: current.truncated || next.truncated,
|
|
371
|
+
};
|
|
372
|
+
eventWindowRef.current = retained;
|
|
373
|
+
oldestSequenceRef.current = retained.events[0]?.sequence ?? null;
|
|
374
|
+
newestSequenceRef.current = maxResumeSequenceOrNull(retained.events);
|
|
375
|
+
lastSequenceRef.current = Math.max(lastSequenceRef.current, plan.resumeSequence);
|
|
376
|
+
streamResumeSequenceRef.current = Math.max(
|
|
377
|
+
streamResumeSequenceRef.current,
|
|
378
|
+
plan.resumeSequence,
|
|
379
|
+
);
|
|
380
|
+
if (status !== undefined) {
|
|
381
|
+
setSessionStatusProjection(status);
|
|
382
|
+
}
|
|
383
|
+
setEventWindow(retained);
|
|
384
|
+
if (retained.truncated) {
|
|
385
|
+
hasOlderRef.current = true;
|
|
386
|
+
setHasOlder(true);
|
|
387
|
+
}
|
|
388
|
+
} else if (plan.kind === "reload") {
|
|
389
|
+
// A large/complex missed window would make the foreground timeline
|
|
390
|
+
// and pinned camera chase many rapid commits. Clear once so the
|
|
391
|
+
// normal bottom-anchored bulk-load path can paint one latest tail.
|
|
392
|
+
eventWindowRef.current = EMPTY_EVENT_WINDOW;
|
|
393
|
+
oldestSequenceRef.current = null;
|
|
394
|
+
newestSequenceRef.current = null;
|
|
395
|
+
hasOlderRef.current = false;
|
|
396
|
+
hasNewerRef.current = false;
|
|
397
|
+
initialWindowLoadedRef.current = false;
|
|
398
|
+
streamResumeSequenceRef.current = after;
|
|
399
|
+
setEventWindow(EMPTY_EVENT_WINDOW);
|
|
400
|
+
setHasOlder(false);
|
|
401
|
+
setHasNewer(false);
|
|
402
|
+
setInitialLoading(true);
|
|
403
|
+
setError(null);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
312
406
|
if (!fullReplay && !initialWindowLoadedRef.current) {
|
|
313
407
|
setConnectionState("connecting");
|
|
314
408
|
// First paint is ONE compact fetch — the newest window, revealed at
|
|
@@ -425,6 +519,8 @@ export function useSessionEvents(
|
|
|
425
519
|
workspaceId,
|
|
426
520
|
sessionId,
|
|
427
521
|
after,
|
|
522
|
+
enabled,
|
|
523
|
+
pageLive,
|
|
428
524
|
streamEnabled,
|
|
429
525
|
fullReplay,
|
|
430
526
|
streamKey,
|
|
@@ -1083,6 +1179,70 @@ function observeSessionStatus(
|
|
|
1083
1179
|
return latest.status;
|
|
1084
1180
|
}
|
|
1085
1181
|
|
|
1182
|
+
type ForegroundCatchupPlan =
|
|
1183
|
+
| { kind: "resume" }
|
|
1184
|
+
| { kind: "append"; events: SessionEvent[]; resumeSequence: number }
|
|
1185
|
+
| { kind: "reload" };
|
|
1186
|
+
|
|
1187
|
+
/**
|
|
1188
|
+
* Decide how a sustained hidden-tab suspension rejoins the durable event log.
|
|
1189
|
+
*
|
|
1190
|
+
* `lastSequence - cursor` is the exact raw durable work missed, but it is not
|
|
1191
|
+
* the number of visible messages: one streaming answer can own thousands of
|
|
1192
|
+
* adjacent delta rows. Small raw gaps therefore resume directly; medium gaps
|
|
1193
|
+
* get one forward compact page and are judged by the browser-visible result;
|
|
1194
|
+
* a page that cannot prove complete bounded coverage, or that would add too
|
|
1195
|
+
* many rows/groups/bytes, reloads the latest tail instead.
|
|
1196
|
+
*/
|
|
1197
|
+
async function planForegroundCatchup(
|
|
1198
|
+
client: EmbeddedSessionClientLike,
|
|
1199
|
+
workspaceId: string,
|
|
1200
|
+
sessionId: string,
|
|
1201
|
+
cursor: number,
|
|
1202
|
+
options: { signal?: AbortSignal } = {},
|
|
1203
|
+
): Promise<ForegroundCatchupPlan> {
|
|
1204
|
+
if (options.signal?.aborted) throw abortError();
|
|
1205
|
+
const session = await client.getSession(workspaceId, sessionId, {
|
|
1206
|
+
fresh: true,
|
|
1207
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1208
|
+
});
|
|
1209
|
+
if (options.signal?.aborted) throw abortError();
|
|
1210
|
+
const durableHead = Math.max(cursor, session.lastSequence);
|
|
1211
|
+
const rawGap = durableHead - cursor;
|
|
1212
|
+
if (rawGap <= FOREGROUND_DIRECT_REPLAY_MAX_SEQUENCES) {
|
|
1213
|
+
return { kind: "resume" };
|
|
1214
|
+
}
|
|
1215
|
+
if (rawGap > FOREGROUND_COMPACT_PROBE_MAX_SEQUENCES) {
|
|
1216
|
+
return { kind: "reload" };
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
const page = await loadNextPage(client, workspaceId, sessionId, cursor, {
|
|
1220
|
+
pageSize: rawGap,
|
|
1221
|
+
...(options.signal ? { signal: options.signal } : {}),
|
|
1222
|
+
});
|
|
1223
|
+
const events = page
|
|
1224
|
+
.filter((event) => eventResumeSequence(event) > cursor)
|
|
1225
|
+
.map(boundBrowserLegacyEvent);
|
|
1226
|
+
if (events.length === 0) {
|
|
1227
|
+
return { kind: "reload" };
|
|
1228
|
+
}
|
|
1229
|
+
assertAscending(events);
|
|
1230
|
+
const resumeSequence = maxResumeSequence(events);
|
|
1231
|
+
if (resumeSequence < durableHead) {
|
|
1232
|
+
// Byte/count truncation or an incomplete projection means this page cannot
|
|
1233
|
+
// prove that one append reaches the head observed above.
|
|
1234
|
+
return { kind: "reload" };
|
|
1235
|
+
}
|
|
1236
|
+
if (
|
|
1237
|
+
events.length > FOREGROUND_COMPACT_CATCHUP_MAX_EVENTS ||
|
|
1238
|
+
groupCount(events) > FOREGROUND_COMPACT_CATCHUP_MAX_GROUPS ||
|
|
1239
|
+
browserJsonBytes(events) > FOREGROUND_COMPACT_CATCHUP_MAX_BYTES
|
|
1240
|
+
) {
|
|
1241
|
+
return { kind: "reload" };
|
|
1242
|
+
}
|
|
1243
|
+
return { kind: "append", events, resumeSequence };
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1086
1246
|
type LoadedEventWindow = {
|
|
1087
1247
|
events: SessionEvent[];
|
|
1088
1248
|
oldestSequence: number | null;
|
|
@@ -1378,13 +1538,13 @@ function maxResumeSequenceOrNull(events: readonly SessionEvent[]): number | null
|
|
|
1378
1538
|
}
|
|
1379
1539
|
|
|
1380
1540
|
function eventResumeSequence(event: SessionEvent): number {
|
|
1381
|
-
const
|
|
1382
|
-
|
|
1383
|
-
return
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1541
|
+
const streamedCoverage = sessionEventStreamCoveredThrough(event);
|
|
1542
|
+
if (streamedCoverage !== null) return streamedCoverage;
|
|
1543
|
+
return typeof event.coveredThrough === "number" &&
|
|
1544
|
+
Number.isSafeInteger(event.coveredThrough) &&
|
|
1545
|
+
event.coveredThrough >= event.sequence
|
|
1546
|
+
? event.coveredThrough
|
|
1547
|
+
: event.sequence;
|
|
1388
1548
|
}
|
|
1389
1549
|
|
|
1390
1550
|
function assertAscending(events: SessionEvent[]): void {
|
package/src/model-policy.ts
CHANGED
|
@@ -29,6 +29,7 @@ const BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {
|
|
|
29
29
|
codex_subscription: "Codex",
|
|
30
30
|
supergrok_subscription: "SuperGrok",
|
|
31
31
|
byok: "Workspace providers",
|
|
32
|
+
organization_byok: "Organization providers",
|
|
32
33
|
};
|
|
33
34
|
|
|
34
35
|
const AVAILABILITY_REASON_LABELS: Record<string, string> = {
|
|
@@ -42,6 +43,9 @@ const AVAILABILITY_REASON_LABELS: Record<string, string> = {
|
|
|
42
43
|
};
|
|
43
44
|
|
|
44
45
|
export function billingClassForModel(model: ClientModel): PickerBillingClass {
|
|
46
|
+
if (model.provider === "organization-gateway" || model.provider === "organization-openrouter") {
|
|
47
|
+
return "organization_byok";
|
|
48
|
+
}
|
|
45
49
|
if (model.provider === "workspace-gateway" || model.provider === "workspace-openrouter") {
|
|
46
50
|
return "byok";
|
|
47
51
|
}
|
|
@@ -897,7 +897,12 @@ export function buildTimeline(events: SessionEvent[]): TimelineItem[] {
|
|
|
897
897
|
: "credential",
|
|
898
898
|
providerDomain: stringValue(payload.providerDomain),
|
|
899
899
|
connectionId: typeof payload.connectionId === "string" ? payload.connectionId : null,
|
|
900
|
-
|
|
900
|
+
authoritySource: payload.authoritySource === "host" ? "host" : null,
|
|
901
|
+
reason: authNeededReason(
|
|
902
|
+
payload.authoritySource === "host" && typeof payload.authorizationUrl === "string"
|
|
903
|
+
? (payload.hostReason ?? payload.reason)
|
|
904
|
+
: payload.reason,
|
|
905
|
+
),
|
|
901
906
|
scopes: stringList(payload.scopes),
|
|
902
907
|
resource: typeof payload.resource === "string" ? payload.resource : null,
|
|
903
908
|
toolName: typeof payload.toolName === "string" ? payload.toolName : null,
|
package/src/timeline/types.ts
CHANGED
|
@@ -420,6 +420,8 @@ export type AuthNeededItem = {
|
|
|
420
420
|
providerDomain: string;
|
|
421
421
|
/** The lapsed connection to reconnect, when the row survived. */
|
|
422
422
|
connectionId: string | null;
|
|
423
|
+
/** Host-owned bindings must never be routed into OpenGeni's native reconnect flow. */
|
|
424
|
+
authoritySource?: ToolAuthNeededPayload["authoritySource"] | null | undefined;
|
|
423
425
|
reason: ToolAuthNeededPayload["reason"] | null;
|
|
424
426
|
/** Scopes the provider now needs; may inform the copy, never shown as a raw label. */
|
|
425
427
|
scopes: string[];
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/components/model-policy-picker.tsx"],"sourcesContent":["import type { ClientModel, LatencyMode, ReasoningEffort } from \"@opengeni/sdk\";\nimport {\n CheckIcon,\n ChevronDownIcon,\n ChevronLeftIcon,\n ChevronRightIcon,\n Globe2Icon,\n KeyRoundIcon,\n ZapIcon,\n} from \"lucide-react\";\nimport { motion, useReducedMotion } from \"motion/react\";\nimport { DropdownMenu } from \"radix-ui\";\nimport {\n useEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type ReactNode,\n type SVGProps,\n} from \"react\";\nimport { cn } from \"../lib/cn\";\nimport { usePortalTokenSource, usePortalTokenStyle } from \"../lib/use-portal-token-style\";\nimport {\n effortOptionsForModel,\n findPickerRow,\n groupPickerRowsByBillingClass,\n labelReasoningEffort,\n payerSummaryForModel,\n projectClientModelRows,\n runnableLatencyModesForModel,\n type PickerBillingClass,\n type PickerModelRow,\n} from \"../model-policy\";\n\ntype ClientPickerModelRow = PickerModelRow<ClientModel>;\n\ntype NavLevel = \"providers\" | \"models\" | \"thinking\";\n\ntype PickerNavState = {\n level: NavLevel;\n rail: PickerBillingClass | null;\n modelId: string | null;\n};\n\ntype NavUpdater = PickerNavState | ((previous: PickerNavState) => PickerNavState);\n\nexport type ModelPolicyPickerMessages = {\n label: string;\n loading: string;\n noModels: string;\n thinking: string;\n fast: string;\n fastRateHint: string;\n codexOnly: string;\n billingHints: Record<PickerBillingClass, string>;\n};\n\nexport const defaultModelPolicyPickerMessages: ModelPolicyPickerMessages = {\n label: \"Model and effort\",\n loading: \"Loading model catalog…\",\n noModels: \"No models available.\",\n thinking: \"Thinking\",\n fast: \"Fast\",\n fastRateHint: \"2× rate\",\n codexOnly: \"Codex-only session\",\n billingHints: {\n opengeni_credits: \"Will use credits\",\n external: \"Provider terms and limits apply\",\n codex_subscription: \"ChatGPT / Codex plan\",\n supergrok_subscription: \"SuperGrok / xAI plan\",\n byok: \"Billed to the workspace provider account\",\n },\n};\n\nexport type ModelPolicyPickerProps = {\n /** Lightweight deployment models. Catalog rows take precedence when supplied. */\n models?: ClientModel[] | undefined;\n /** Catalog-backed rows with availability and billing-class truth. */\n rows?: PickerModelRow[] | undefined;\n model: string;\n effort: ReasoningEffort;\n latencyMode: LatencyMode;\n disabled?: boolean | undefined;\n loading?: boolean | undefined;\n error?: string | null | undefined;\n /** Controlled menu state. Omit for the built-in uncontrolled behavior. */\n open?: boolean | undefined;\n /** Initial menu state when `open` is uncontrolled. */\n defaultOpen?: boolean | undefined;\n onOpenChange?: ((open: boolean) => void) | undefined;\n /** Non-Codex models stay visible but cannot be selected. */\n codexOnly?: boolean | undefined;\n /** Resets drill-down navigation when the session scope changes. */\n sessionKey?: string | undefined;\n /** Prefer bottom on new-chat surfaces and top for bottom-docked composers. */\n menuSide?: \"top\" | \"bottom\" | undefined;\n /** Hide latency controls on surfaces whose saved policy does not include latency. */\n allowLatencyMode?: boolean | undefined;\n /** Classes for the portalled menu surface. Prefer --og-* tokens for theming. */\n contentClassName?: string | undefined;\n /** Inline styles for the portalled menu, applied after inherited --og-* tokens. */\n contentStyle?: CSSProperties | undefined;\n className?: string | undefined;\n messages?: Partial<ModelPolicyPickerMessages> | undefined;\n onModelChange: (modelId: string) => void;\n onEffortChange: (effort: ReasoningEffort) => void;\n onLatencyModeChange: (latencyMode: LatencyMode) => void;\n};\n\nconst SLIDE_EASE = [0.22, 1, 0.36, 1] as const;\n\nfunction OpenGeniMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg\n viewBox=\"0 0 140 133\"\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={1.2}\n strokeLinejoin=\"round\"\n aria-hidden=\"true\"\n {...props}\n >\n <g transform=\"translate(-17.5,-20.999893188476562) scale(1.75)\">\n <g transform=\"translate(0,-952.36218)\">\n <path d=\"m 60.7828,964.36215 27.1809,0.8834 -27.1809,25.9958 z m -1.9745,1.4513 0,26.7845 -25.2681,0 c 8.6166,-8.7334 16.8796,-17.8103 25.2681,-26.7845 z m 27.7053,3.628 3.4864,1.1989 -12.5877,7.4768 z m -68.1835,2.9656 5.5226,0 12.8654,14.0705 -5.9854,6.1204 -12.4026,0 c 9e-4,-6.7347 0,-13.4597 0,-20.1909 z m -1.9746,1.2304 0,5.8364 -6.3555,0 z m 3.363,20.9796 38.627,0 -10.7675,29.43465 z m 39.0898,4.54286 0,41.20229 -12.5878,-6.8775 c 4.1972,-11.443 8.3886,-22.879 12.5878,-34.32479 z\" />\n </g>\n </g>\n </svg>\n );\n}\n\nfunction ChatGptMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <path d=\"M22.2819 9.8211a5.9847 5.9847 0 0 0-.5157-4.9108 6.0462 6.0462 0 0 0-6.5098-2.9A6.0651 6.0651 0 0 0 4.9807 4.1818a5.9847 5.9847 0 0 0-3.9977 2.9 6.0462 6.0462 0 0 0 .7427 7.0966 5.98 5.98 0 0 0 .511 4.9107 6.051 6.051 0 0 0 6.5146 2.9001A5.9847 5.9847 0 0 0 13.2599 24a6.0557 6.0557 0 0 0 5.7718-4.2058 5.9894 5.9894 0 0 0 3.9977-2.9001 6.0557 6.0557 0 0 0-.7475-7.0729zm-9.022 12.6081a4.4755 4.4755 0 0 1-2.8764-1.0408l.1419-.0804 4.7783-2.7582a.7948.7948 0 0 0 .3927-.6813v-6.7369l2.02 1.1686a.071.071 0 0 1 .038.052v5.5826a4.504 4.504 0 0 1-4.4945 4.4944zm-9.6607-4.1254a4.4708 4.4708 0 0 1-.5346-3.0137l.142.0852 4.783 2.7582a.7712.7712 0 0 0 .7806 0l5.8428-3.3685v2.3324a.0804.0804 0 0 1-.0332.0615L9.74 19.9502a4.4992 4.4992 0 0 1-6.1408-1.6464zM2.3408 7.8956a4.485 4.485 0 0 1 2.3653-1.9728V11.6a.7664.7664 0 0 0 .3879.6765l5.8414 3.3698-2.02 1.1685a.0757.0757 0 0 1-.071 0l-4.8303-2.7865A4.504 4.504 0 0 1 2.3408 7.872zm16.5963 3.8558L13.1038 8.364 15.1192 7.2a.0757.0757 0 0 1 .071 0l4.8303 2.7913a4.4944 4.4944 0 0 1-.6765 8.1042v-5.6772a.79.79 0 0 0-.407-.667zm2.0107-3.0231l-.142-.0852-4.783-2.7622a.7759.7759 0 0 0-.7854 0L9.409 9.2297V6.8974a.0662.0662 0 0 1 .0284-.0615l4.8303-2.7866a4.4992 4.4992 0 0 1 6.6802 4.66zM8.3065 12.863l-2.02-1.1638a.0804.0804 0 0 1-.038-.0567V6.0742a4.4992 4.4992 0 0 1 7.3757-3.4537l-.142.0805L8.704 5.459a.7948.7948 0 0 0-.3927.6813zm1.0976-2.3654l2.602-1.4998 2.6069 1.4998v2.9994l-2.5974 1.4997-2.6067-1.4997Z\" />\n </svg>\n );\n}\n\nfunction XaiMark(props: SVGProps<SVGSVGElement>) {\n return (\n <svg viewBox=\"0 0 466.04 516.93\" fill=\"currentColor\" aria-hidden=\"true\" {...props}>\n <polygon points=\"0.12 182.71 234.14 516.92 338.15 516.92 104.13 182.71 0.12 182.71\" />\n <polygon points=\"0 516.92 104.08 516.92 156.08 442.67 104.04 368.34 0 516.92\" />\n <polygon points=\"466.04 0 361.96 0 182.1 256.86 234.15 331.18 466.04 0\" />\n <polygon points=\"380.78 516.92 466.04 516.92 466.04 37.16 380.78 158.92 380.78 516.92\" />\n </svg>\n );\n}\n\nexport function BillingClassMark(props: {\n billingClass: PickerBillingClass;\n className?: string | undefined;\n \"aria-label\"?: string | undefined;\n}) {\n const labels: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n external: \"External provider\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n byok: \"Workspace provider account\",\n };\n const label = props[\"aria-label\"] ?? labels[props.billingClass];\n const accessibility =\n label.length === 0\n ? { \"aria-hidden\": true as const }\n : { role: \"img\" as const, \"aria-label\": label };\n const shell = cn(\n \"inline-flex size-3.5 shrink-0 items-center justify-center overflow-hidden text-og-fg-subtle\",\n props.className,\n );\n const mark = \"size-3.5\";\n return (\n <span\n className={shell}\n data-testid={`billing-class-icon-${props.billingClass}`}\n {...accessibility}\n >\n {props.billingClass === \"opengeni_credits\" ? (\n <OpenGeniMark className={mark} />\n ) : props.billingClass === \"external\" ? (\n <Globe2Icon className={mark} aria-hidden />\n ) : props.billingClass === \"codex_subscription\" ? (\n <ChatGptMark className={mark} />\n ) : props.billingClass === \"supergrok_subscription\" ? (\n <XaiMark className={mark} />\n ) : (\n <KeyRoundIcon className={mark} aria-hidden />\n )}\n </span>\n );\n}\n\nfunction isCodexModel(model: ClientModel): boolean {\n return model.id.startsWith(\"codex/\") || model.source === \"codex\";\n}\n\nfunction billingClassForMissingSelection(modelId: string): PickerBillingClass {\n if (modelId.startsWith(\"workspace-gateway/\")) return \"byok\";\n if (modelId.startsWith(\"workspace-openrouter/\")) return \"byok\";\n // A deployment OpenRouter ID does not encode its workspace-facing cost.\n // Missing rows therefore use the credits-safe rail instead of falsely\n // claiming that an unknown former selection was externally funded.\n if (modelId.startsWith(\"openrouter/\")) return \"opengeni_credits\";\n if (modelId.startsWith(\"codex/\")) return \"codex_subscription\";\n if (modelId.startsWith(\"supergrok/\")) return \"supergrok_subscription\";\n return \"opengeni_credits\";\n}\n\nfunction applyCodexOnly(\n rows: ClientPickerModelRow[],\n codexOnly: boolean,\n unavailableReason: string,\n): ClientPickerModelRow[] {\n if (!codexOnly) return rows;\n return rows.map((row) =>\n isCodexModel(row.catalog)\n ? row\n : {\n ...row,\n selectable: false,\n unavailableReason: row.unavailableReason ?? unavailableReason,\n },\n );\n}\n\nfunction effectiveRows(props: ModelPolicyPickerProps): ClientPickerModelRow[] {\n const rows = props.rows !== undefined ? props.rows : projectClientModelRows(props.models ?? []);\n const messages = { ...defaultModelPolicyPickerMessages, ...props.messages };\n return applyCodexOnly(rows, props.codexOnly === true, messages.codexOnly);\n}\n\nfunction defaultNavState(rows: ClientPickerModelRow[], modelId: string): PickerNavState {\n const selected = findPickerRow(rows, modelId);\n if (!selected) {\n const rails = new Set(rows.map((row) => row.billingClass));\n const onlyRail = rails.size === 1 ? rows[0]?.billingClass : null;\n return onlyRail\n ? { level: \"models\", rail: onlyRail, modelId: null }\n : { level: \"providers\", rail: null, modelId: null };\n }\n return {\n level: \"thinking\",\n rail: selected.billingClass,\n modelId: selected.id,\n };\n}\n\nfunction usePickerNavState(\n sessionKey: string | undefined,\n rows: ClientPickerModelRow[],\n model: string,\n): [PickerNavState, (next: NavUpdater) => void] {\n const [state, setState] = useState<PickerNavState>(() => defaultNavState(rows, model));\n useEffect(() => {\n setState(defaultNavState(rows, model));\n // The menu deliberately preserves its drill-down while the same session is mounted.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [sessionKey]);\n return [state, setState];\n}\n\nexport function PickerNavRow(props: {\n label: string;\n hint?: string | undefined;\n icon?: ReactNode;\n trailing?: ReactNode;\n showChevron?: boolean | undefined;\n disabled?: boolean | undefined;\n title?: string | undefined;\n active?: boolean | undefined;\n testId?: string | undefined;\n onClick: () => void;\n}) {\n return (\n <button\n type=\"button\"\n disabled={props.disabled}\n title={props.title}\n onClick={props.onClick}\n data-testid={props.testId}\n className={cn(\n \"flex w-full cursor-pointer items-center gap-2 rounded-og-sm px-[var(--og-model-picker-row-padding-x)] py-[var(--og-model-picker-row-padding-y)] text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\",\n props.disabled && \"cursor-not-allowed opacity-50\",\n )}\n >\n {props.icon}\n <span className=\"min-w-0 flex-1\">\n <span className={cn(\"block truncate text-og-menu\", props.active && \"font-medium\")}>\n {props.label}\n </span>\n {props.hint ? (\n <span className=\"mt-0.5 block truncate text-og-control text-og-fg-subtle\">\n {props.hint}\n </span>\n ) : null}\n </span>\n {props.trailing ? <span className=\"ml-auto shrink-0\">{props.trailing}</span> : null}\n {props.showChevron === false ? null : (\n <ChevronRightIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n )}\n </button>\n );\n}\n\nexport function PickerBackHeader(props: {\n label: string;\n icon?: ReactNode;\n onBack: () => void;\n trailing?: ReactNode;\n}) {\n return (\n <div className=\"mb-1 flex items-center gap-0.5 border-b border-og-border/70 px-0.5 pb-1.5\">\n <button\n type=\"button\"\n onClick={props.onBack}\n data-testid=\"model-picker-back\"\n className=\"flex min-w-0 flex-1 cursor-pointer items-center gap-1.5 rounded-og-sm px-1.5 py-1.5 text-left text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40\"\n >\n <ChevronLeftIcon className=\"size-3.5 shrink-0 text-og-fg-subtle\" />\n {props.icon}\n <span className=\"min-w-0 flex-1 truncate text-og-menu font-medium\">{props.label}</span>\n </button>\n {props.trailing ? <div className=\"relative z-10 shrink-0\">{props.trailing}</div> : null}\n </div>\n );\n}\n\nexport function PickerAnimatedPage(props: {\n pageKey: string;\n direction: 1 | -1;\n children: ReactNode;\n}) {\n const reduceMotion = useReducedMotion();\n return (\n <motion.div\n key={props.pageKey}\n initial={reduceMotion ? false : { x: props.direction * 14, opacity: 0 }}\n animate={{ x: 0, opacity: 1 }}\n transition={{ duration: reduceMotion ? 0 : 0.18, ease: SLIDE_EASE }}\n className=\"w-full\"\n >\n {props.children}\n </motion.div>\n );\n}\n\nexport function ModelPolicyPickerMenu(\n props: ModelPolicyPickerProps & {\n nav?: PickerNavState | undefined;\n onNavChange?: ((next: NavUpdater) => void) | undefined;\n },\n) {\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const rows = effectiveRows(props);\n const groups = useMemo(() => groupPickerRowsByBillingClass(rows), [rows]);\n const [localNav, setLocalNav] = usePickerNavState(\n props.nav === undefined ? props.sessionKey : undefined,\n rows,\n props.model,\n );\n const nav = props.nav ?? localNav;\n const setNav = props.onNavChange ?? setLocalNav;\n const [direction, setDirection] = useState<1 | -1>(1);\n\n const effectiveNav =\n nav.level === \"providers\" && groups.length === 1\n ? {\n level: \"models\" as const,\n rail: groups[0]!.billingClass,\n modelId: null,\n }\n : nav;\n const activeGroup =\n effectiveNav.rail === null\n ? undefined\n : groups.find((group) => group.billingClass === effectiveNav.rail);\n const focusModel =\n effectiveNav.modelId === null ? undefined : findPickerRow(rows, effectiveNav.modelId);\n const selectedRow = findPickerRow(rows, props.model);\n\n const go = (next: PickerNavState, nextDirection: 1 | -1) => {\n setDirection(nextDirection);\n setNav(next);\n };\n\n const selectEffort = (row: ClientPickerModelRow, effort: ReasoningEffort) => {\n if (!row.selectable) return;\n if (row.id !== props.model) props.onModelChange(row.id);\n props.onEffortChange(effort);\n };\n\n const pageKey =\n effectiveNav.level === \"providers\"\n ? \"providers\"\n : effectiveNav.level === \"models\"\n ? `models:${effectiveNav.rail ?? \"\"}`\n : `thinking:${effectiveNav.modelId ?? \"\"}`;\n\n let body: ReactNode;\n if (props.loading) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.loading}</p>;\n } else if (groups.length === 0) {\n body = <p className=\"px-2 py-3 text-og-control text-og-fg-subtle\">{messages.noModels}</p>;\n } else if (effectiveNav.level === \"providers\") {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n active={selectedRow?.billingClass === group.billingClass}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n } else if (effectiveNav.level === \"models\" && activeGroup) {\n body = (\n <div data-testid=\"model-picker-models\">\n {groups.length > 1 ? (\n <PickerBackHeader\n label={activeGroup.label}\n icon={<BillingClassMark billingClass={activeGroup.billingClass} />}\n onBack={() => go({ level: \"providers\", rail: null, modelId: null }, -1)}\n />\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {activeGroup.rows.map((row) => (\n <PickerNavRow\n key={`${row.billingClass}:${row.id}`}\n label={row.label}\n hint={row.unavailableReason ?? payerSummaryForModel(row.catalog)}\n disabled={!row.selectable}\n title={row.unavailableReason ?? undefined}\n active={row.id === props.model}\n testId={`model-picker-choice-${row.id}`}\n onClick={() => {\n if (row.selectable) {\n go(\n {\n level: \"thinking\",\n rail: row.billingClass,\n modelId: row.id,\n },\n 1,\n );\n }\n }}\n />\n ))}\n </div>\n </div>\n );\n } else if (effectiveNav.level === \"thinking\" && focusModel) {\n const activeModel = focusModel.id === props.model;\n const supportsFast = runnableLatencyModesForModel(focusModel.catalog).includes(\"fast\");\n body = (\n <div data-testid=\"model-picker-reasoning\">\n <PickerBackHeader\n label={focusModel.label}\n icon={<BillingClassMark billingClass={focusModel.billingClass} />}\n onBack={() => go({ level: \"models\", rail: focusModel.billingClass, modelId: null }, -1)}\n trailing={\n supportsFast && props.allowLatencyMode !== false ? (\n <button\n type=\"button\"\n disabled={!focusModel.selectable}\n aria-pressed={activeModel && props.latencyMode === \"fast\"}\n aria-label={\n activeModel && props.latencyMode === \"fast\"\n ? `Disable ${messages.fast}`\n : `Enable ${messages.fast}`\n }\n title={`${messages.fast} · ${messages.fastRateHint}`}\n data-testid=\"model-picker-fast\"\n onClick={() => {\n if (!activeModel) {\n props.onModelChange(focusModel.id);\n }\n props.onLatencyModeChange(\n activeModel && props.latencyMode === \"fast\" ? \"standard\" : \"fast\",\n );\n }}\n className={cn(\n \"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-og-sm text-og-fg-subtle outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n activeModel && props.latencyMode === \"fast\" && \"text-og-fg\",\n )}\n >\n <ZapIcon\n className={cn(\n \"size-3.5\",\n activeModel && props.latencyMode === \"fast\" && \"fill-current\",\n )}\n />\n </button>\n ) : null\n }\n />\n <p className=\"px-2.5 pt-1 pb-1 text-og-control font-medium tracking-wide text-og-fg-subtle uppercase\">\n {messages.thinking}\n </p>\n {focusModel.catalog.capabilities?.inputModalities.includes(\"image\") === false ? (\n <p className=\"px-2.5 pb-1.5 text-og-control leading-relaxed text-og-fg-subtle\">\n Unsupported attachments stay in the session but are hidden from this model.\n </p>\n ) : null}\n <div className=\"flex flex-col gap-0.5\">\n {effortOptionsForModel(focusModel.catalog).map((effort) => {\n const selected = activeModel && effort === props.effort;\n return (\n <button\n key={effort}\n type=\"button\"\n disabled={!focusModel.selectable}\n onClick={() => selectEffort(focusModel, effort)}\n className={cn(\n \"flex h-[var(--og-model-picker-effort-height)] w-full cursor-pointer items-center rounded-og-sm px-[var(--og-model-picker-row-padding-x)] text-left text-og-menu text-og-fg outline-hidden transition-colors hover:bg-og-surface-2 focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50\",\n selected && \"bg-og-surface-2\",\n )}\n >\n <span className=\"min-w-0 flex-1 truncate\">{labelReasoningEffort(effort)}</span>\n {selected ? (\n <CheckIcon\n className=\"ml-auto size-3.5 shrink-0\"\n data-testid=\"model-picker-effort-check\"\n />\n ) : null}\n </button>\n );\n })}\n </div>\n </div>\n );\n } else {\n body = (\n <div className=\"flex flex-col gap-0.5\" data-testid=\"model-picker-providers\">\n {groups.map((group) => (\n <PickerNavRow\n key={group.billingClass}\n label={group.label}\n hint={messages.billingHints[group.billingClass]}\n icon={<BillingClassMark billingClass={group.billingClass} />}\n testId={`model-picker-rail-${group.billingClass}`}\n onClick={() => go({ level: \"models\", rail: group.billingClass, modelId: null }, 1)}\n />\n ))}\n </div>\n );\n }\n\n return (\n <div data-testid=\"model-picker-menu\" className=\"relative overflow-hidden\">\n {props.error ? (\n <p className=\"px-2 py-1 text-og-control text-og-status-failed\" role=\"alert\">\n {props.error}\n </p>\n ) : null}\n <PickerAnimatedPage pageKey={pageKey} direction={direction}>\n {body}\n </PickerAnimatedPage>\n </div>\n );\n}\n\n/** @internal Shared state seam kept outside the Radix portal for deterministic testing. */\nexport function useModelPolicyPickerState(props: ModelPolicyPickerProps) {\n const [uncontrolledOpen, setUncontrolledOpen] = useState(props.defaultOpen ?? false);\n const open = props.open ?? uncontrolledOpen;\n const rows = effectiveRows(props);\n const [nav, setNav] = usePickerNavState(props.sessionKey, rows, props.model);\n const previousControlledOpen = useRef(props.open);\n useEffect(() => {\n const wasOpen = previousControlledOpen.current;\n previousControlledOpen.current = props.open;\n if (props.open === true && wasOpen !== true) {\n setNav(defaultNavState(rows, props.model));\n }\n // Reset only on the controlled closed-to-open edge; ordinary rerenders\n // deliberately preserve the current drill-down page.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [props.open]);\n const setOpen = (next: boolean) => {\n if (next) {\n setNav(defaultNavState(rows, props.model));\n }\n if (props.open === undefined) setUncontrolledOpen(next);\n props.onOpenChange?.(next);\n };\n return { open, setOpen, rows, nav, setNav };\n}\n\nexport function ModelPolicyPicker(props: ModelPolicyPickerProps) {\n const trigger = usePortalTokenSource<HTMLButtonElement>();\n const portalStyle = usePortalTokenStyle(trigger.source);\n const messages = useMemo(\n () => ({ ...defaultModelPolicyPickerMessages, ...props.messages }),\n [props.messages],\n );\n const { open, setOpen, rows, nav, setNav } = useModelPolicyPickerState(props);\n const selected = findPickerRow(rows, props.model);\n\n if (props.loading) {\n return (\n <span\n className={cn(\n \"og-root inline-flex h-8 w-40 shrink-0 animate-pulse rounded-full bg-og-surface-2\",\n props.className,\n )}\n aria-label={messages.loading}\n data-testid=\"model-picker-loading\"\n />\n );\n }\n return (\n <DropdownMenu.Root open={open} onOpenChange={setOpen}>\n <DropdownMenu.Trigger asChild>\n <button\n ref={trigger.ref}\n type=\"button\"\n disabled={props.disabled}\n aria-label={messages.label}\n className={cn(\n \"og-root og-model-policy-trigger inline-flex h-[var(--og-model-picker-trigger-height)] min-w-0 max-w-64 items-center gap-1 rounded-full border border-transparent px-2.5 text-og-control text-og-fg-muted outline-hidden transition-colors hover:border-og-border hover:bg-og-surface-2 hover:text-og-fg focus-visible:ring-2 focus-visible:ring-og-accent/40 disabled:cursor-not-allowed disabled:opacity-50 max-sm:h-11 max-sm:max-w-[7.5rem] max-sm:px-2\",\n props.className,\n )}\n >\n <BillingClassMark\n billingClass={selected?.billingClass ?? billingClassForMissingSelection(props.model)}\n className=\"text-og-fg\"\n />\n <span className=\"og-model-policy-label-full min-w-0 truncate font-medium text-og-fg max-sm:hidden\">\n {selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-label-short min-w-0 truncate font-medium text-og-fg sm:hidden\">\n {selected?.shortLabel ?? selected?.label ?? props.model}\n </span>\n <span className=\"og-model-policy-effort max-sm:hidden\">\n {labelReasoningEffort(props.effort)}\n </span>\n {props.latencyMode === \"fast\" ? (\n <ZapIcon\n className=\"size-3.5 shrink-0 fill-current stroke-current text-og-fg\"\n aria-label={messages.fast}\n data-testid=\"model-picker-fast-icon\"\n />\n ) : null}\n <ChevronDownIcon className=\"size-3 shrink-0\" />\n </button>\n </DropdownMenu.Trigger>\n <DropdownMenu.Portal>\n <DropdownMenu.Content\n align=\"start\"\n side={props.menuSide ?? \"bottom\"}\n sideOffset={8}\n collisionPadding={12}\n onCloseAutoFocus={(event) => event.preventDefault()}\n className={cn(\n \"og-root og-model-policy-menu z-50 flex max-h-[min(20rem,var(--radix-dropdown-menu-content-available-height))] w-[var(--og-model-picker-menu-width)] max-w-[calc(100vw-16px)] flex-col overflow-hidden rounded-og-lg border border-og-border bg-og-surface-1 p-[var(--og-model-picker-menu-padding)] text-og-fg shadow-og-lg\",\n props.contentClassName,\n )}\n style={{ ...portalStyle, ...props.contentStyle }}\n data-testid=\"model-picker-content\"\n >\n <div className=\"min-h-0 flex-1 overflow-y-auto overscroll-contain\">\n <ModelPolicyPickerMenu {...props} nav={nav} onNavChange={setNav} />\n </div>\n </DropdownMenu.Content>\n </DropdownMenu.Portal>\n </DropdownMenu.Root>\n );\n}\n"],"mappings":"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","names":[]}
|