@opengeni/react 3.1.0 → 3.3.2-canary.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/dist/accounts.js +87 -11
  2. package/dist/accounts.js.map +1 -1
  3. package/dist/{browser-viewer-AEFHUYZK.js → browser-viewer-MKKFRRRV.js} +3 -3
  4. package/dist/{chunk-YFKJ7VZH.js → chunk-7EAFGICR.js} +45 -11
  5. package/dist/chunk-7EAFGICR.js.map +1 -0
  6. package/dist/{chunk-W6NFJXSA.js → chunk-7KBJLTKS.js} +52 -12
  7. package/dist/chunk-7KBJLTKS.js.map +1 -0
  8. package/dist/{chunk-YJFGAFBD.js → chunk-E5KISKFG.js} +241 -97
  9. package/dist/chunk-E5KISKFG.js.map +1 -0
  10. package/dist/{chunk-MZ2G2XBA.js → chunk-IOLVN74D.js} +31 -4
  11. package/dist/chunk-IOLVN74D.js.map +1 -0
  12. package/dist/{chunk-D3UIAJKY.js → chunk-JAQ5K7W6.js} +3 -2
  13. package/dist/{chunk-D3UIAJKY.js.map → chunk-JAQ5K7W6.js.map} +1 -1
  14. package/dist/{chunk-LAKLF4PA.js → chunk-VTOREEXF.js} +8 -7
  15. package/dist/chunk-VTOREEXF.js.map +1 -0
  16. package/dist/{chunk-X4A66EWZ.js → chunk-X5S426G2.js} +38 -44
  17. package/dist/chunk-X5S426G2.js.map +1 -0
  18. package/dist/{chunk-HQVCKCLG.js → chunk-YG4QEQF5.js} +354 -255
  19. package/dist/chunk-YG4QEQF5.js.map +1 -0
  20. package/dist/{chunk-XXWH2JLC.js → chunk-YVCHAQZY.js} +2 -2
  21. package/dist/{chunk-RGALCTWO.js → chunk-ZKNTK7IA.js} +13 -9
  22. package/dist/{chunk-RGALCTWO.js.map → chunk-ZKNTK7IA.js.map} +1 -1
  23. package/dist/{chunk-OAM4WAAM.js → chunk-ZPBTI3TE.js} +2 -2
  24. package/dist/components/fleet-tile.d.ts +2 -2
  25. package/dist/components/model-policy-picker.d.ts +9 -0
  26. package/dist/components/tip-follow.d.ts +16 -0
  27. package/dist/composer.js +6 -6
  28. package/dist/hooks/use-file-attachments.d.ts +2 -0
  29. package/dist/hooks/use-session-events.d.ts +3 -3
  30. package/dist/index.js +77 -69
  31. package/dist/index.js.map +1 -1
  32. package/dist/interaction.js +2 -2
  33. package/dist/machines.js +2 -2
  34. package/dist/model-policy.d.ts +2 -1
  35. package/dist/model-policy.js +1 -1
  36. package/dist/realtime.js +47 -8
  37. package/dist/realtime.js.map +1 -1
  38. package/dist/session-ui.js +4 -4
  39. package/dist/session.js +4 -4
  40. package/dist/timeline/entrance.d.ts +13 -1
  41. package/package.json +3 -2
  42. package/src/accounts.tsx +116 -14
  43. package/src/components/composer.tsx +15 -5
  44. package/src/components/fleet-tile.tsx +4 -15
  45. package/src/components/message-timeline.tsx +237 -101
  46. package/src/components/model-policy-picker.tsx +59 -13
  47. package/src/components/tip-follow.ts +52 -0
  48. package/src/components/tooltip.tsx +38 -5
  49. package/src/hooks/use-available-models.ts +20 -14
  50. package/src/hooks/use-composer.ts +49 -3
  51. package/src/hooks/use-file-attachments.ts +28 -2
  52. package/src/hooks/use-goal.ts +31 -7
  53. package/src/hooks/use-last-started-turn-policy.ts +11 -5
  54. package/src/hooks/use-session-events.ts +186 -61
  55. package/src/hooks/use-session-lineage.ts +20 -16
  56. package/src/hooks/use-session.ts +23 -19
  57. package/src/hooks/use-workspace-sessions.ts +31 -27
  58. package/src/lib/format.ts +8 -6
  59. package/src/model-policy.ts +45 -54
  60. package/src/realtime/realtime-control.tsx +55 -6
  61. package/src/timeline/entrance.tsx +28 -3
  62. package/src/timeline/projection.ts +4 -0
  63. package/styles/compiled.css +5 -0
  64. package/dist/chunk-HQVCKCLG.js.map +0 -1
  65. package/dist/chunk-LAKLF4PA.js.map +0 -1
  66. package/dist/chunk-MZ2G2XBA.js.map +0 -1
  67. package/dist/chunk-W6NFJXSA.js.map +0 -1
  68. package/dist/chunk-X4A66EWZ.js.map +0 -1
  69. package/dist/chunk-YFKJ7VZH.js.map +0 -1
  70. package/dist/chunk-YJFGAFBD.js.map +0 -1
  71. /package/dist/{browser-viewer-AEFHUYZK.js.map → browser-viewer-MKKFRRRV.js.map} +0 -0
  72. /package/dist/{chunk-XXWH2JLC.js.map → chunk-YVCHAQZY.js.map} +0 -0
  73. /package/dist/{chunk-OAM4WAAM.js.map → chunk-ZPBTI3TE.js.map} +0 -0
@@ -1,16 +1,17 @@
1
1
  // src/lib/format.ts
2
2
  import { OpenGeniApiError } from "@opengeni/sdk";
3
+ var clockTimeFormatter = new Intl.DateTimeFormat(void 0, {
4
+ month: "short",
5
+ day: "numeric",
6
+ hour: "numeric",
7
+ minute: "2-digit"
8
+ });
3
9
  function formatClockTime(iso) {
4
10
  const then = new Date(iso);
5
11
  if (Number.isNaN(then.getTime())) {
6
12
  return "";
7
13
  }
8
- return then.toLocaleString(void 0, {
9
- month: "short",
10
- day: "numeric",
11
- hour: "numeric",
12
- minute: "2-digit"
13
- });
14
+ return clockTimeFormatter.format(then);
14
15
  }
15
16
  function formatRelativeTime(iso, now = /* @__PURE__ */ new Date()) {
16
17
  const then = new Date(iso).getTime();
@@ -138,4 +139,4 @@ export {
138
139
  isCreditExhaustion,
139
140
  humanizeFailureReason
140
141
  };
141
- //# sourceMappingURL=chunk-LAKLF4PA.js.map
142
+ //# sourceMappingURL=chunk-VTOREEXF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/format.ts"],"sourcesContent":["import { OpenGeniApiError } from \"@opengeni/sdk\";\n\nconst clockTimeFormatter = new Intl.DateTimeFormat(undefined, {\n month: \"short\",\n day: \"numeric\",\n hour: \"numeric\",\n minute: \"2-digit\",\n});\n\n/** Local finished-at stamp for message footers: \"Aug 2, 3:42 PM\" / locale-equivalent. */\nexport function formatClockTime(iso: string): string {\n const then = new Date(iso);\n if (Number.isNaN(then.getTime())) {\n return \"\";\n }\n return clockTimeFormatter.format(then);\n}\n\n/** Compact relative time: \"now\", \"42s\", \"7m\", \"3h\", \"2d\", then a date. */\nexport function formatRelativeTime(iso: string, now: Date = new Date()): string {\n const then = new Date(iso).getTime();\n if (Number.isNaN(then)) {\n return \"\";\n }\n const seconds = Math.max(0, Math.floor((now.getTime() - then) / 1000));\n if (seconds < 10) {\n return \"now\";\n }\n if (seconds < 60) {\n return `${seconds}s`;\n }\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) {\n return `${minutes}m`;\n }\n const hours = Math.floor(minutes / 60);\n if (hours < 24) {\n return `${hours}h`;\n }\n const days = Math.floor(hours / 24);\n if (days < 14) {\n return `${days}d`;\n }\n return new Date(iso).toLocaleDateString();\n}\n\n/** Human-readable byte size: \"512 B\", \"8.0 KB\", \"1.4 MB\", \"3 GB\". */\nexport function formatBytes(bytes: number): string {\n if (bytes < 1024) {\n return `${bytes} B`;\n }\n const units = [\"KB\", \"MB\", \"GB\"] as const;\n let value = bytes / 1024;\n for (const unit of units) {\n if (value < 1024 || unit === \"GB\") {\n return `${value.toFixed(value < 10 ? 1 : 0)} ${unit}`;\n }\n value /= 1024;\n }\n return `${bytes} B`;\n}\n\n/** Single-line preview of arbitrary text, for tiles and collapsed rows. */\nexport function truncate(text: string, maxLength: number): string {\n const collapsed = text.replace(/\\s+/g, \" \").trim();\n if (collapsed.length <= maxLength) {\n return collapsed;\n }\n return `${collapsed.slice(0, Math.max(0, maxLength - 1)).trimEnd()}…`;\n}\n\n/** Render an unknown payload as readable text (pretty JSON when possible). */\nexport function stringifyPayload(value: unknown): string {\n if (value === null || value === undefined) {\n return \"\";\n }\n if (typeof value === \"string\") {\n const parsed = tryParseJson(value);\n if (parsed !== undefined && typeof parsed === \"object\") {\n return stringifyPayload(parsed);\n }\n return value;\n }\n try {\n return JSON.stringify(value, null, 2) ?? String(value);\n } catch {\n return String(value);\n }\n}\n\n/** JSON.parse that returns `undefined` instead of throwing. */\nexport function tryParseJson(text: string): unknown {\n const trimmed = text.trim();\n if (!trimmed.startsWith(\"{\") && !trimmed.startsWith(\"[\") && !trimmed.startsWith('\"')) {\n return undefined;\n }\n try {\n return JSON.parse(trimmed) as unknown;\n } catch {\n return undefined;\n }\n}\n\n/**\n * The canonical credit-death sentence. Credit exhaustion is the one failure a\n * user can fix themselves — the copy must say what happened (empty balance),\n * what to do (add credits), and what is safe (nothing was lost). Crucially it\n * must NOT say \"send a message to revive\": a revive turn burns credits the\n * workspace no longer has.\n */\nexport const CREDIT_EXHAUSTION_MESSAGE =\n \"Out of OpenGeni credits — this workspace's balance is empty. Add credits to continue; the conversation is preserved.\";\n\n/**\n * Actionable composer copy for an edge rejection before a turn is accepted.\n * Unlike an in-flight credit exhaustion, this path has not consumed the\n * actor-private draft or any finalized attachment.\n */\nexport const COMPOSER_PAYMENT_REQUIRED_MESSAGE =\n \"This turn requires OpenGeni managed credits, but the account balance is empty. Add credits or choose a connected Codex subscription model, then retry. Your draft and attachments are preserved.\";\n\nexport function composerSubmissionErrorMessage(error: Error): string {\n return error instanceof OpenGeniApiError &&\n error.status === 402 &&\n error.code === \"payment_required\"\n ? COMPOSER_PAYMENT_REQUIRED_MESSAGE\n : error.message;\n}\n\n/**\n * Does this failure/completion payload (or raw error string) mean the\n * workspace ran out of OpenGeni credits? Matches the engine's\n * \"insufficient OpenGeni credits\" text (case-insensitive, substring — it\n * arrives both bare and wrapped in \"Activity task failed: …\") and the\n * budget-exhausted segment limit the engine stamps on a turn it ended early.\n */\nexport function isCreditExhaustion(\n input: { error?: string | null; detail?: string | null; segmentLimit?: string | null } | string,\n): boolean {\n if (typeof input === \"string\") {\n return input.toLowerCase().includes(\"insufficient opengeni credits\");\n }\n if (input.segmentLimit === \"budget_exhausted\") {\n return true;\n }\n for (const text of [input.error, input.detail]) {\n if (typeof text === \"string\" && text.toLowerCase().includes(\"insufficient opengeni credits\")) {\n return true;\n }\n }\n return false;\n}\n\n/**\n * Humanize engine/provider failure text before it reaches the timeline or a\n * failure banner. Raw provider errors leak the wrong audience's instructions —\n * \"Incorrect API key … find your API key at platform.openai.com\" tells a\n * managed-deployment USER to fix credentials only an OPERATOR controls (and is\n * flatly wrong for Azure or subscription-backed engines). Auth, quota, and\n * credit-exhaustion failures collapse to one neutral, honest sentence; every\n * other reason passes through untouched. Raw payloads stay available in the\n * debug surfaces.\n */\nexport function humanizeFailureReason(reason: string | null): string | null {\n if (!reason) {\n return reason;\n }\n if (isCreditExhaustion(reason)) {\n return CREDIT_EXHAUSTION_MESSAGE;\n }\n const normalized = reason.toLowerCase();\n const authFailure =\n normalized.includes(\"incorrect api key\") ||\n normalized.includes(\"invalid api key\") ||\n normalized.includes(\"invalid_api_key\") ||\n normalized.includes(\"platform.openai.com/account/api-keys\") ||\n (normalized.includes(\"401\") &&\n (normalized.includes(\"api key\") || normalized.includes(\"unauthorized\")));\n if (authFailure) {\n return \"The model provider rejected this deployment's engine credentials. Sending messages won't help until the deployment's engine configuration is fixed.\";\n }\n const quotaFailure =\n normalized.includes(\"insufficient_quota\") || normalized.includes(\"exceeded your current quota\");\n if (quotaFailure) {\n return \"The model provider refused the request: this deployment's provider quota is exhausted.\";\n }\n return reason;\n}\n"],"mappings":"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","names":[]}
@@ -1,17 +1,14 @@
1
1
  // src/model-policy.ts
2
- var BILLING_CLASS_ORDER = [
3
- "opengeni_credits",
4
- "external",
5
- "codex_subscription",
6
- "supergrok_subscription",
7
- "byok"
8
- ];
2
+ import {
3
+ compareModelPickerOrder,
4
+ modelPickerBillingClassFor
5
+ } from "@opengeni/sdk/model-picker-order";
9
6
  var BILLING_CLASS_LABELS = {
10
7
  opengeni_credits: "OpenGeni",
11
8
  external: "External",
12
9
  codex_subscription: "Codex",
13
10
  supergrok_subscription: "SuperGrok",
14
- byok: "Your Gateway"
11
+ byok: "Workspace providers"
15
12
  };
16
13
  var AVAILABILITY_REASON_LABELS = {
17
14
  missing_credential: "Credentials required",
@@ -23,31 +20,10 @@ var AVAILABILITY_REASON_LABELS = {
23
20
  unsupported: "Unsupported"
24
21
  };
25
22
  function billingClassForModel(model) {
26
- if (model.billing?.metering === "external" && model.billing.upstreamPayer === "deployment") {
27
- return "external";
28
- }
29
- if (model.source === "supergrok") {
30
- return "supergrok_subscription";
31
- }
32
- if (model.source === "codex") {
33
- return "codex_subscription";
34
- }
35
- if (model.source === "workspace_gateway") {
36
- return "byok";
37
- }
38
- if (model.credentialSource?.kind === "connected_subscription") {
39
- return model.credentialSource.provider === "xai" ? "supergrok_subscription" : "codex_subscription";
40
- }
41
- if (model.credentialSource?.kind === "workspace_connection") {
23
+ if (model.provider === "workspace-gateway" || model.provider === "workspace-openrouter") {
42
24
  return "byok";
43
25
  }
44
- if (model.billing?.upstreamPayer === "connected_subscription") {
45
- return "codex_subscription";
46
- }
47
- if (model.billing?.upstreamPayer === "workspace") {
48
- return "byok";
49
- }
50
- return "opengeni_credits";
26
+ return modelPickerBillingClassFor(model);
51
27
  }
52
28
  function billingClassLabel(billingClass) {
53
29
  return BILLING_CLASS_LABELS[billingClass];
@@ -101,6 +77,18 @@ function labelReasoningEffort(effort) {
101
77
  return effort.slice(0, 1).toUpperCase() + effort.slice(1);
102
78
  }
103
79
  function payerSummaryForModel(model) {
80
+ if (model.cost === "free") {
81
+ return "Free in this deployment";
82
+ }
83
+ if (model.cost === "credits") {
84
+ return "OpenGeni credits";
85
+ }
86
+ if (model.cost === "subscription") {
87
+ return model.source === "supergrok" ? "SuperGrok subscription \xB7 external billing" : "Codex subscription \xB7 external billing";
88
+ }
89
+ if (model.cost === "workspace") {
90
+ return workspaceProviderPayerSummary(model);
91
+ }
104
92
  const billing = model.billing;
105
93
  if (!billing) {
106
94
  return "Route unknown";
@@ -112,7 +100,7 @@ function payerSummaryForModel(model) {
112
100
  return model.source === "supergrok" ? "SuperGrok subscription \xB7 external billing" : "Codex subscription \xB7 external billing";
113
101
  }
114
102
  if (billing.upstreamPayer === "workspace") {
115
- return "Billed to your AI Gateway";
103
+ return workspaceProviderPayerSummary(model);
116
104
  }
117
105
  return "External provider \xB7 no OpenGeni credits";
118
106
  }
@@ -125,13 +113,28 @@ function advancedSourceSummary(model) {
125
113
  return source.provider === "xai" ? "Connected SuperGrok subscription" : "Connected Codex subscription";
126
114
  }
127
115
  if (source.kind === "workspace_connection") {
128
- return "Workspace AI Gateway";
116
+ if (model.provider === "workspace-openrouter") {
117
+ return "Workspace OpenRouter connection";
118
+ }
119
+ if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
120
+ return "Workspace Vercel AI Gateway";
121
+ }
122
+ return "Workspace provider connection";
129
123
  }
130
124
  if (source.kind === "deployment") {
131
125
  return source.mechanism === "azure_ad_bearer" ? "Deployment Azure identity" : "Deployment API key";
132
126
  }
133
127
  return null;
134
128
  }
129
+ function workspaceProviderPayerSummary(model) {
130
+ if (model.provider === "workspace-openrouter") {
131
+ return "Billed to the workspace OpenRouter account";
132
+ }
133
+ if (model.provider === "workspace-gateway" || model.source === "workspace_gateway") {
134
+ return "Billed to the workspace Vercel account";
135
+ }
136
+ return "Billed to the workspace provider account";
137
+ }
135
138
  function projectPickerRows(models) {
136
139
  return models.filter((catalog) => {
137
140
  const billingClass = billingClassForModel(catalog);
@@ -170,16 +173,7 @@ function projectClientModelRows(models) {
170
173
  });
171
174
  }
172
175
  function sortPickerRows(rows) {
173
- return [...rows].sort((left, right) => {
174
- const classDelta = BILLING_CLASS_ORDER.indexOf(left.billingClass) - BILLING_CLASS_ORDER.indexOf(right.billingClass);
175
- if (classDelta !== 0) {
176
- return classDelta;
177
- }
178
- if (left.selectable !== right.selectable) {
179
- return left.selectable ? -1 : 1;
180
- }
181
- return left.label.localeCompare(right.label);
182
- });
176
+ return [...rows].sort(compareModelPickerOrder);
183
177
  }
184
178
  function findPickerRow(rows, modelId) {
185
179
  return rows.find((row) => row.id === modelId) ?? null;
@@ -220,4 +214,4 @@ export {
220
214
  findPickerRow,
221
215
  groupPickerRowsByBillingClass
222
216
  };
223
- //# sourceMappingURL=chunk-X4A66EWZ.js.map
217
+ //# sourceMappingURL=chunk-X5S426G2.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/model-policy.ts"],"sourcesContent":["import {\n compareModelPickerOrder,\n modelPickerBillingClassFor,\n type ModelPickerBillingClass,\n} from \"@opengeni/sdk/model-picker-order\";\nimport type { ClientModel, ReasoningEffort, WorkspaceModelCatalogModel } from \"@opengeni/sdk\";\n\nexport type PickerBillingClass = ModelPickerBillingClass;\n\nexport type PickerModelRow<TCatalog extends ClientModel = WorkspaceModelCatalogModel> = {\n id: string;\n label: string;\n /** Catalog-curated compact label for dense UI; fall back to `label` when absent. */\n shortLabel?: string | undefined;\n billingClass: PickerBillingClass;\n billingClassLabel: string;\n selectable: boolean;\n unavailableReason: string | null;\n provider: string;\n providerLabel: string;\n catalog: TCatalog;\n};\n\nexport type LatencyModeId = \"standard\" | \"priority\" | \"fast\";\n\nconst BILLING_CLASS_LABELS: Record<PickerBillingClass, string> = {\n opengeni_credits: \"OpenGeni\",\n external: \"External\",\n codex_subscription: \"Codex\",\n supergrok_subscription: \"SuperGrok\",\n byok: \"Workspace providers\",\n};\n\nconst AVAILABILITY_REASON_LABELS: Record<string, string> = {\n missing_credential: \"Credentials required\",\n needs_reauth: \"Reconnect required\",\n credential_not_ready: \"Credential not ready\",\n not_entitled: \"Not entitled\",\n provider_unhealthy: \"Provider unavailable\",\n policy_blocked: \"Blocked by workspace policy\",\n unsupported: \"Unsupported\",\n};\n\nexport function billingClassForModel(model: ClientModel): PickerBillingClass {\n if (model.provider === \"workspace-gateway\" || model.provider === \"workspace-openrouter\") {\n return \"byok\";\n }\n return modelPickerBillingClassFor(model);\n}\n\nexport function billingClassLabel(billingClass: PickerBillingClass): string {\n return BILLING_CLASS_LABELS[billingClass];\n}\n\nexport function availabilityReasonLabel(\n reason: WorkspaceModelCatalogModel[\"availability\"][\"reason\"],\n): string | null {\n if (!reason) {\n return null;\n }\n return AVAILABILITY_REASON_LABELS[reason] ?? \"Unavailable\";\n}\n\nexport function effortOptionsForModel(model: ClientModel): ReasoningEffort[] {\n const efforts = model.capabilities?.reasoning.efforts;\n if (!efforts || efforts.length === 0) {\n return [\"low\"];\n }\n const order: ReasoningEffort[] = [\"none\", \"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"];\n return order.filter((effort) => efforts.includes(effort));\n}\n\nexport function defaultEffortForModel(model: ClientModel): ReasoningEffort {\n const options = effortOptionsForModel(model);\n const configured = model.capabilities?.reasoning.defaultEffort;\n if (configured && options.includes(configured)) {\n return configured;\n }\n return options[0] ?? \"low\";\n}\n\nexport function coerceReasoningEffortForModel(\n model: ClientModel,\n effort: ReasoningEffort,\n): ReasoningEffort {\n const options = effortOptionsForModel(model);\n if (options.includes(effort)) {\n return effort;\n }\n return defaultEffortForModel(model);\n}\n\nexport function runnableLatencyModesForModel(model: ClientModel): LatencyModeId[] {\n const modes = model.capabilities?.latencyModes ?? [];\n return modes.filter((mode) => mode.runnable).map((mode) => mode.id);\n}\n\nexport function labelLatencyMode(mode: LatencyModeId): string {\n if (mode === \"fast\") {\n return \"Fast\";\n }\n if (mode === \"priority\") {\n return \"Priority\";\n }\n return \"Standard\";\n}\n\nexport function labelReasoningEffort(effort: ReasoningEffort): string {\n if (effort === \"xhigh\") {\n return \"Extra high\";\n }\n return effort.slice(0, 1).toUpperCase() + effort.slice(1);\n}\n\nexport function payerSummaryForModel(model: ClientModel): string {\n if (model.cost === \"free\") {\n return \"Free in this deployment\";\n }\n if (model.cost === \"credits\") {\n return \"OpenGeni credits\";\n }\n if (model.cost === \"subscription\") {\n return model.source === \"supergrok\"\n ? \"SuperGrok subscription · external billing\"\n : \"Codex subscription · external billing\";\n }\n if (model.cost === \"workspace\") {\n return workspaceProviderPayerSummary(model);\n }\n\n // Older client-config payloads do not carry `cost`; preserve their existing\n // settlement-derived label until every deployment has rolled forward.\n const billing = model.billing;\n if (!billing) {\n return \"Route unknown\";\n }\n if (billing.metering === \"opengeni_credits\") {\n return \"OpenGeni credits · automatic managed route\";\n }\n if (billing.upstreamPayer === \"connected_subscription\") {\n return model.source === \"supergrok\"\n ? \"SuperGrok subscription · external billing\"\n : \"Codex subscription · external billing\";\n }\n if (billing.upstreamPayer === \"workspace\") {\n return workspaceProviderPayerSummary(model);\n }\n return \"External provider · no OpenGeni credits\";\n}\n\nexport function advancedSourceSummary(model: ClientModel): string | null {\n const source = model.credentialSource;\n if (!source) {\n return model.billing?.metering === \"external\" && model.billing.upstreamPayer === \"deployment\"\n ? \"Deployment route · no authentication\"\n : null;\n }\n if (source.kind === \"connected_subscription\") {\n return source.provider === \"xai\"\n ? \"Connected SuperGrok subscription\"\n : \"Connected Codex subscription\";\n }\n if (source.kind === \"workspace_connection\") {\n if (model.provider === \"workspace-openrouter\") {\n return \"Workspace OpenRouter connection\";\n }\n if (model.provider === \"workspace-gateway\" || model.source === \"workspace_gateway\") {\n return \"Workspace Vercel AI Gateway\";\n }\n return \"Workspace provider connection\";\n }\n if (source.kind === \"deployment\") {\n return source.mechanism === \"azure_ad_bearer\"\n ? \"Deployment Azure identity\"\n : \"Deployment API key\";\n }\n return null;\n}\n\nfunction workspaceProviderPayerSummary(model: ClientModel): string {\n if (model.provider === \"workspace-openrouter\") {\n return \"Billed to the workspace OpenRouter account\";\n }\n if (model.provider === \"workspace-gateway\" || model.source === \"workspace_gateway\") {\n return \"Billed to the workspace Vercel account\";\n }\n return \"Billed to the workspace provider account\";\n}\n\nexport function projectPickerRows(models: WorkspaceModelCatalogModel[]): PickerModelRow[] {\n return models\n .filter((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return billingClass === \"opengeni_credits\" || catalog.credentialReadiness.status === \"ready\";\n })\n .map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n ...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: catalog.availability.selectable,\n unavailableReason: catalog.availability.selectable\n ? null\n : availabilityReasonLabel(catalog.availability.reason),\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\n/** Project the lightweight client-config model list into the same picker contract. */\nexport function projectClientModelRows(models: ClientModel[]): PickerModelRow<ClientModel>[] {\n return models.map((catalog) => {\n const billingClass = billingClassForModel(catalog);\n return {\n id: catalog.id,\n label: catalog.label,\n ...(catalog.shortLabel ? { shortLabel: catalog.shortLabel } : {}),\n billingClass,\n billingClassLabel: billingClassLabel(billingClass),\n selectable: true,\n unavailableReason: null,\n provider: catalog.provider,\n providerLabel: catalog.providerLabel,\n catalog,\n };\n });\n}\n\nexport function sortPickerRows<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): PickerModelRow<TCatalog>[] {\n return [...rows].sort(compareModelPickerOrder);\n}\n\nexport function findPickerRow<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n modelId: string,\n): PickerModelRow<TCatalog> | null {\n return rows.find((row) => row.id === modelId) ?? null;\n}\n\nexport function groupPickerRowsByBillingClass(\n rows: PickerModelRow[],\n): Array<{ billingClass: PickerBillingClass; label: string; rows: PickerModelRow[] }>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}>;\nexport function groupPickerRowsByBillingClass<TCatalog extends ClientModel>(\n rows: PickerModelRow<TCatalog>[],\n): Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n}> {\n const sorted = sortPickerRows(rows);\n const groups: Array<{\n billingClass: PickerBillingClass;\n label: string;\n rows: PickerModelRow<TCatalog>[];\n }> = [];\n for (const row of sorted) {\n const existing = groups.find((group) => group.billingClass === row.billingClass);\n if (existing) {\n existing.rows.push(row);\n continue;\n }\n groups.push({\n billingClass: row.billingClass,\n label: row.billingClassLabel,\n rows: [row],\n });\n }\n return groups;\n}\n"],"mappings":"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","names":[]}