@opengeni/react 3.3.2 → 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.
Files changed (54) hide show
  1. package/dist/artifacts.js +12 -12
  2. package/dist/{browser-viewer-MKKFRRRV.js → browser-viewer-E5DDEFQ4.js} +3 -3
  3. package/dist/{chunk-2DZVYHVO.js → chunk-4JFUT22W.js} +39 -13
  4. package/dist/chunk-4JFUT22W.js.map +1 -0
  5. package/dist/{chunk-ELIYWBZR.js → chunk-BV5A4OTE.js} +4 -4
  6. package/dist/{chunk-3AGBGTRT.js → chunk-IB27C53Y.js} +130 -16
  7. package/dist/chunk-IB27C53Y.js.map +1 -0
  8. package/dist/{chunk-JAQ5K7W6.js → chunk-TJFMMDQU.js} +56 -53
  9. package/dist/chunk-TJFMMDQU.js.map +1 -0
  10. package/dist/{chunk-XYRDDSXF.js → chunk-UNCXXIQR.js} +16 -12
  11. package/dist/{chunk-XYRDDSXF.js.map → chunk-UNCXXIQR.js.map} +1 -1
  12. package/dist/{chunk-ZPBTI3TE.js → chunk-W2RGYBCV.js} +4 -4
  13. package/dist/{chunk-WSK7G5LE.js → chunk-W3OUAH6M.js} +80 -80
  14. package/dist/chunk-W3OUAH6M.js.map +1 -0
  15. package/dist/{chunk-X4A66EWZ.js → chunk-WMGF4Z7X.js} +41 -43
  16. package/dist/chunk-WMGF4Z7X.js.map +1 -0
  17. package/dist/{chunk-YG4QEQF5.js → chunk-WY3MELVJ.js} +7769 -7753
  18. package/dist/chunk-WY3MELVJ.js.map +1 -0
  19. package/dist/composer.js +4 -4
  20. package/dist/{computer-viewer-FWEZISW4.js → computer-viewer-7TEZC6Y2.js} +3 -3
  21. package/dist/hooks/use-file-attachments.d.ts +2 -0
  22. package/dist/hooks/use-session-events.d.ts +1 -1
  23. package/dist/index.js +85 -87
  24. package/dist/index.js.map +1 -1
  25. package/dist/interaction.js +3 -3
  26. package/dist/model-policy.d.ts +2 -1
  27. package/dist/model-policy.js +1 -1
  28. package/dist/{queue-surface-implementation-BSKUWHRI.js → queue-surface-implementation-XL2A6PBM.js} +2 -2
  29. package/dist/realtime.js +2 -2
  30. package/dist/session-ui.js +6 -6
  31. package/dist/session.js +4 -4
  32. package/dist/timeline/types.d.ts +2 -0
  33. package/package.json +2 -2
  34. package/src/components/composer.tsx +15 -5
  35. package/src/components/message-timeline.tsx +16 -2
  36. package/src/components/model-policy-picker.tsx +37 -10
  37. package/src/components/sandbox-workspace.tsx +7 -6
  38. package/src/hooks/use-file-attachments.ts +28 -2
  39. package/src/hooks/use-session-events.ts +168 -8
  40. package/src/model-policy.ts +48 -53
  41. package/src/timeline/projection.ts +6 -1
  42. package/src/timeline/types.ts +2 -0
  43. package/styles/compiled.css +5 -0
  44. package/dist/chunk-2DZVYHVO.js.map +0 -1
  45. package/dist/chunk-3AGBGTRT.js.map +0 -1
  46. package/dist/chunk-JAQ5K7W6.js.map +0 -1
  47. package/dist/chunk-WSK7G5LE.js.map +0 -1
  48. package/dist/chunk-X4A66EWZ.js.map +0 -1
  49. package/dist/chunk-YG4QEQF5.js.map +0 -1
  50. /package/dist/{browser-viewer-MKKFRRRV.js.map → browser-viewer-E5DDEFQ4.js.map} +0 -0
  51. /package/dist/{chunk-ELIYWBZR.js.map → chunk-BV5A4OTE.js.map} +0 -0
  52. /package/dist/{chunk-ZPBTI3TE.js.map → chunk-W2RGYBCV.js.map} +0 -0
  53. /package/dist/{computer-viewer-FWEZISW4.js.map → computer-viewer-7TEZC6Y2.js.map} +0 -0
  54. /package/dist/{queue-surface-implementation-BSKUWHRI.js.map → queue-surface-implementation-XL2A6PBM.js.map} +0 -0
@@ -1,4 +1,9 @@
1
- import type { SessionEvent, SessionStatus, StreamConnectionState } from "@opengeni/sdk";
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 payload = asRecord(event.payload);
1382
- const coalescedUntil = Number(payload.coalescedUntil);
1383
- return Math.max(event.sequence, Number.isFinite(coalescedUntil) ? Math.floor(coalescedUntil) : 0);
1384
- }
1385
-
1386
- function asRecord(value: unknown): Record<string, unknown> {
1387
- return value && typeof value === "object" ? (value as Record<string, unknown>) : {};
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 {
@@ -1,11 +1,11 @@
1
+ import {
2
+ compareModelPickerOrder,
3
+ modelPickerBillingClassFor,
4
+ type ModelPickerBillingClass,
5
+ } from "@opengeni/sdk/model-picker-order";
1
6
  import type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from "@opengeni/sdk";
2
7
 
3
- export type PickerBillingClass =
4
- | "opengeni_credits"
5
- | "external"
6
- | "codex_subscription"
7
- | "supergrok_subscription"
8
- | "byok";
8
+ export type PickerBillingClass = ModelPickerBillingClass;
9
9
 
10
10
  export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {
11
11
  id: string;
@@ -23,20 +23,13 @@ export type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogM
23
23
 
24
24
  export type LatencyModeId = "standard" | "priority" | "fast";
25
25
 
26
- const BILLING_CLASS_ORDER: PickerBillingClass[] = [
27
- "opengeni_credits",
28
- "external",
29
- "codex_subscription",
30
- "supergrok_subscription",
31
- "byok",
32
- ];
33
-
34
26
  const BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {
35
27
  opengeni_credits: "OpenGeni",
36
28
  external: "External",
37
29
  codex_subscription: "Codex",
38
30
  supergrok_subscription: "SuperGrok",
39
- byok: "Your Gateway",
31
+ byok: "Workspace providers",
32
+ organization_byok: "Organization providers",
40
33
  };
41
34
 
42
35
  const AVAILABILITY_REASON_LABELS: Record<string, string> = {
@@ -50,33 +43,13 @@ const AVAILABILITY_REASON_LABELS: Record<string, string> = {
50
43
  };
51
44
 
52
45
  export function billingClassForModel(model: ClientModel): PickerBillingClass {
53
- if (model.billing?.metering === "external" && model.billing.upstreamPayer === "deployment") {
54
- return "external";
55
- }
56
- if (model.source === "supergrok") {
57
- return "supergrok_subscription";
46
+ if (model.provider === "organization-gateway" || model.provider === "organization-openrouter") {
47
+ return "organization_byok";
58
48
  }
59
- if (model.source === "codex") {
60
- return "codex_subscription";
61
- }
62
- if (model.source === "workspace_gateway") {
49
+ if (model.provider === "workspace-gateway" || model.provider === "workspace-openrouter") {
63
50
  return "byok";
64
51
  }
65
- if (model.credentialSource?.kind === "connected_subscription") {
66
- return model.credentialSource.provider === "xai"
67
- ? "supergrok_subscription"
68
- : "codex_subscription";
69
- }
70
- if (model.credentialSource?.kind === "workspace_connection") {
71
- return "byok";
72
- }
73
- if (model.billing?.upstreamPayer === "connected_subscription") {
74
- return "codex_subscription";
75
- }
76
- if (model.billing?.upstreamPayer === "workspace") {
77
- return "byok";
78
- }
79
- return "opengeni_credits";
52
+ return modelPickerBillingClassFor(model);
80
53
  }
81
54
 
82
55
  export function billingClassLabel(billingClass: PickerBillingClass): string {
@@ -144,6 +117,23 @@ export function labelReasoningEffort(effort: ReasoningEffort): string {
144
117
  }
145
118
 
146
119
  export function payerSummaryForModel(model: ClientModel): string {
120
+ if (model.cost === "free") {
121
+ return "Free in this deployment";
122
+ }
123
+ if (model.cost === "credits") {
124
+ return "OpenGeni credits";
125
+ }
126
+ if (model.cost === "subscription") {
127
+ return model.source === "supergrok"
128
+ ? "SuperGrok subscription · external billing"
129
+ : "Codex subscription · external billing";
130
+ }
131
+ if (model.cost === "workspace") {
132
+ return workspaceProviderPayerSummary(model);
133
+ }
134
+
135
+ // Older client-config payloads do not carry `cost`; preserve their existing
136
+ // settlement-derived label until every deployment has rolled forward.
147
137
  const billing = model.billing;
148
138
  if (!billing) {
149
139
  return "Route unknown";
@@ -157,7 +147,7 @@ export function payerSummaryForModel(model: ClientModel): string {
157
147
  : "Codex subscription · external billing";
158
148
  }
159
149
  if (billing.upstreamPayer === "workspace") {
160
- return "Billed to your AI Gateway";
150
+ return workspaceProviderPayerSummary(model);
161
151
  }
162
152
  return "External provider · no OpenGeni credits";
163
153
  }
@@ -175,7 +165,13 @@ export function advancedSourceSummary(model: ClientModel): string | null {
175
165
  : "Connected Codex subscription";
176
166
  }
177
167
  if (source.kind === "workspace_connection") {
178
- return "Workspace AI Gateway";
168
+ if (model.provider === "workspace-openrouter") {
169
+ return "Workspace OpenRouter connection";
170
+ }
171
+ if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
172
+ return "Workspace Vercel AI Gateway";
173
+ }
174
+ return "Workspace provider connection";
179
175
  }
180
176
  if (source.kind === "deployment") {
181
177
  return source.mechanism === "azure_ad_bearer"
@@ -185,6 +181,16 @@ export function advancedSourceSummary(model: ClientModel): string | null {
185
181
  return null;
186
182
  }
187
183
 
184
+ function workspaceProviderPayerSummary(model: ClientModel): string {
185
+ if (model.provider === "workspace-openrouter") {
186
+ return "Billed to the workspace OpenRouter account";
187
+ }
188
+ if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
189
+ return "Billed to the workspace Vercel account";
190
+ }
191
+ return "Billed to the workspace provider account";
192
+ }
193
+
188
194
  export function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerModelRow[] {
189
195
  return models
190
196
  .filter((catalog) => {
@@ -232,18 +238,7 @@ export function projectClientModelRows(models: ClientModel[]): PickerModelRow<Cl
232
238
  export function sortPickerRows<TCatalog extends ClientModel>(
233
239
  rows: PickerModelRow<TCatalog>[],
234
240
  ): PickerModelRow<TCatalog>[] {
235
- return [...rows].sort((left, right) => {
236
- const classDelta =
237
- BILLING_CLASS_ORDER.indexOf(left.billingClass) -
238
- BILLING_CLASS_ORDER.indexOf(right.billingClass);
239
- if (classDelta !== 0) {
240
- return classDelta;
241
- }
242
- if (left.selectable !== right.selectable) {
243
- return left.selectable ? -1 : 1;
244
- }
245
- return left.label.localeCompare(right.label);
246
- });
241
+ return [...rows].sort(compareModelPickerOrder);
247
242
  }
248
243
 
249
244
  export function findPickerRow<TCatalog extends ClientModel>(
@@ -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
- reason: authNeededReason(payload.reason),
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,
@@ -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[];
@@ -4495,6 +4495,11 @@
4495
4495
  max-width: 12rem;
4496
4496
  }
4497
4497
  }
4498
+ :where(.og-root).sm\:max-w-\[32rem\],:where(.og-root) .sm\:max-w-\[32rem\] {
4499
+ @media (width >= 40rem) {
4500
+ max-width: 32rem;
4501
+ }
4502
+ }
4498
4503
  :where(.og-root).sm\:max-w-\[82\%\],:where(.og-root) .sm\:max-w-\[82\%\] {
4499
4504
  @media (width >= 40rem) {
4500
4505
  max-width: 82%;
@@ -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 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 your AI Gateway\",\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: \"Bring your own key\",\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 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 { level: \"thinking\", rail: selected.billingClass, modelId: selected.id };\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 ? { level: \"models\" as const, rail: groups[0]!.billingClass, modelId: null }\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 ?? undefined}\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({ level: \"thinking\", rail: row.billingClass, modelId: row.id }, 1);\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={\n selected?.billingClass ??\n (props.model.startsWith(\"codex/\") ? \"codex_subscription\" : \"opengeni_credits\")\n }\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":[]}