@opengeni/config 0.22.5 → 1.0.0-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/index.js CHANGED
@@ -45,6 +45,10 @@ import { createHash } from "crypto";
45
45
  import { z } from "zod";
46
46
  var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
47
47
  var registryId = /^[A-Za-z0-9_-]+$/;
48
+ var DEFAULT_OPENROUTER_MODEL_ID = "openrouter/nvidia/nemotron-3-super-120b-a12b:free";
49
+ var DEFAULT_MODEL_COST_POLICY_JSON = JSON.stringify({
50
+ [DEFAULT_OPENROUTER_MODEL_ID]: "free"
51
+ });
48
52
  var SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS = 60 * 6e4;
49
53
  var SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS = 1e4;
50
54
  var SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS = SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS - SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS;
@@ -177,8 +181,16 @@ var McpServerConnectionRefSchema = z.object({
177
181
  seen.add(key);
178
182
  }
179
183
  }).optional(),
184
+ authoritySource: z.literal("host").optional(),
180
185
  subjectScope: z.enum(["workspace", "subject"]).optional()
181
186
  }).strict().superRefine((reference, context) => {
187
+ if (reference.authoritySource === "host" && !reference.connectionId) {
188
+ context.addIssue({
189
+ code: "custom",
190
+ message: "host authority requires connectionId",
191
+ path: ["connectionId"]
192
+ });
193
+ }
182
194
  if (!reference.selectedResources) return;
183
195
  if (!reference.connectionId) {
184
196
  context.addIssue({
@@ -195,6 +207,7 @@ var McpServerConnectionRefSchema = z.object({
195
207
  });
196
208
  }
197
209
  });
210
+ var DEFAULT_MODAL_IMAGE_REF = "opengenipublicneuacr.azurecr.io/opengeni-desktop@sha256:c3bd17b8841de1bff9bb2777aad422cf8c75de78e2c30f9ac81d0cd6810a1b78";
198
211
  var SettingsSchema = z.object({
199
212
  serviceName: z.string().default("opengeni"),
200
213
  environment: z.string().default("local"),
@@ -251,6 +264,12 @@ var SettingsSchema = z.object({
251
264
  analyticsPosthogHost: z.string().url().max(2048).optional(),
252
265
  analyticsGa4MeasurementId: z.string().max(32).regex(/^G-[A-Z0-9]+$/u).optional(),
253
266
  publicBaseUrl: z.string().url().optional(),
267
+ // Standards-based OAuth authorization server for external workspace MCP
268
+ // clients. Opt-in because it creates a new public authentication surface.
269
+ mcpOauthEnabled: EnvBoolean.default(false),
270
+ // Forwarded client addresses are ignored by default. Operators may trust an
271
+ // exact number of proxy hops only when direct access to the API is blocked.
272
+ mcpOauthTrustedProxyHops: z.coerce.number().int().min(0).max(16).default(0),
254
273
  // Browser origin when the web app and API use separate origins in local
255
274
  // development. Production normally leaves this unset and uses publicBaseUrl.
256
275
  webBaseUrl: z.string().url().optional(),
@@ -384,6 +403,13 @@ var SettingsSchema = z.object({
384
403
  // into @opengeni/db once at boot.
385
404
  // Env: OPENGENI_CHILD_LIFECYCLE_NOTICES_ENABLED.
386
405
  childLifecycleNoticesEnabled: EnvBoolean.default(false),
406
+ // Explicit host-owned MCP connection authority is a rolling protocol
407
+ // activation. Keep it off while any API, worker, or browser bundle predates
408
+ // the authority discriminator; enable it only after the whole fleet runs an
409
+ // image that understands host refs. Legacy markerless non-UUID refs remain a
410
+ // separate compatibility lane for already-persisted embedding integrations.
411
+ // Env: OPENGENI_HOST_MCP_AUTHORITY_SOURCE_ADMISSION_ENABLED.
412
+ hostMcpAuthoritySourceAdmissionEnabled: EnvBoolean.default(false),
387
413
  // Per-channel and per-DM Slack workspace routing. Default ON. A channel does
388
414
  // not count a personal workspace as a candidate, so an organization with one
389
415
  // shared workspace resolves it as the sole candidate and never asks; the
@@ -511,6 +537,24 @@ var SettingsSchema = z.object({
511
537
  // keep subscription model routing while disabling Codex voice input.
512
538
  voiceInputCodexExperimentalEnabled: EnvBoolean.default(false),
513
539
  modelPricingJson: z.string().default("{}"),
540
+ // Supported-model membership source. Database mode is resolved by the async
541
+ // core overlay; getSettings remains synchronous and env-only.
542
+ modelCatalogSource: z.enum(["code", "database"]).default("code"),
543
+ // Deployment-owned workspace-facing price policy. This is deliberately
544
+ // separate from catalog membership and upstream credential ownership.
545
+ // Shape: { "product/model-id": "free" | "credits" }.
546
+ modelCostPolicyJson: z.string().default("{}"),
547
+ // Optional per-product agent guidance. Database mode replaces this with the
548
+ // singleton document's validated modelNotes map.
549
+ modelNotesJson: z.string().default("{}"),
550
+ // Managed OpenRouter credential. The curated model table is injected in
551
+ // code/catalog-document resolution and never read from host provider JSON.
552
+ openrouterApiKey: z.string().optional(),
553
+ // Internal, secret-free catalog overlays populated only by
554
+ // applyModelCatalogDocument. They intentionally have no OPENGENI_* env
555
+ // binding so database mode cannot be bypassed with a second source.
556
+ resolvedGatewayModelsJson: z.string().optional(),
557
+ resolvedOpenRouterModelsJson: z.string().optional(),
514
558
  // Extra (non-built-in) model providers, declared by the host as a JSON
515
559
  // provider registry. Each entry carries its own base URL, API key, wire API
516
560
  // ("responses" | "chat") and the models it exposes. The models a client may
@@ -552,11 +596,6 @@ var SettingsSchema = z.object({
552
596
  // the Codex rollout so an emergency Codex opt-out cannot disable every model.
553
597
  // OPENGENI_LAZY_TOOL_SEARCH_ENABLED
554
598
  lazyToolSearchEnabled: EnvBoolean.default(true),
555
- // credential allocator atomic, workspace-local credential allocation. Default OFF is a
556
- // deliberate rolling-deploy fence: migrate + roll every worker first, then
557
- // enable. Turning it off restores the legacy sticky selector without a schema
558
- // rollback; the additive lease table/cursor columns become inert.
559
- codexCredentialLeasingEnabled: EnvBoolean.default(false),
560
599
  // Decision-observability fence. When enabled, the worker emits one
561
600
  // bounded, metadata-only adaptive-policy replay record alongside the unchanged
562
601
  // sticky-sharded decision. It never changes placement/admission/failover.
@@ -956,6 +995,13 @@ var SettingsSchema = z.object({
956
995
  // treated exactly like a failed best-effort snapshot. Knob:
957
996
  // OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
958
997
  sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().max(SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS).default(6e4),
998
+ // A zero-holder drain may need substantially longer than a best-effort
999
+ // mid-turn/turn-end snapshot for a very large workspace. Keep that provider
1000
+ // budget independent so increasing drain recovery headroom cannot pin an
1001
+ // ordinary turn finalizer for the same duration. Unset preserves the legacy
1002
+ // single-budget behavior. Knob:
1003
+ // OPENGENI_SANDBOX_DRAIN_SNAPSHOT_TIMEOUT_MS.
1004
+ sandboxDrainSnapshotTimeoutMs: z.coerce.number().int().positive().max(SANDBOX_SNAPSHOT_MAX_TIMEOUT_MS).optional(),
959
1005
  // Begin a controlled snapshot/quiesce/drain/rematerialize transition this far
960
1006
  // ahead of a finite provider deadline. Modal's 24h creation clock cannot be
961
1007
  // extended; the logical sandbox outlives it by moving to one successor box.
@@ -1073,6 +1119,15 @@ var SettingsSchema = z.object({
1073
1119
  // Rolling browser login-slot compatibility. Repository/deployment default is
1074
1120
  // deliberately legacy; changing to broker is an operator-authorized rollout.
1075
1121
  managedAuthSessionSetMode: z.enum(["legacy", "dual", "broker"]).default("legacy"),
1122
+ // Query transport is an explicit second-stage rollout. A pre-compatibility
1123
+ // web image understands only fragment bearers, so API replicas must keep
1124
+ // generating fragment links until the compatible web fleet has converged.
1125
+ organizationUserSetupEmailTokenTransport: z.enum(["fragment", "query"]).default("fragment"),
1126
+ // Query-bearing setup links may appear in controller/edge error logs even
1127
+ // when access logs and request tracing are disabled. This explicit operator
1128
+ // confirmation keeps query transport fail closed until that separate sink is
1129
+ // proven sanitized.
1130
+ organizationUserSetupQueryEdgeSanitizationConfirmed: EnvBoolean.default(false),
1076
1131
  resendApiKey: z.string().optional(),
1077
1132
  emailFrom: z.string().default("OpenGeni <auth@mail.opengeni.ai>"),
1078
1133
  stripeSecretKey: z.string().optional(),
@@ -1227,6 +1282,7 @@ function voiceInputDeploymentConfigured(settings) {
1227
1282
  var ModelPricingSchema = z.object({
1228
1283
  inputMicrosPerMillionTokens: z.number().int().nonnegative(),
1229
1284
  cachedInputMicrosPerMillionTokens: z.number().int().nonnegative().optional(),
1285
+ cacheWriteMicrosPerMillionTokens: z.number().int().nonnegative().optional(),
1230
1286
  outputMicrosPerMillionTokens: z.number().int().nonnegative(),
1231
1287
  marginBps: z.number().int().min(0).max(1e5).optional()
1232
1288
  });
@@ -1362,7 +1418,10 @@ var RegistryProviderKind = z.enum([
1362
1418
  "codex-subscription",
1363
1419
  "xai-subscription",
1364
1420
  "vercel-gateway-managed",
1365
- "vercel-gateway-workspace"
1421
+ "vercel-gateway-workspace",
1422
+ "vercel-gateway-organization",
1423
+ "openrouter-workspace",
1424
+ "openrouter-organization"
1366
1425
  ]);
1367
1426
  var RegistryModelSchema = z.object({
1368
1427
  id: z.string().min(1),
@@ -1475,6 +1534,297 @@ var RegistryProviderSchema = z.object({
1475
1534
  });
1476
1535
  }
1477
1536
  });
1537
+ var OPENGENI_GATEWAY_PROVIDER_ID = "opengeni-gateway";
1538
+ var WORKSPACE_GATEWAY_PROVIDER_ID = "workspace-gateway";
1539
+ var WORKSPACE_GATEWAY_MODEL_ID_PREFIX = "workspace-gateway/";
1540
+ var ORGANIZATION_GATEWAY_PROVIDER_ID = "organization-gateway";
1541
+ var ORGANIZATION_GATEWAY_MODEL_ID_PREFIX = "organization-gateway/";
1542
+ var OPENROUTER_PROVIDER_ID = "openrouter";
1543
+ var OPENROUTER_MODEL_ID_PREFIX = "openrouter/";
1544
+ var WORKSPACE_OPENROUTER_PROVIDER_ID = "workspace-openrouter";
1545
+ var WORKSPACE_OPENROUTER_MODEL_ID_PREFIX = "workspace-openrouter/";
1546
+ var ORGANIZATION_OPENROUTER_PROVIDER_ID = "organization-openrouter";
1547
+ var ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX = "organization-openrouter/";
1548
+ var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
1549
+ var RESERVED_MODEL_PROVIDER_IDS = /* @__PURE__ */ new Set([
1550
+ "openai",
1551
+ "azure",
1552
+ CODEX_PROVIDER_ID,
1553
+ XAI_SUBSCRIPTION_PROVIDER_ID,
1554
+ OPENGENI_GATEWAY_PROVIDER_ID,
1555
+ WORKSPACE_GATEWAY_PROVIDER_ID,
1556
+ ORGANIZATION_GATEWAY_PROVIDER_ID,
1557
+ OPENROUTER_PROVIDER_ID,
1558
+ WORKSPACE_OPENROUTER_PROVIDER_ID,
1559
+ ORGANIZATION_OPENROUTER_PROVIDER_ID
1560
+ ]);
1561
+ var ModelCostClass = z.enum(["free", "credits"]);
1562
+ var ConfiguredModelCostClass = z.enum([
1563
+ "free",
1564
+ "credits",
1565
+ "subscription",
1566
+ "workspace",
1567
+ "organization"
1568
+ ]);
1569
+ var ModelNote = z.string().max(500).refine((value) => !/[\r\n|]/u.test(value), {
1570
+ message: "model notes must not contain newlines or the | field separator"
1571
+ });
1572
+ function parseModelCostPolicyJson(raw) {
1573
+ let parsed;
1574
+ try {
1575
+ parsed = JSON.parse(raw);
1576
+ } catch (error) {
1577
+ throw new Error(
1578
+ `OPENGENI_MODEL_COST_POLICY_JSON must be valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1579
+ { cause: error }
1580
+ );
1581
+ }
1582
+ return z.record(z.string().min(1), ModelCostClass).parse(parsed);
1583
+ }
1584
+ function parseModelNotesJson(raw) {
1585
+ let parsed;
1586
+ try {
1587
+ parsed = JSON.parse(raw);
1588
+ } catch (error) {
1589
+ throw new Error(
1590
+ `OPENGENI_MODEL_NOTES_JSON must be valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1591
+ { cause: error }
1592
+ );
1593
+ }
1594
+ return z.record(z.string().min(1), ModelNote).parse(parsed);
1595
+ }
1596
+ function configuredModelNotes(settings) {
1597
+ return parseModelNotesJson(settings.modelNotesJson);
1598
+ }
1599
+ var GatewayCatalogModel = z.object({
1600
+ productId: z.string().min(1),
1601
+ workspaceProductId: z.string().min(1).startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX),
1602
+ upstreamModelId: z.string().min(1),
1603
+ label: z.string().min(1),
1604
+ shortLabel: z.string().min(1).max(64).optional(),
1605
+ providers: z.array(z.string().min(1)).min(1),
1606
+ implicitCaching: z.boolean().default(false),
1607
+ vision: z.boolean().default(false),
1608
+ inputFileMediaTypes: z.array(z.string().min(1)).default([]),
1609
+ contextWindowTokens: z.number().int().positive().default(1e6),
1610
+ effectiveContextWindowTokens: z.number().int().positive().default(9e5),
1611
+ autoCompactTokenLimit: z.number().int().positive().default(85e4),
1612
+ pricing: z.union([ModelPricingSchema, ModelPricingScheduleSchema]).optional(),
1613
+ credentialSource: z.never().optional(),
1614
+ billing: z.never().optional(),
1615
+ apiKey: z.never().optional()
1616
+ }).strict();
1617
+ var OpenRouterCatalogModel = z.object({
1618
+ upstreamModelId: z.string().min(1).endsWith(":free"),
1619
+ label: z.string().min(1),
1620
+ shortLabel: z.string().min(1).max(64).optional(),
1621
+ aliases: z.array(z.string().min(1)).default([]),
1622
+ capabilities: ModelCapabilitiesV1Schema,
1623
+ contextWindowTokens: z.number().int().positive().optional(),
1624
+ effectiveContextWindowTokens: z.number().int().positive().optional(),
1625
+ autoCompactTokenLimit: z.number().int().positive().optional(),
1626
+ toolOutputTruncationTokens: z.number().int().positive().optional(),
1627
+ credentialSource: z.never().optional(),
1628
+ billing: z.never().optional(),
1629
+ pricing: z.never().optional(),
1630
+ apiKey: z.never().optional()
1631
+ }).strict();
1632
+ var DeploymentRegistryBaseUrl = z.string().url().superRefine((value, context) => {
1633
+ const url = new URL(value);
1634
+ if (url.username || url.password) {
1635
+ context.addIssue({
1636
+ code: "custom",
1637
+ message: "database catalog provider baseUrl must not contain userinfo"
1638
+ });
1639
+ }
1640
+ if (url.search) {
1641
+ context.addIssue({
1642
+ code: "custom",
1643
+ message: "database catalog provider baseUrl must not contain a query"
1644
+ });
1645
+ }
1646
+ if (url.hash) {
1647
+ context.addIssue({
1648
+ code: "custom",
1649
+ message: "database catalog provider baseUrl must not contain a fragment"
1650
+ });
1651
+ }
1652
+ });
1653
+ var DeploymentRegistryProviderKind = z.enum(["api-key", "anonymous"]);
1654
+ var DeploymentRegistryModelSchema = RegistryModelSchema.safeExtend({
1655
+ pricing: z.never().optional()
1656
+ }).strict();
1657
+ var DeploymentRegistryProviderSchema = RegistryProviderSchema.safeExtend({
1658
+ kind: DeploymentRegistryProviderKind.default("api-key"),
1659
+ baseUrl: DeploymentRegistryBaseUrl,
1660
+ models: z.array(DeploymentRegistryModelSchema).min(1),
1661
+ apiKey: z.never().optional(),
1662
+ apiKeyEnv: z.never().optional(),
1663
+ defaultHeaders: z.never().optional(),
1664
+ defaultQuery: z.never().optional(),
1665
+ publicDefaultHeaderNames: z.never().optional(),
1666
+ publicDefaultQueryNames: z.never().optional()
1667
+ }).strict();
1668
+ var DeploymentGatewayCatalogModelSchema = GatewayCatalogModel.safeExtend({
1669
+ pricing: z.never().optional()
1670
+ }).strict();
1671
+ var ModelCatalogDocument = z.object({
1672
+ schemaVersion: z.literal(1),
1673
+ /** Canonical deployment default. Omission preserves the V1 first-built-in
1674
+ * fallback for existing documents; operators should set this explicitly
1675
+ * when cutting over a registry or connected-subscription default. */
1676
+ defaultModel: z.string().min(1).optional(),
1677
+ builtInModels: z.array(z.string().min(1)).min(1),
1678
+ registryProviders: z.array(DeploymentRegistryProviderSchema).default([]),
1679
+ gatewayModels: z.array(DeploymentGatewayCatalogModelSchema).default([]),
1680
+ openrouterModels: z.array(OpenRouterCatalogModel).default([]),
1681
+ modelNotes: z.record(z.string().min(1), ModelNote).default({}),
1682
+ billing: z.never().optional(),
1683
+ enabled: z.never().optional(),
1684
+ apiKey: z.never().optional(),
1685
+ bands: z.never().optional()
1686
+ }).strict().superRefine((document, context) => {
1687
+ const productIds = /* @__PURE__ */ new Set();
1688
+ const providerIds = /* @__PURE__ */ new Set();
1689
+ const gatewayUpstreamIds = /* @__PURE__ */ new Set();
1690
+ const add = (id, path) => {
1691
+ if (/[\u000A\u000D|]/u.test(id)) {
1692
+ context.addIssue({
1693
+ code: "custom",
1694
+ path,
1695
+ message: "catalog product ids must not contain newlines or the | field separator"
1696
+ });
1697
+ }
1698
+ if (productIds.has(id)) {
1699
+ context.addIssue({
1700
+ code: "custom",
1701
+ path,
1702
+ message: `duplicate product id ${id}`
1703
+ });
1704
+ }
1705
+ productIds.add(id);
1706
+ };
1707
+ document.builtInModels.forEach((id, index) => add(id, ["builtInModels", index]));
1708
+ document.registryProviders.forEach((provider, providerIndex) => {
1709
+ if (RESERVED_MODEL_PROVIDER_IDS.has(provider.id)) {
1710
+ context.addIssue({
1711
+ code: "custom",
1712
+ path: ["registryProviders", providerIndex, "id"],
1713
+ message: `provider id ${provider.id} is reserved for a reviewed OpenGeni provider`
1714
+ });
1715
+ }
1716
+ if (providerIds.has(provider.id)) {
1717
+ context.addIssue({
1718
+ code: "custom",
1719
+ path: ["registryProviders", providerIndex, "id"],
1720
+ message: `duplicate provider id ${provider.id}`
1721
+ });
1722
+ }
1723
+ providerIds.add(provider.id);
1724
+ provider.models.forEach(
1725
+ (model, modelIndex) => add(model.id, ["registryProviders", providerIndex, "models", modelIndex, "id"])
1726
+ );
1727
+ });
1728
+ document.gatewayModels.forEach((model, index) => {
1729
+ if (gatewayUpstreamIds.has(model.upstreamModelId)) {
1730
+ context.addIssue({
1731
+ code: "custom",
1732
+ path: ["gatewayModels", index, "upstreamModelId"],
1733
+ message: `duplicate Gateway upstream model id ${model.upstreamModelId}`
1734
+ });
1735
+ }
1736
+ gatewayUpstreamIds.add(model.upstreamModelId);
1737
+ add(model.productId, ["gatewayModels", index, "productId"]);
1738
+ add(model.workspaceProductId, ["gatewayModels", index, "workspaceProductId"]);
1739
+ });
1740
+ document.openrouterModels.forEach(
1741
+ (model, index) => add(`${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`, [
1742
+ "openrouterModels",
1743
+ index,
1744
+ "upstreamModelId"
1745
+ ])
1746
+ );
1747
+ if (document.defaultModel && /[\u000A\u000D|]/u.test(document.defaultModel)) {
1748
+ context.addIssue({
1749
+ code: "custom",
1750
+ path: ["defaultModel"],
1751
+ message: "catalog default model must not contain newlines or the | field separator"
1752
+ });
1753
+ }
1754
+ if (document.defaultModel && !productIds.has(document.defaultModel) && !document.defaultModel.startsWith(CODEX_MODEL_ID_PREFIX) && !document.defaultModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)) {
1755
+ context.addIssue({
1756
+ code: "custom",
1757
+ path: ["defaultModel"],
1758
+ message: "catalog default model must reference deployment catalog membership or a connected-subscription product"
1759
+ });
1760
+ }
1761
+ for (const productId of Object.keys(document.modelNotes)) {
1762
+ if (!productIds.has(productId)) {
1763
+ context.addIssue({
1764
+ code: "custom",
1765
+ path: ["modelNotes", productId],
1766
+ message: "model note references a product id outside the deployment catalog"
1767
+ });
1768
+ }
1769
+ }
1770
+ });
1771
+ function parseModelCatalogDocument(value) {
1772
+ return ModelCatalogDocument.parse(value);
1773
+ }
1774
+ function deploymentRegistryProvidersWithHostCredentials(settings, providers) {
1775
+ const hostProviders = new Map(
1776
+ parseModelProvidersJson(settings.modelProvidersJson).map((provider) => [provider.id, provider])
1777
+ );
1778
+ return providers.map((provider) => {
1779
+ if (provider.kind !== "api-key") return provider;
1780
+ const host = hostProviders.get(provider.id);
1781
+ if (!host || host.kind !== "api-key") {
1782
+ throw new Error(
1783
+ `database model catalog provider ${provider.id} has no matching host-authorized api-key transport`
1784
+ );
1785
+ }
1786
+ const transportIdentity = (candidate) => ({
1787
+ kind: candidate.kind,
1788
+ baseUrl: candidate.baseUrl,
1789
+ api: candidate.api,
1790
+ wireProfile: candidate.wireProfile
1791
+ });
1792
+ if (canonicalJson(transportIdentity(provider)) !== canonicalJson(transportIdentity(host))) {
1793
+ throw new Error(
1794
+ `database model catalog provider ${provider.id} does not match its host-authorized transport`
1795
+ );
1796
+ }
1797
+ return {
1798
+ ...provider,
1799
+ ...host.defaultHeaders === void 0 ? {} : { defaultHeaders: host.defaultHeaders },
1800
+ ...host.defaultQuery === void 0 ? {} : { defaultQuery: host.defaultQuery },
1801
+ ...host.publicDefaultHeaderNames === void 0 ? {} : { publicDefaultHeaderNames: host.publicDefaultHeaderNames },
1802
+ ...host.publicDefaultQueryNames === void 0 ? {} : { publicDefaultQueryNames: host.publicDefaultQueryNames },
1803
+ ...host.apiKey === void 0 ? {} : { apiKey: host.apiKey },
1804
+ ...host.apiKeyEnv === void 0 ? {} : { apiKeyEnv: host.apiKeyEnv }
1805
+ };
1806
+ });
1807
+ }
1808
+ function applyModelCatalogDocument(settings, rawDocument) {
1809
+ const document = parseModelCatalogDocument(rawDocument);
1810
+ const defaultModel = document.defaultModel ?? document.builtInModels[0];
1811
+ const resolved = {
1812
+ ...settings,
1813
+ openaiModel: defaultModel,
1814
+ // Keep the complete built-in membership, including the default. The worker
1815
+ // replaces openaiModel with the exact turn model; the run-scoped router
1816
+ // needs one stable built-in id in this allow-list so a bare provider model
1817
+ // is not temporarily claimed by OpenAI/Azure during name re-resolution.
1818
+ openaiAllowedModels: document.builtInModels.join(","),
1819
+ modelProvidersJson: JSON.stringify(
1820
+ deploymentRegistryProvidersWithHostCredentials(settings, document.registryProviders)
1821
+ ),
1822
+ resolvedGatewayModelsJson: JSON.stringify(document.gatewayModels),
1823
+ resolvedOpenRouterModelsJson: JSON.stringify(document.openrouterModels),
1824
+ modelNotesJson: JSON.stringify(document.modelNotes)
1825
+ };
1826
+ return resolved;
1827
+ }
1478
1828
  var IntegrationOAuthClientConfigSchema = z.object({
1479
1829
  clientId: z.string().min(1),
1480
1830
  clientSecret: z.string().min(1).optional(),
@@ -1482,11 +1832,10 @@ var IntegrationOAuthClientConfigSchema = z.object({
1482
1832
  });
1483
1833
  var VERCEL_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh/v1";
1484
1834
  var VERCEL_AI_GATEWAY_AI_SDK_BASE_URL = "https://ai-gateway.vercel.sh/v4/ai";
1485
- var OPENGENI_GATEWAY_PROVIDER_ID = "opengeni-gateway";
1486
- var WORKSPACE_GATEWAY_PROVIDER_ID = "workspace-gateway";
1487
- var WORKSPACE_GATEWAY_MODEL_ID_PREFIX = "workspace-gateway/";
1488
1835
  var VERCEL_AI_GATEWAY_CONNECTION_DOMAIN = "ai-gateway.vercel.sh";
1489
1836
  var VERCEL_AI_GATEWAY_CONNECTION_ROLE = "vercel_ai_gateway";
1837
+ var WORKSPACE_OPENROUTER_CONNECTION_DOMAIN = "openrouter.ai";
1838
+ var WORKSPACE_OPENROUTER_CONNECTION_ROLE = "openrouter";
1490
1839
  var CODEX_REALTIME_MODEL_ID = "gpt-live-1-boulder-alpha";
1491
1840
  var SUPERGROK_REALTIME_MODEL_ID = "supergrok/grok-voice-think-fast-2.0";
1492
1841
  var OPENGENI_REALTIME_MODEL_ID_PREFIX = "opengeni-gateway/";
@@ -1545,23 +1894,126 @@ var OPENGENI_GATEWAY_MODELS = {
1545
1894
  implicitCaching: true
1546
1895
  }
1547
1896
  };
1897
+ var OPENGENI_OPENROUTER_MODELS = [
1898
+ OpenRouterCatalogModel.parse({
1899
+ upstreamModelId: "nvidia/nemotron-3-super-120b-a12b:free",
1900
+ label: "Nemotron 3 Super 120B",
1901
+ shortLabel: "Nemotron 3 Super",
1902
+ aliases: [],
1903
+ capabilities: {
1904
+ reasoning: {
1905
+ upstream: "supported",
1906
+ // OpenRouter advertises the reasoning controls, but the catalogue does
1907
+ // not publish this model's accepted effort vocabulary. Preserve that
1908
+ // upstream fact without exposing an unverified runnable selector.
1909
+ runnable: false,
1910
+ efforts: [],
1911
+ defaultEffort: null,
1912
+ required: false
1913
+ },
1914
+ functionCalling: { upstream: "supported", runnable: true },
1915
+ structuredOutput: { upstream: "supported", runnable: true },
1916
+ hostedTools: {
1917
+ webSearch: { upstream: "unknown", runnable: false },
1918
+ xSearch: { upstream: "unknown", runnable: false },
1919
+ codeExecution: { upstream: "unknown", runnable: false },
1920
+ imageGeneration: { upstream: "unknown", runnable: false }
1921
+ },
1922
+ inputModalities: ["text"],
1923
+ inputFileMediaTypes: [],
1924
+ outputModalities: ["text"],
1925
+ transports: {
1926
+ sse: { upstream: "supported", runnable: true },
1927
+ responsesWebSocket: { upstream: "unknown", runnable: false },
1928
+ realtimeAudio: { upstream: "unsupported", runnable: false }
1929
+ },
1930
+ latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
1931
+ },
1932
+ contextWindowTokens: 262144,
1933
+ effectiveContextWindowTokens: 235929,
1934
+ autoCompactTokenLimit: 22e4
1935
+ })
1936
+ ];
1937
+ function defaultGatewayCatalogModels() {
1938
+ return [
1939
+ {
1940
+ ...OPENGENI_GATEWAY_MODELS.deepseek,
1941
+ vision: false,
1942
+ inputFileMediaTypes: [],
1943
+ contextWindowTokens: 1e6,
1944
+ effectiveContextWindowTokens: 9e5,
1945
+ autoCompactTokenLimit: 85e4
1946
+ },
1947
+ {
1948
+ ...OPENGENI_GATEWAY_MODELS.kimi,
1949
+ vision: true,
1950
+ inputFileMediaTypes: ["application/pdf"],
1951
+ contextWindowTokens: 1e6,
1952
+ effectiveContextWindowTokens: 9e5,
1953
+ autoCompactTokenLimit: 85e4
1954
+ }
1955
+ ].map((model) => GatewayCatalogModel.parse(model));
1956
+ }
1957
+ function configuredGatewayCatalogModels(settings) {
1958
+ if (settings.resolvedGatewayModelsJson === void 0) {
1959
+ return defaultGatewayCatalogModels();
1960
+ }
1961
+ return z.array(GatewayCatalogModel).parse(JSON.parse(settings.resolvedGatewayModelsJson));
1962
+ }
1963
+ function configuredGatewayUpstreamModelIds(settings) {
1964
+ return configuredGatewayCatalogModels(settings).map((model) => model.upstreamModelId);
1965
+ }
1966
+ function configuredGatewayWorkspaceProductModelIds(settings) {
1967
+ return configuredGatewayCatalogModels(settings).map((model) => model.workspaceProductId);
1968
+ }
1969
+ function configuredGatewayOrganizationProductModelIds(settings) {
1970
+ void settings;
1971
+ return [];
1972
+ }
1973
+ function configuredModelInputIdentities(settings) {
1974
+ return configuredModels(settings).flatMap((model) => [model.id, ...model.aliases]);
1975
+ }
1976
+ function configuredOpenRouterCatalogModels(settings) {
1977
+ if (settings.resolvedOpenRouterModelsJson === void 0) {
1978
+ return [...OPENGENI_OPENROUTER_MODELS];
1979
+ }
1980
+ return z.array(OpenRouterCatalogModel).parse(JSON.parse(settings.resolvedOpenRouterModelsJson));
1981
+ }
1982
+ function configuredOpenRouterUpstreamModelIds(settings) {
1983
+ return configuredOpenRouterCatalogModels(settings).map((model) => model.upstreamModelId);
1984
+ }
1985
+ function workspaceOpenRouterProductId(modelId) {
1986
+ return `${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${modelId.startsWith(OPENROUTER_MODEL_ID_PREFIX) ? modelId.slice(OPENROUTER_MODEL_ID_PREFIX.length) : modelId}`;
1987
+ }
1988
+ function configuredOpenRouterWorkspaceProductModelIds(settings) {
1989
+ return configuredOpenRouterCatalogModels(settings).flatMap(
1990
+ (model) => [model.upstreamModelId, ...model.aliases].map(workspaceOpenRouterProductId)
1991
+ );
1992
+ }
1993
+ function configuredOpenRouterOrganizationProductModelIds(settings) {
1994
+ void settings;
1995
+ return [];
1996
+ }
1548
1997
  var defaultModelPricing = {
1549
1998
  "gpt-5.6-sol": {
1550
1999
  default: {
1551
- inputMicrosPerMillionTokens: 5e6,
1552
- cachedInputMicrosPerMillionTokens: 5e5,
1553
- outputMicrosPerMillionTokens: 3e7,
1554
- marginBps: 2500
2000
+ // Promotional OpenAI pricing, guaranteed through at least 2026-11-21.
2001
+ inputMicrosPerMillionTokens: 4e6,
2002
+ cachedInputMicrosPerMillionTokens: 4e5,
2003
+ cacheWriteMicrosPerMillionTokens: 5e6,
2004
+ outputMicrosPerMillionTokens: 2e7,
2005
+ marginBps: 500
1555
2006
  },
1556
2007
  inputTokenTiers: [
1557
2008
  {
1558
2009
  // OpenAI: prompts with >272K input tokens use the long-context rate.
1559
2010
  minimumInputTokens: 272001,
1560
2011
  pricing: {
1561
- inputMicrosPerMillionTokens: 1e7,
1562
- cachedInputMicrosPerMillionTokens: 1e6,
1563
- outputMicrosPerMillionTokens: 45e6,
1564
- marginBps: 2500
2012
+ inputMicrosPerMillionTokens: 8e6,
2013
+ cachedInputMicrosPerMillionTokens: 8e5,
2014
+ cacheWriteMicrosPerMillionTokens: 1e7,
2015
+ outputMicrosPerMillionTokens: 3e7,
2016
+ marginBps: 500
1565
2017
  }
1566
2018
  }
1567
2019
  ]
@@ -1570,8 +2022,9 @@ var defaultModelPricing = {
1570
2022
  default: {
1571
2023
  inputMicrosPerMillionTokens: 2e6,
1572
2024
  cachedInputMicrosPerMillionTokens: 2e5,
2025
+ cacheWriteMicrosPerMillionTokens: 25e5,
1573
2026
  outputMicrosPerMillionTokens: 12e6,
1574
- marginBps: 2500
2027
+ marginBps: 500
1575
2028
  },
1576
2029
  inputTokenTiers: [
1577
2030
  {
@@ -1579,8 +2032,9 @@ var defaultModelPricing = {
1579
2032
  pricing: {
1580
2033
  inputMicrosPerMillionTokens: 4e6,
1581
2034
  cachedInputMicrosPerMillionTokens: 4e5,
2035
+ cacheWriteMicrosPerMillionTokens: 5e6,
1582
2036
  outputMicrosPerMillionTokens: 18e6,
1583
- marginBps: 2500
2037
+ marginBps: 500
1584
2038
  }
1585
2039
  }
1586
2040
  ]
@@ -1589,8 +2043,9 @@ var defaultModelPricing = {
1589
2043
  default: {
1590
2044
  inputMicrosPerMillionTokens: 2e5,
1591
2045
  cachedInputMicrosPerMillionTokens: 2e4,
2046
+ cacheWriteMicrosPerMillionTokens: 25e4,
1592
2047
  outputMicrosPerMillionTokens: 12e5,
1593
- marginBps: 2500
2048
+ marginBps: 500
1594
2049
  },
1595
2050
  inputTokenTiers: [
1596
2051
  {
@@ -1598,8 +2053,9 @@ var defaultModelPricing = {
1598
2053
  pricing: {
1599
2054
  inputMicrosPerMillionTokens: 4e5,
1600
2055
  cachedInputMicrosPerMillionTokens: 4e4,
2056
+ cacheWriteMicrosPerMillionTokens: 5e5,
1601
2057
  outputMicrosPerMillionTokens: 18e5,
1602
- marginBps: 2500
2058
+ marginBps: 500
1603
2059
  }
1604
2060
  }
1605
2061
  ]
@@ -1614,7 +2070,7 @@ var defaultModelPricing = {
1614
2070
  inputMicrosPerMillionTokens: 14e4,
1615
2071
  cachedInputMicrosPerMillionTokens: 28e3,
1616
2072
  outputMicrosPerMillionTokens: 28e4,
1617
- marginBps: 2500
2073
+ marginBps: 500
1618
2074
  }
1619
2075
  },
1620
2076
  [OPENGENI_GATEWAY_MODELS.kimi.productId]: {
@@ -1622,7 +2078,7 @@ var defaultModelPricing = {
1622
2078
  inputMicrosPerMillionTokens: 3e6,
1623
2079
  cachedInputMicrosPerMillionTokens: 3e5,
1624
2080
  outputMicrosPerMillionTokens: 15e6,
1625
- marginBps: 2500
2081
+ marginBps: 500
1626
2082
  }
1627
2083
  },
1628
2084
  // Fireworks AI / GLM 5.2 — the first shipped non-OpenAI registry model. A
@@ -1634,7 +2090,7 @@ var defaultModelPricing = {
1634
2090
  inputMicrosPerMillionTokens: 14e5,
1635
2091
  cachedInputMicrosPerMillionTokens: 14e4,
1636
2092
  outputMicrosPerMillionTokens: 44e5,
1637
- marginBps: 2500
2093
+ marginBps: 500
1638
2094
  }
1639
2095
  }
1640
2096
  };
@@ -1693,11 +2149,14 @@ function objectStorageConfiguredForWorkspaceArchives(settings) {
1693
2149
  }
1694
2150
  }
1695
2151
  }
1696
- function optional(name) {
1697
- const value = process.env[name];
2152
+ function optionalEnvironmentValue(name, source) {
2153
+ const value = source[name];
1698
2154
  return value && value.trim().length > 0 ? value : void 0;
1699
2155
  }
1700
- function getSettings() {
2156
+ function getSettings(source = process.env) {
2157
+ const optional = (name) => optionalEnvironmentValue(name, source);
2158
+ const modelCatalogSource = optional("OPENGENI_MODEL_CATALOG_SOURCE");
2159
+ const modelCostPolicyJson = optional("OPENGENI_MODEL_COST_POLICY_JSON") ?? (modelCatalogSource === "database" ? "{}" : DEFAULT_MODEL_COST_POLICY_JSON);
1701
2160
  const raw = {
1702
2161
  serviceName: optional("OPENGENI_SERVICE_NAME"),
1703
2162
  environment: optional("OPENGENI_ENVIRONMENT"),
@@ -1742,6 +2201,8 @@ function getSettings() {
1742
2201
  analyticsPosthogHost: optional("OPENGENI_ANALYTICS_POSTHOG_HOST"),
1743
2202
  analyticsGa4MeasurementId: optional("OPENGENI_ANALYTICS_GA4_MEASUREMENT_ID"),
1744
2203
  publicBaseUrl: optional("OPENGENI_PUBLIC_BASE_URL"),
2204
+ mcpOauthEnabled: optional("OPENGENI_MCP_OAUTH_ENABLED"),
2205
+ mcpOauthTrustedProxyHops: optional("OPENGENI_MCP_OAUTH_TRUSTED_PROXY_HOPS"),
1745
2206
  webBaseUrl: optional("OPENGENI_WEB_BASE_URL"),
1746
2207
  agentReleasesBaseUrl: optional("OPENGENI_AGENT_RELEASES_BASE_URL"),
1747
2208
  agentStableVersion: optional("OPENGENI_AGENT_STABLE_VERSION"),
@@ -1813,6 +2274,9 @@ function getSettings() {
1813
2274
  goalIdleBackoffMs: optional("OPENGENI_GOAL_IDLE_BACKOFF_MS"),
1814
2275
  goalIdleBackoffMaxMs: optional("OPENGENI_GOAL_IDLE_BACKOFF_MAX_MS"),
1815
2276
  childLifecycleNoticesEnabled: optional("OPENGENI_CHILD_LIFECYCLE_NOTICES_ENABLED"),
2277
+ hostMcpAuthoritySourceAdmissionEnabled: optional(
2278
+ "OPENGENI_HOST_MCP_AUTHORITY_SOURCE_ADMISSION_ENABLED"
2279
+ ),
1816
2280
  slackWorkspaceRoutingEnabled: optional("OPENGENI_SLACK_WORKSPACE_ROUTING_ENABLED"),
1817
2281
  agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
1818
2282
  contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
@@ -1882,6 +2346,10 @@ function getSettings() {
1882
2346
  voiceInputAzureAdToken: optional("OPENGENI_VOICE_INPUT_AZURE_AD_TOKEN"),
1883
2347
  voiceInputCodexExperimentalEnabled: optional("OPENGENI_VOICE_INPUT_CODEX_EXPERIMENTAL"),
1884
2348
  modelPricingJson: optional("OPENGENI_MODEL_PRICING_JSON"),
2349
+ modelCatalogSource,
2350
+ modelCostPolicyJson,
2351
+ modelNotesJson: optional("OPENGENI_MODEL_NOTES_JSON"),
2352
+ openrouterApiKey: optional("OPENGENI_OPENROUTER_API_KEY"),
1885
2353
  modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
1886
2354
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
1887
2355
  supergrokSubscriptionEnabled: optional("OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED"),
@@ -1891,7 +2359,6 @@ function getSettings() {
1891
2359
  codexConnectedAppsEnabled: optional("OPENGENI_CODEX_CONNECTED_APPS_ENABLED"),
1892
2360
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
1893
2361
  lazyToolSearchEnabled: optional("OPENGENI_LAZY_TOOL_SEARCH_ENABLED"),
1894
- codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
1895
2362
  codexFleetPolicyShadowEnabled: optional("OPENGENI_CODEX_FLEET_POLICY_SHADOW_ENABLED"),
1896
2363
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
1897
2364
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
@@ -1916,7 +2383,7 @@ function getSettings() {
1916
2383
  dockerNetwork: optional("OPENGENI_DOCKER_NETWORK"),
1917
2384
  dockerWorkspaceBaseDir: optional("OPENGENI_DOCKER_WORKSPACE_BASE_DIR"),
1918
2385
  modalAppName: optional("OPENGENI_MODAL_APP_NAME"),
1919
- modalImageRef: optional("OPENGENI_MODAL_IMAGE_REF"),
2386
+ modalImageRef: optional("OPENGENI_MODAL_IMAGE_REF") ?? DEFAULT_MODAL_IMAGE_REF,
1920
2387
  modalImageId: optional("OPENGENI_MODAL_IMAGE_ID"),
1921
2388
  modalImageRegistrySecret: optional("OPENGENI_MODAL_IMAGE_REGISTRY_SECRET"),
1922
2389
  modalTimeoutSeconds: optional("OPENGENI_MODAL_TIMEOUT_SECONDS"),
@@ -2018,6 +2485,7 @@ function getSettings() {
2018
2485
  sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
2019
2486
  sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
2020
2487
  sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
2488
+ sandboxDrainSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_DRAIN_SNAPSHOT_TIMEOUT_MS"),
2021
2489
  sandboxRotationLeadMs: optional("OPENGENI_SANDBOX_ROTATION_LEAD_MS"),
2022
2490
  sandboxRotationBatchSize: optional("OPENGENI_SANDBOX_ROTATION_BATCH_SIZE"),
2023
2491
  sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
@@ -2092,6 +2560,12 @@ function getSettings() {
2092
2560
  managedAuthGithubClientId: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_ID"),
2093
2561
  managedAuthGithubClientSecret: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_SECRET"),
2094
2562
  managedAuthSessionSetMode: optional("OPENGENI_MANAGED_AUTH_SESSION_SET_MODE"),
2563
+ organizationUserSetupEmailTokenTransport: optional(
2564
+ "OPENGENI_ORGANIZATION_USER_SETUP_EMAIL_TOKEN_TRANSPORT"
2565
+ ),
2566
+ organizationUserSetupQueryEdgeSanitizationConfirmed: optional(
2567
+ "OPENGENI_ORGANIZATION_USER_SETUP_QUERY_EDGE_SANITIZATION_CONFIRMED"
2568
+ ),
2095
2569
  resendApiKey: optional("OPENGENI_RESEND_API_KEY"),
2096
2570
  emailFrom: optional("OPENGENI_EMAIL_FROM"),
2097
2571
  stripeSecretKey: optional("OPENGENI_STRIPE_SECRET_KEY"),
@@ -2107,7 +2581,7 @@ function getSettings() {
2107
2581
  sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 && parsed.sandboxBackend === "modal" ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
2108
2582
  mcpServers: ensureBuiltInMcpServers(parsed)
2109
2583
  };
2110
- validateSettings(settings);
2584
+ validateSettings(settings, source);
2111
2585
  return settings;
2112
2586
  }
2113
2587
  var LOCAL_FIRST_PARTY_DELEGATION_SECRET = "opengeni-local-first-party-delegation-secret-v1";
@@ -2167,8 +2641,13 @@ function sandboxArchiveCaptureTimeoutMs(settings) {
2167
2641
  settings.sandboxSnapshotTimeoutMs + SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS
2168
2642
  );
2169
2643
  }
2644
+ function effectiveSandboxDrainSnapshotTimeoutMs(settings) {
2645
+ return settings.sandboxDrainSnapshotTimeoutMs ?? settings.sandboxSnapshotTimeoutMs;
2646
+ }
2170
2647
  function sandboxLifecycleTransitionWaitMs(settings) {
2171
- const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
2648
+ const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs({
2649
+ sandboxSnapshotTimeoutMs: effectiveSandboxDrainSnapshotTimeoutMs(settings)
2650
+ });
2172
2651
  return Math.min(
2173
2652
  SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS,
2174
2653
  settings.sandboxLeaseReaperPeriodMs + captureTimeoutMs + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS
@@ -2422,10 +2901,8 @@ function legacyModelCapabilities(settings, input) {
2422
2901
  latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
2423
2902
  });
2424
2903
  }
2425
- function gatewayRequestPolicyForUpstreamModel(upstreamModelId) {
2426
- const model = Object.values(OPENGENI_GATEWAY_MODELS).find(
2427
- (candidate) => candidate.upstreamModelId === upstreamModelId
2428
- );
2904
+ function gatewayRequestPolicyForUpstreamModel(upstreamModelId, models = defaultGatewayCatalogModels()) {
2905
+ const model = models.find((candidate) => candidate.upstreamModelId === upstreamModelId);
2429
2906
  if (!model) {
2430
2907
  return void 0;
2431
2908
  }
@@ -2456,30 +2933,83 @@ function gatewayModelCapabilities(settings, input) {
2456
2933
  latencyModes: [{ id: "standard", upstream: "supported", runnable: true }]
2457
2934
  });
2458
2935
  }
2936
+ function openRouterCustomModelCapabilities(settings) {
2937
+ const legacy = legacyModelCapabilities(settings, {
2938
+ reasoningEffort: false,
2939
+ hostedWebSearch: false
2940
+ });
2941
+ return normalizeCapabilities({
2942
+ ...legacy,
2943
+ functionCalling: { upstream: "supported", runnable: true },
2944
+ inputModalities: ["text"],
2945
+ inputFileMediaTypes: [],
2946
+ transports: {
2947
+ ...legacy.transports,
2948
+ sse: { upstream: "supported", runnable: true }
2949
+ },
2950
+ promptCaching: { upstream: "unsupported", runnable: false, mode: "none" },
2951
+ latencyModes: [{ id: "standard", upstream: "supported", runnable: true }]
2952
+ });
2953
+ }
2459
2954
  function gatewayRegistryProvider(settings, input) {
2460
2955
  const workspace = input.kind === "vercel-gateway-workspace";
2461
- const models = [OPENGENI_GATEWAY_MODELS.deepseek, OPENGENI_GATEWAY_MODELS.kimi].map((model) => {
2462
- const kimi = model === OPENGENI_GATEWAY_MODELS.kimi;
2956
+ const organization = input.kind === "vercel-gateway-organization";
2957
+ const scoped = workspace || organization;
2958
+ const curated = organization ? [] : configuredGatewayCatalogModels(settings);
2959
+ const upstreamIds = new Set(curated.map((model) => model.upstreamModelId));
2960
+ const productIds = new Set(
2961
+ parseModelProvidersJson(settings.modelProvidersJson).filter(
2962
+ (provider) => provider.id !== WORKSPACE_GATEWAY_PROVIDER_ID && provider.id !== ORGANIZATION_GATEWAY_PROVIDER_ID
2963
+ ).flatMap(
2964
+ (provider) => provider.models.flatMap((model) => [model.id, ...model.aliases ?? []])
2965
+ )
2966
+ );
2967
+ const models = curated.map((model) => {
2968
+ const id = workspace ? model.workspaceProductId : organization ? `${ORGANIZATION_GATEWAY_MODEL_ID_PREFIX}${model.upstreamModelId}` : model.productId;
2969
+ productIds.add(id);
2463
2970
  return {
2464
- id: workspace ? model.workspaceProductId : model.productId,
2971
+ id,
2465
2972
  upstreamModelId: model.upstreamModelId,
2466
2973
  label: model.label,
2467
- shortLabel: model.shortLabel,
2974
+ ...model.shortLabel ? { shortLabel: model.shortLabel } : {},
2468
2975
  capabilities: gatewayModelCapabilities(settings, {
2469
2976
  implicitCaching: model.implicitCaching,
2470
- vision: kimi,
2471
- inputFileMediaTypes: kimi ? ["application/pdf"] : []
2977
+ vision: model.vision,
2978
+ inputFileMediaTypes: model.inputFileMediaTypes
2472
2979
  }),
2473
- contextWindowTokens: 1e6,
2474
- effectiveContextWindowTokens: 9e5,
2475
- autoCompactTokenLimit: 85e4,
2476
- toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
2980
+ contextWindowTokens: model.contextWindowTokens,
2981
+ effectiveContextWindowTokens: model.effectiveContextWindowTokens,
2982
+ autoCompactTokenLimit: model.autoCompactTokenLimit,
2983
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
2984
+ ...model.pricing === void 0 ? {} : { pricing: model.pricing }
2477
2985
  };
2478
2986
  });
2987
+ if (scoped) {
2988
+ for (const custom of input.customModels ?? []) {
2989
+ const productId = `${workspace ? WORKSPACE_GATEWAY_MODEL_ID_PREFIX : ORGANIZATION_GATEWAY_MODEL_ID_PREFIX}${custom.upstreamModelId}`;
2990
+ if (upstreamIds.has(custom.upstreamModelId) || productIds.has(productId)) continue;
2991
+ upstreamIds.add(custom.upstreamModelId);
2992
+ productIds.add(productId);
2993
+ models.push({
2994
+ id: productId,
2995
+ upstreamModelId: custom.upstreamModelId,
2996
+ label: custom.label?.trim() || custom.upstreamModelId,
2997
+ capabilities: gatewayModelCapabilities(settings, {
2998
+ implicitCaching: false,
2999
+ vision: false,
3000
+ inputFileMediaTypes: []
3001
+ }),
3002
+ contextWindowTokens: 1e6,
3003
+ effectiveContextWindowTokens: 9e5,
3004
+ autoCompactTokenLimit: 85e4,
3005
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
3006
+ });
3007
+ }
3008
+ }
2479
3009
  return {
2480
3010
  kind: input.kind,
2481
- id: workspace ? WORKSPACE_GATEWAY_PROVIDER_ID : OPENGENI_GATEWAY_PROVIDER_ID,
2482
- label: workspace ? "Your Gateway" : "OpenGeni",
3011
+ id: workspace ? WORKSPACE_GATEWAY_PROVIDER_ID : organization ? ORGANIZATION_GATEWAY_PROVIDER_ID : OPENGENI_GATEWAY_PROVIDER_ID,
3012
+ label: workspace ? "Your Gateway" : organization ? "Organization Gateway" : "OpenGeni",
2483
3013
  // Responses preserves vision, reasoning items, and provider-native usage.
2484
3014
  // Model-specific compatibility stays at the reviewed request fence rather
2485
3015
  // than downgrading the whole provider wire.
@@ -2490,47 +3020,179 @@ function gatewayRegistryProvider(settings, input) {
2490
3020
  models
2491
3021
  };
2492
3022
  }
3023
+ function openRouterRegistryProvider(settings, input) {
3024
+ const workspace = input.kind === "openrouter-workspace";
3025
+ const organization = input.kind === "openrouter-organization";
3026
+ const scoped = workspace || organization;
3027
+ const curated = organization ? [] : configuredOpenRouterCatalogModels(settings);
3028
+ const upstreamIds = new Set(curated.map((model) => model.upstreamModelId));
3029
+ const productIds = new Set(
3030
+ parseModelProvidersJson(settings.modelProvidersJson).filter(
3031
+ (provider) => provider.id !== WORKSPACE_OPENROUTER_PROVIDER_ID && provider.id !== ORGANIZATION_OPENROUTER_PROVIDER_ID
3032
+ ).flatMap(
3033
+ (provider) => provider.models.flatMap((model) => [model.id, ...model.aliases ?? []])
3034
+ )
3035
+ );
3036
+ const models = curated.map((model) => {
3037
+ const id = workspace ? workspaceOpenRouterProductId(model.upstreamModelId) : organization ? `${ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}` : `${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`;
3038
+ const aliases = workspace ? model.aliases.map(workspaceOpenRouterProductId) : organization ? model.aliases.map((alias) => `${ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX}${alias}`) : model.aliases;
3039
+ productIds.add(id);
3040
+ for (const alias of aliases) productIds.add(alias);
3041
+ return {
3042
+ id,
3043
+ upstreamModelId: model.upstreamModelId,
3044
+ aliases,
3045
+ label: model.label,
3046
+ ...model.shortLabel ? { shortLabel: model.shortLabel } : {},
3047
+ capabilities: model.capabilities,
3048
+ ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
3049
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
3050
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
3051
+ toolOutputTruncationTokens: model.toolOutputTruncationTokens ?? settings.modelToolOutputTruncationTokens
3052
+ };
3053
+ });
3054
+ if (scoped) {
3055
+ for (const custom of input.customModels ?? []) {
3056
+ const productId = `${workspace ? WORKSPACE_OPENROUTER_MODEL_ID_PREFIX : ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX}${custom.upstreamModelId}`;
3057
+ if (upstreamIds.has(custom.upstreamModelId) || productIds.has(productId)) continue;
3058
+ upstreamIds.add(custom.upstreamModelId);
3059
+ productIds.add(productId);
3060
+ models.push({
3061
+ id: productId,
3062
+ upstreamModelId: custom.upstreamModelId,
3063
+ aliases: [],
3064
+ label: custom.label?.trim() || custom.upstreamModelId,
3065
+ capabilities: openRouterCustomModelCapabilities(settings),
3066
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
3067
+ });
3068
+ }
3069
+ }
3070
+ if (models.length === 0) return null;
3071
+ const defaultHeaders = {
3072
+ "x-title": "OpenGeni",
3073
+ ...settings.publicBaseUrl ? { "http-referer": settings.publicBaseUrl } : {}
3074
+ };
3075
+ return {
3076
+ kind: input.kind,
3077
+ id: workspace ? WORKSPACE_OPENROUTER_PROVIDER_ID : organization ? ORGANIZATION_OPENROUTER_PROVIDER_ID : OPENROUTER_PROVIDER_ID,
3078
+ label: workspace ? "Your OpenRouter" : organization ? "Organization OpenRouter" : "OpenRouter",
3079
+ api: "chat",
3080
+ wireProfile: "openai",
3081
+ baseUrl: OPENROUTER_BASE_URL,
3082
+ ...input.apiKey ? { apiKey: input.apiKey } : {},
3083
+ defaultHeaders,
3084
+ publicDefaultHeaderNames: Object.keys(defaultHeaders),
3085
+ models
3086
+ };
3087
+ }
2493
3088
  function configuredRegistryProviders(settings) {
2494
3089
  const providers = parseModelProvidersJson(settings.modelProvidersJson);
2495
- if (!settings.vercelAiGatewayApiKey) {
2496
- return providers;
2497
- }
2498
- if (providers.some((provider) => provider.id === OPENGENI_GATEWAY_PROVIDER_ID)) {
2499
- throw new Error(
2500
- `${OPENGENI_GATEWAY_PROVIDER_ID} is reserved for OPENGENI_VERCEL_AI_GATEWAY_API_KEY`
3090
+ const injected = [...providers];
3091
+ if (settings.vercelAiGatewayApiKey && configuredGatewayCatalogModels(settings).length > 0) {
3092
+ injected.push(
3093
+ gatewayRegistryProvider(settings, {
3094
+ kind: "vercel-gateway-managed",
3095
+ apiKey: settings.vercelAiGatewayApiKey
3096
+ })
2501
3097
  );
2502
3098
  }
2503
- return [
2504
- ...providers,
2505
- gatewayRegistryProvider(settings, {
2506
- kind: "vercel-gateway-managed",
2507
- apiKey: settings.vercelAiGatewayApiKey
2508
- })
2509
- ];
3099
+ const openrouter = settings.openrouterApiKey ? openRouterRegistryProvider(settings, {
3100
+ kind: "openrouter-managed",
3101
+ apiKey: settings.openrouterApiKey
3102
+ }) : null;
3103
+ if (openrouter) injected.push(openrouter);
3104
+ return injected;
2510
3105
  }
2511
- function withWorkspaceGatewayCatalogProvider(settings) {
3106
+ function withWorkspaceGatewayCatalogProvider(settings, customModels = []) {
2512
3107
  const providers = parseModelProvidersJson(settings.modelProvidersJson);
2513
- if (providers.some((provider) => provider.id === WORKSPACE_GATEWAY_PROVIDER_ID)) {
2514
- return settings;
2515
- }
3108
+ const withoutWorkspace = providers.filter(
3109
+ (provider) => provider.id !== WORKSPACE_GATEWAY_PROVIDER_ID
3110
+ );
3111
+ const curatedCount = configuredGatewayCatalogModels(settings).length;
3112
+ if (curatedCount === 0 && customModels.length === 0) return settings;
2516
3113
  return {
2517
3114
  ...settings,
2518
3115
  modelProvidersJson: JSON.stringify([
2519
- ...providers,
2520
- gatewayRegistryProvider(settings, { kind: "vercel-gateway-workspace" })
3116
+ ...withoutWorkspace,
3117
+ gatewayRegistryProvider(settings, {
3118
+ kind: "vercel-gateway-workspace",
3119
+ customModels
3120
+ })
2521
3121
  ])
2522
3122
  };
2523
3123
  }
2524
- function withWorkspaceGatewayCredential(settings, apiKey) {
3124
+ function withWorkspaceGatewayCredential(settings, apiKey, customModels = []) {
2525
3125
  if (!apiKey.trim()) {
2526
3126
  throw new Error("workspace AI Gateway credential is empty");
2527
3127
  }
2528
- const catalogSettings = withWorkspaceGatewayCatalogProvider(settings);
3128
+ const catalogSettings = withWorkspaceGatewayCatalogProvider(settings, customModels);
2529
3129
  const providers = parseModelProvidersJson(catalogSettings.modelProvidersJson).map(
2530
3130
  (provider) => provider.id === WORKSPACE_GATEWAY_PROVIDER_ID ? { ...provider, apiKey } : provider
2531
3131
  );
2532
3132
  return { ...catalogSettings, modelProvidersJson: JSON.stringify(providers) };
2533
3133
  }
3134
+ function withWorkspaceOpenRouterCatalogProvider(settings, customModels = []) {
3135
+ const providers = parseModelProvidersJson(settings.modelProvidersJson);
3136
+ const withoutWorkspace = providers.filter(
3137
+ (provider2) => provider2.id !== WORKSPACE_OPENROUTER_PROVIDER_ID
3138
+ );
3139
+ const provider = openRouterRegistryProvider(settings, {
3140
+ kind: "openrouter-workspace",
3141
+ customModels
3142
+ });
3143
+ if (!provider) return settings;
3144
+ return {
3145
+ ...settings,
3146
+ modelProvidersJson: JSON.stringify([...withoutWorkspace, provider])
3147
+ };
3148
+ }
3149
+ function withWorkspaceOpenRouterCredential(settings, apiKey, customModels = []) {
3150
+ if (!apiKey.trim()) {
3151
+ throw new Error("workspace OpenRouter credential is empty");
3152
+ }
3153
+ const catalogSettings = withWorkspaceOpenRouterCatalogProvider(settings, customModels);
3154
+ const providers = parseModelProvidersJson(catalogSettings.modelProvidersJson).map(
3155
+ (provider) => provider.id === WORKSPACE_OPENROUTER_PROVIDER_ID ? { ...provider, apiKey } : provider
3156
+ );
3157
+ return { ...catalogSettings, modelProvidersJson: JSON.stringify(providers) };
3158
+ }
3159
+ function withOrganizationGatewayCatalogProvider(settings, customModels = []) {
3160
+ if (customModels.length === 0) return settings;
3161
+ const providers = parseModelProvidersJson(settings.modelProvidersJson).filter(
3162
+ (provider2) => provider2.id !== ORGANIZATION_GATEWAY_PROVIDER_ID
3163
+ );
3164
+ const provider = gatewayRegistryProvider(settings, {
3165
+ kind: "vercel-gateway-organization",
3166
+ customModels
3167
+ });
3168
+ return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
3169
+ }
3170
+ function withOrganizationGatewayCredential(settings, apiKey, customModels = []) {
3171
+ if (!apiKey.trim()) throw new Error("organization AI Gateway credential is empty");
3172
+ const catalog = withOrganizationGatewayCatalogProvider(settings, customModels);
3173
+ const providers = parseModelProvidersJson(catalog.modelProvidersJson).map(
3174
+ (provider) => provider.id === ORGANIZATION_GATEWAY_PROVIDER_ID ? { ...provider, apiKey } : provider
3175
+ );
3176
+ return { ...catalog, modelProvidersJson: JSON.stringify(providers) };
3177
+ }
3178
+ function withOrganizationOpenRouterCatalogProvider(settings, customModels = []) {
3179
+ const providers = parseModelProvidersJson(settings.modelProvidersJson).filter(
3180
+ (provider2) => provider2.id !== ORGANIZATION_OPENROUTER_PROVIDER_ID
3181
+ );
3182
+ const provider = openRouterRegistryProvider(settings, {
3183
+ kind: "openrouter-organization",
3184
+ customModels
3185
+ });
3186
+ return provider ? { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) } : settings;
3187
+ }
3188
+ function withOrganizationOpenRouterCredential(settings, apiKey, customModels = []) {
3189
+ if (!apiKey.trim()) throw new Error("organization OpenRouter credential is empty");
3190
+ const catalog = withOrganizationOpenRouterCatalogProvider(settings, customModels);
3191
+ const providers = parseModelProvidersJson(catalog.modelProvidersJson).map(
3192
+ (provider) => provider.id === ORGANIZATION_OPENROUTER_PROVIDER_ID ? { ...provider, apiKey } : provider
3193
+ );
3194
+ return { ...catalog, modelProvidersJson: JSON.stringify(providers) };
3195
+ }
2534
3196
  var GPT56_FAST_BILLING_MULTIPLIER_BPS = 2e4;
2535
3197
  function productLabelForModelId(modelId) {
2536
3198
  const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
@@ -2561,6 +3223,8 @@ function productShortLabelForModelId(modelId) {
2561
3223
  return "5.6 Terra";
2562
3224
  case "gpt-5.6-luna":
2563
3225
  return "5.6 Luna";
3226
+ case "gpt-6-astra":
3227
+ return "6 Astra";
2564
3228
  default:
2565
3229
  return null;
2566
3230
  }
@@ -2580,7 +3244,7 @@ function builtinContextLimitsForModel(settings, modelId) {
2580
3244
  return { contextWindowTokens: settings.contextWindowTokens };
2581
3245
  }
2582
3246
  function builtinLatencyModesForModel(modelId) {
2583
- if (isBuiltinGpt56ModelId(modelId) || modelId.startsWith("codex/gpt-5.6-")) {
3247
+ if (isBuiltinGpt56ModelId(modelId) || modelId.startsWith("codex/gpt-5.6-") || modelId === "codex/gpt-6-astra") {
2584
3248
  return [
2585
3249
  { id: "standard", upstream: "supported", runnable: true },
2586
3250
  {
@@ -2643,31 +3307,60 @@ function assertLatencyModeRunnable(settings, modelId, latencyMode) {
2643
3307
  }
2644
3308
  }
2645
3309
  function registryCredentialSource(provider) {
2646
- if (provider.kind === "anonymous") {
2647
- return { kind: "deployment", mechanism: "none" };
2648
- }
2649
- if (provider.kind === "codex-subscription") {
2650
- return { kind: "connected_subscription", provider: "codex" };
2651
- }
2652
- if (provider.kind === "xai-subscription") {
2653
- return { kind: "connected_subscription", provider: "xai" };
2654
- }
2655
- if (provider.kind === "vercel-gateway-workspace") {
2656
- return { kind: "workspace_connection", mechanism: "api_key" };
3310
+ switch (provider.kind) {
3311
+ case "anonymous":
3312
+ return { kind: "deployment", mechanism: "none" };
3313
+ case "codex-subscription":
3314
+ return { kind: "connected_subscription", provider: "codex" };
3315
+ case "xai-subscription":
3316
+ return { kind: "connected_subscription", provider: "xai" };
3317
+ case "vercel-gateway-workspace":
3318
+ case "openrouter-workspace":
3319
+ return { kind: "workspace_connection", mechanism: "api_key" };
3320
+ case "vercel-gateway-organization":
3321
+ case "openrouter-organization":
3322
+ return { kind: "organization_connection", mechanism: "api_key" };
3323
+ case "api-key":
3324
+ case "vercel-gateway-managed":
3325
+ case "openrouter-managed":
3326
+ return { kind: "deployment", mechanism: "api_key" };
3327
+ default: {
3328
+ const _exhaustive = provider.kind;
3329
+ return _exhaustive;
3330
+ }
2657
3331
  }
2658
- return { kind: "deployment", mechanism: "api_key" };
2659
3332
  }
2660
3333
  function registryBilling(provider) {
2661
- if (provider.kind === "anonymous") {
2662
- return { upstreamPayer: "deployment", metering: "external" };
2663
- }
2664
- if (provider.kind === "codex-subscription" || provider.kind === "xai-subscription") {
2665
- return { upstreamPayer: "connected_subscription", metering: "external" };
2666
- }
2667
- if (provider.kind === "vercel-gateway-workspace") {
2668
- return { upstreamPayer: "workspace", metering: "external" };
3334
+ switch (provider.kind) {
3335
+ case "anonymous":
3336
+ case "openrouter-managed":
3337
+ return { upstreamPayer: "deployment", metering: "external" };
3338
+ case "codex-subscription":
3339
+ case "xai-subscription":
3340
+ return { upstreamPayer: "connected_subscription", metering: "external" };
3341
+ case "vercel-gateway-workspace":
3342
+ case "openrouter-workspace":
3343
+ return { upstreamPayer: "workspace", metering: "external" };
3344
+ case "vercel-gateway-organization":
3345
+ case "openrouter-organization":
3346
+ return { upstreamPayer: "organization", metering: "external" };
3347
+ case "api-key":
3348
+ case "vercel-gateway-managed":
3349
+ return { upstreamPayer: "deployment", metering: "opengeni_credits" };
3350
+ default: {
3351
+ const _exhaustive = provider.kind;
3352
+ return _exhaustive;
3353
+ }
2669
3354
  }
2670
- return { upstreamPayer: "deployment", metering: "opengeni_credits" };
3355
+ }
3356
+ function configuredCostForModel(settings, productModelId, credentialSource) {
3357
+ if (credentialSource.kind === "workspace_connection") return "workspace";
3358
+ if (credentialSource.kind === "organization_connection") return "organization";
3359
+ if (credentialSource.kind === "connected_subscription") return "subscription";
3360
+ return parseModelCostPolicyJson(settings.modelCostPolicyJson)[productModelId] ?? "credits";
3361
+ }
3362
+ function modelCostClassForConfiguredModel(_settings, model) {
3363
+ return model.cost;
2671
3364
  }
2672
3365
  function builtinCredentialSource(settings) {
2673
3366
  if (settings.openaiProvider === "azure" && !settings.azureOpenaiApiKey) {
@@ -2726,6 +3419,10 @@ function definitionVersionFor(model, provider, options = {}) {
2726
3419
  billing: model.billing,
2727
3420
  executionLimits: model.executionLimits,
2728
3421
  capabilities: model.capabilities,
3422
+ // Workspace-facing free/credits classification is a separate live
3423
+ // deployment policy. Operators must drain/fence accepted turns before
3424
+ // changing it; it is intentionally not a second executable-definition
3425
+ // freeze inside TurnExecutionPolicyV1.
2729
3426
  ...model.requestPolicy ? { requestPolicy: model.requestPolicy } : {},
2730
3427
  pricing: model.pricing ?? null
2731
3428
  });
@@ -2734,7 +3431,9 @@ function definitionVersionFor(model, provider, options = {}) {
2734
3431
  function legacyImplicitOpenAiDefinitionVersionFor(model, provider) {
2735
3432
  if (provider.wireProfile !== "openai") return null;
2736
3433
  const { definitionVersion: _definitionVersion, ...modelWithoutVersion } = model;
2737
- return definitionVersionFor(modelWithoutVersion, provider, { includeWireProfile: false });
3434
+ return definitionVersionFor(modelWithoutVersion, provider, {
3435
+ includeWireProfile: false
3436
+ });
2738
3437
  }
2739
3438
  function builtinProviderId(settings) {
2740
3439
  return settings.openaiProvider === "azure" ? "azure" : "openai";
@@ -2742,7 +3441,7 @@ function builtinProviderId(settings) {
2742
3441
  function builtinProviderLabel(settings) {
2743
3442
  return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
2744
3443
  }
2745
- function configuredProviders(settings) {
3444
+ function configuredProviders(settings, source = process.env) {
2746
3445
  const credentialSource = builtinCredentialSource(settings);
2747
3446
  const builtin = {
2748
3447
  id: builtinProviderId(settings),
@@ -2771,7 +3470,7 @@ function configuredProviders(settings) {
2771
3470
  wireProfile: provider.wireProfile,
2772
3471
  builtin: false,
2773
3472
  baseUrl: provider.baseUrl,
2774
- apiKey: resolveProviderApiKey(provider),
3473
+ apiKey: resolveProviderApiKey(provider, source),
2775
3474
  defaultQuery: provider.defaultQuery,
2776
3475
  defaultHeaders: provider.defaultHeaders,
2777
3476
  publicDefaultQueryNames: provider.publicDefaultQueryNames,
@@ -2799,7 +3498,7 @@ function withCodexCatalogProvider(settings) {
2799
3498
  ...legacyModelCapabilities(settings, {
2800
3499
  reasoningEffort: true,
2801
3500
  hostedWebSearch: true,
2802
- vision: slug.startsWith("gpt-5.6-")
3501
+ vision: slug.startsWith("gpt-5.6-") || slug === "gpt-6-astra"
2803
3502
  }),
2804
3503
  ...builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`) ? {
2805
3504
  promptCaching: builtinPromptCachingForModel(`${CODEX_MODEL_ID_PREFIX}${slug}`)
@@ -2854,8 +3553,14 @@ function withXaiSubscriptionCatalogProvider(settings) {
2854
3553
  { id: "standard", upstream: "supported", runnable: true },
2855
3554
  { id: "fast", upstream: "supported", runnable: true }
2856
3555
  ];
2857
- capabilities.hostedTools.xSearch = { upstream: "supported", runnable: true };
2858
- capabilities.hostedTools.imageGeneration = { upstream: "supported", runnable: true };
3556
+ capabilities.hostedTools.xSearch = {
3557
+ upstream: "supported",
3558
+ runnable: true
3559
+ };
3560
+ capabilities.hostedTools.imageGeneration = {
3561
+ upstream: "supported",
3562
+ runnable: true
3563
+ };
2859
3564
  return {
2860
3565
  id: `${XAI_SUBSCRIPTION_MODEL_ID_PREFIX}${slug}`,
2861
3566
  upstreamModelId: slug,
@@ -2871,7 +3576,10 @@ function withXaiSubscriptionCatalogProvider(settings) {
2871
3576
  };
2872
3577
  })
2873
3578
  };
2874
- return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
3579
+ return {
3580
+ ...settings,
3581
+ modelProvidersJson: JSON.stringify([...providers, provider])
3582
+ };
2875
3583
  }
2876
3584
  function policyProviderIdForModel(settings, modelId) {
2877
3585
  const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
@@ -2884,6 +3592,9 @@ function policyProviderIdForModel(settings, modelId) {
2884
3592
  if (canonicalModelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
2885
3593
  return WORKSPACE_GATEWAY_PROVIDER_ID;
2886
3594
  }
3595
+ if (canonicalModelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX)) {
3596
+ return WORKSPACE_OPENROUTER_PROVIDER_ID;
3597
+ }
2887
3598
  const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
2888
3599
  return configured?.providerId ?? builtinProviderId(settings);
2889
3600
  }
@@ -2896,10 +3607,14 @@ function resolvedExecutionLimits(settings, model) {
2896
3607
  };
2897
3608
  }
2898
3609
  function finalizeConfiguredModel(settings, provider, input) {
2899
- const requestPolicy = provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" ? gatewayRequestPolicyForUpstreamModel(input.upstreamModelId) : void 0;
3610
+ const requestPolicy = provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" || provider.kind === "vercel-gateway-organization" ? gatewayRequestPolicyForUpstreamModel(
3611
+ input.upstreamModelId,
3612
+ configuredGatewayCatalogModels(settings)
3613
+ ) : void 0;
2900
3614
  const modelWithoutVersion = {
2901
3615
  schemaVersion: 1,
2902
3616
  ...input,
3617
+ cost: configuredCostForModel(settings, input.id, input.credentialSource),
2903
3618
  ...requestPolicy ? { requestPolicy } : {},
2904
3619
  executionLimits: resolvedExecutionLimits(settings, input)
2905
3620
  };
@@ -2939,10 +3654,10 @@ function assertUniqueModelIdentities(models) {
2939
3654
  }
2940
3655
  }
2941
3656
  }
2942
- function configuredModels(settings) {
3657
+ function configuredModels(settings, source = process.env) {
2943
3658
  const builtinId = builtinProviderId(settings);
2944
3659
  const builtinLabel = builtinProviderLabel(settings);
2945
- const providers = configuredProviders(settings);
3660
+ const providers = configuredProviders(settings, source);
2946
3661
  const providerById = new Map(providers.map((provider) => [provider.id, provider]));
2947
3662
  const pricingSchedules = configuredModelPricingSchedules(settings);
2948
3663
  const parsedRegistry = configuredRegistryProviders(settings);
@@ -3031,7 +3746,10 @@ function configuredModels(settings) {
3031
3746
  }
3032
3747
  }
3033
3748
  assertUniqueModelIdentities(out);
3034
- return out;
3749
+ const defaultIndex = out.findIndex(
3750
+ (model) => model.id === settings.openaiModel || model.aliases.includes(settings.openaiModel)
3751
+ );
3752
+ return defaultIndex > 0 ? [out[defaultIndex], ...out.slice(0, defaultIndex), ...out.slice(defaultIndex + 1)] : out;
3035
3753
  }
3036
3754
  function canonicalizeConfiguredModelId(settings, modelId) {
3037
3755
  const models = configuredModels(settings);
@@ -3066,10 +3784,32 @@ function settingsForTurnExecutionPolicy(settings, modelId) {
3066
3784
  return withXaiSubscriptionCatalogProvider(settings);
3067
3785
  }
3068
3786
  if (modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
3787
+ if (resolveModelProvider(settings, modelId)) {
3788
+ return settings;
3789
+ }
3069
3790
  return withWorkspaceGatewayCatalogProvider(settings);
3070
3791
  }
3792
+ if (modelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX)) {
3793
+ if (resolveModelProvider(settings, modelId)) {
3794
+ return settings;
3795
+ }
3796
+ return withWorkspaceOpenRouterCatalogProvider(settings);
3797
+ }
3798
+ if (modelId.startsWith(ORGANIZATION_GATEWAY_MODEL_ID_PREFIX)) {
3799
+ return resolveModelProvider(settings, modelId) ? settings : withOrganizationGatewayCatalogProvider(settings);
3800
+ }
3801
+ if (modelId.startsWith(ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX)) {
3802
+ return resolveModelProvider(settings, modelId) ? settings : withOrganizationOpenRouterCatalogProvider(settings);
3803
+ }
3071
3804
  return settings;
3072
3805
  }
3806
+ function resolveModelProviderForTurn(settings, modelId) {
3807
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, modelId);
3808
+ return resolveModelProvider(
3809
+ catalogSettings,
3810
+ canonicalizeConfiguredModelId(catalogSettings, modelId)
3811
+ );
3812
+ }
3073
3813
  function resolveTurnExecutionPolicyV1(settings, input) {
3074
3814
  const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
3075
3815
  const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
@@ -3147,7 +3887,7 @@ function configuredModelPricingSchedules(settings) {
3147
3887
  const configured = Object.fromEntries(
3148
3888
  Object.entries(parseModelPricingJson(settings.modelPricingJson)).map(([model, pricing]) => [
3149
3889
  model,
3150
- { default: pricing }
3890
+ normalizeModelPricingSchedule(pricing)
3151
3891
  ])
3152
3892
  );
3153
3893
  return {
@@ -3260,12 +4000,7 @@ function calculateModelUsageCostBreakdown(settings, model, usage, options) {
3260
4000
  function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, options) {
3261
4001
  return calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options).creditCostMicros;
3262
4002
  }
3263
- function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
3264
- const schedule = configuredModelPricingSchedules(settings)[model];
3265
- if (!schedule) {
3266
- throw new Error(`Missing model pricing for ${model}`);
3267
- }
3268
- const pricing = selectModelPricing(schedule, positiveInt(options?.inputTokens));
4003
+ function parseGatewayReportedCostDecimal(inferenceCostUsd) {
3269
4004
  const match = /^(0|[1-9]\d*)(?:\.(\d{1,18}))?$/.exec(inferenceCostUsd);
3270
4005
  if (!match) {
3271
4006
  throw new Error("Invalid AI Gateway inference cost");
@@ -3274,16 +4009,35 @@ function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd
3274
4009
  const decimalDigits = BigInt(`${match[1]}${fraction}`);
3275
4010
  const decimalScale = 10n ** BigInt(fraction.length);
3276
4011
  const providerNumerator = decimalDigits * 1000000n;
3277
- const providerMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
4012
+ const providerCostMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
4013
+ if (providerCostMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
4014
+ throw new Error("AI Gateway inference cost exceeds the supported billing range");
4015
+ }
4016
+ return {
4017
+ providerNumerator,
4018
+ decimalScale,
4019
+ providerCostMicros: Number(providerCostMicros)
4020
+ };
4021
+ }
4022
+ function calculateGatewayReportedProviderCostMicros(inferenceCostUsd) {
4023
+ return parseGatewayReportedCostDecimal(inferenceCostUsd).providerCostMicros;
4024
+ }
4025
+ function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
4026
+ const schedule = configuredModelPricingSchedules(settings)[model];
4027
+ if (!schedule) {
4028
+ throw new Error(`Missing model pricing for ${model}`);
4029
+ }
4030
+ const pricing = selectModelPricing(schedule, positiveInt(options?.inputTokens));
4031
+ const { providerNumerator, decimalScale, providerCostMicros } = parseGatewayReportedCostDecimal(inferenceCostUsd);
3278
4032
  const marginBps = BigInt(1e4 + (pricing.marginBps ?? 0));
3279
4033
  const numerator = providerNumerator * marginBps;
3280
4034
  const denominator = decimalScale * 10000n;
3281
4035
  const creditMicros = (numerator + denominator - 1n) / denominator;
3282
- if (providerMicros > BigInt(Number.MAX_SAFE_INTEGER) || creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
4036
+ if (creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
3283
4037
  throw new Error("AI Gateway inference cost exceeds the supported billing range");
3284
4038
  }
3285
4039
  return {
3286
- providerCostMicros: Number(providerMicros),
4040
+ providerCostMicros,
3287
4041
  creditCostMicros: Number(creditMicros)
3288
4042
  };
3289
4043
  }
@@ -3555,7 +4309,7 @@ function parseModelPricingJson(raw) {
3555
4309
  if (!model.trim()) {
3556
4310
  throw new Error("OPENGENI_MODEL_PRICING_JSON contains an empty model name");
3557
4311
  }
3558
- out[model] = ModelPricingSchema.parse(value);
4312
+ out[model] = z.union([ModelPricingSchema, ModelPricingScheduleSchema]).parse(value);
3559
4313
  }
3560
4314
  return out;
3561
4315
  }
@@ -3739,9 +4493,14 @@ function calculateEntryCostMicros(pricing, entry) {
3739
4493
  const inputTokens = positiveInt(entry.inputTokens);
3740
4494
  const outputTokens = positiveInt(entry.outputTokens);
3741
4495
  const cachedTokens = Math.min(inputTokens, cachedInputTokens(entry));
3742
- const uncachedInputTokens = Math.max(0, inputTokens - cachedTokens);
4496
+ const cacheWriteTokens = Math.min(
4497
+ Math.max(0, inputTokens - cachedTokens),
4498
+ cacheWriteInputTokens(entry)
4499
+ );
4500
+ const uncachedInputTokens = Math.max(0, inputTokens - cachedTokens - cacheWriteTokens);
3743
4501
  const cachedInputRate = pricing.cachedInputMicrosPerMillionTokens ?? pricing.inputMicrosPerMillionTokens;
3744
- return Math.ceil(uncachedInputTokens * pricing.inputMicrosPerMillionTokens / 1e6) + Math.ceil(cachedTokens * cachedInputRate / 1e6) + Math.ceil(outputTokens * pricing.outputMicrosPerMillionTokens / 1e6);
4502
+ const cacheWriteRate = pricing.cacheWriteMicrosPerMillionTokens ?? pricing.inputMicrosPerMillionTokens;
4503
+ return Math.ceil(uncachedInputTokens * pricing.inputMicrosPerMillionTokens / 1e6) + Math.ceil(cachedTokens * cachedInputRate / 1e6) + Math.ceil(cacheWriteTokens * cacheWriteRate / 1e6) + Math.ceil(outputTokens * pricing.outputMicrosPerMillionTokens / 1e6);
3745
4504
  }
3746
4505
  function cachedInputTokens(entry) {
3747
4506
  const details = Array.isArray(entry.inputTokensDetails) ? entry.inputTokensDetails : entry.inputTokensDetails ? [entry.inputTokensDetails] : [];
@@ -3751,6 +4510,14 @@ function cachedInputTokens(entry) {
3751
4510
  }
3752
4511
  return total;
3753
4512
  }
4513
+ function cacheWriteInputTokens(entry) {
4514
+ const details = Array.isArray(entry.inputTokensDetails) ? entry.inputTokensDetails : entry.inputTokensDetails ? [entry.inputTokensDetails] : [];
4515
+ let total = 0;
4516
+ for (const detail of details) {
4517
+ total += positiveInt(detail.cache_write_tokens ?? detail.cacheWriteTokens);
4518
+ }
4519
+ return total;
4520
+ }
3754
4521
  function positiveInt(value) {
3755
4522
  return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
3756
4523
  }
@@ -3863,8 +4630,13 @@ function isDigestPinnedModalDesktopImage(settings) {
3863
4630
  if (settings.modalImageId) return true;
3864
4631
  return typeof settings.modalImageRef === "string" && MODAL_DESKTOP_IMAGE_DIGEST_REF.test(settings.modalImageRef);
3865
4632
  }
3866
- function validateSettings(settings) {
4633
+ function validateSettings(settings, source = process.env) {
3867
4634
  temporalConnectionOptions(settings);
4635
+ if (settings.organizationUserSetupEmailTokenTransport === "query" && !settings.organizationUserSetupQueryEdgeSanitizationConfirmed) {
4636
+ throw new Error(
4637
+ "OPENGENI_ORGANIZATION_USER_SETUP_QUERY_EDGE_SANITIZATION_CONFIRMED=true is required when OPENGENI_ORGANIZATION_USER_SETUP_EMAIL_TOKEN_TRANSPORT=query"
4638
+ );
4639
+ }
3868
4640
  if (settings.goalIdleBackoffMs.some((delayMs) => delayMs > settings.goalIdleBackoffMaxMs)) {
3869
4641
  throw new Error(
3870
4642
  `OPENGENI_GOAL_IDLE_BACKOFF_MS entries must not exceed OPENGENI_GOAL_IDLE_BACKOFF_MAX_MS (${settings.goalIdleBackoffMaxMs})`
@@ -3942,6 +4714,24 @@ function validateSettings(settings) {
3942
4714
  );
3943
4715
  }
3944
4716
  }
4717
+ if (settings.mcpOauthEnabled) {
4718
+ if (settings.productAccessMode === "configured") {
4719
+ throw new Error(
4720
+ "OPENGENI_MCP_OAUTH_ENABLED=true requires managed or local product access mode"
4721
+ );
4722
+ }
4723
+ const publicOrigin = canonicalPublicOrigin(settings.publicBaseUrl);
4724
+ if (!publicOrigin) {
4725
+ throw new Error(
4726
+ "OPENGENI_PUBLIC_BASE_URL must be a credential-free HTTP(S) origin when OPENGENI_MCP_OAUTH_ENABLED=true"
4727
+ );
4728
+ }
4729
+ if (!publicOrigin.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
4730
+ throw new Error(
4731
+ "OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_MCP_OAUTH_ENABLED=true outside local/test"
4732
+ );
4733
+ }
4734
+ }
3945
4735
  environmentsEncryptionKeyBytes(settings);
3946
4736
  if (settings.integrationsEnabled) {
3947
4737
  if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
@@ -4132,15 +4922,6 @@ function validateSettings(settings) {
4132
4922
  if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
4133
4923
  throw new Error("OPENGENI_BILLING_MODE=stripe requires OPENGENI_PRODUCT_ACCESS_MODE=managed");
4134
4924
  }
4135
- if (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed") {
4136
- const pricing = configuredModelPricing(settings);
4137
- const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
4138
- if (missing.length > 0) {
4139
- throw new Error(
4140
- `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
4141
- );
4142
- }
4143
- }
4144
4925
  if (settings.usageLimitsMode === "static") {
4145
4926
  const limits = configuredStaticUsageLimits(settings);
4146
4927
  if (Object.keys(limits).length === 0) {
@@ -4274,6 +5055,11 @@ function validateSettings(settings) {
4274
5055
  throw new Error(`OPENGENI_MCP_SERVERS contains duplicate id ${server.id}`);
4275
5056
  }
4276
5057
  serverIds.add(server.id);
5058
+ if (server.connectionRef?.authoritySource === "host" && !settings.hostMcpAuthoritySourceAdmissionEnabled) {
5059
+ throw new Error(
5060
+ "OPENGENI_MCP_SERVERS host-owned connection refs require OPENGENI_HOST_MCP_AUTHORITY_SOURCE_ADMISSION_ENABLED=true after the whole API/worker fleet is upgraded"
5061
+ );
5062
+ }
4277
5063
  }
4278
5064
  {
4279
5065
  const reaperPeriod = settings.sandboxLeaseReaperPeriodMs;
@@ -4289,11 +5075,35 @@ function validateSettings(settings) {
4289
5075
  `OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS (${reaperPeriod}) must be strictly less than OPENGENI_SANDBOX_INTERACTION_HOLDER_TTL_MS (${interactionTtl}): the reaper must run more often than the controller-heartbeat horizon.`
4290
5076
  );
4291
5077
  }
5078
+ if (settings.sandboxDrainSnapshotTimeoutMs !== void 0) {
5079
+ const drainCaptureTimeoutMs2 = sandboxArchiveCaptureTimeoutMs({
5080
+ sandboxSnapshotTimeoutMs: effectiveSandboxDrainSnapshotTimeoutMs(settings)
5081
+ });
5082
+ const requiredTransitionWaitMs = reaperPeriod + drainCaptureTimeoutMs2 + SANDBOX_LIFECYCLE_RETRY_HANDOFF_GRACE_MS;
5083
+ if (requiredTransitionWaitMs > SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS) {
5084
+ throw new Error(
5085
+ `OPENGENI_SANDBOX_DRAIN_SNAPSHOT_TIMEOUT_MS (${settings.sandboxDrainSnapshotTimeoutMs}) requires a sandbox lifecycle transition wait of ${requiredTransitionWaitMs}ms after one reaper period and provider settlement, exceeding the ${SANDBOX_LIFECYCLE_TRANSITION_MAX_WAIT_MS}ms limit. Lower the drain snapshot timeout or OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS.`
5086
+ );
5087
+ }
5088
+ }
5089
+ const rotationLeadMs = settings.sandboxRotationLeadMs;
5090
+ const ordinaryCaptureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
5091
+ const drainCaptureTimeoutMs = sandboxArchiveCaptureTimeoutMs({
5092
+ sandboxSnapshotTimeoutMs: effectiveSandboxDrainSnapshotTimeoutMs(settings)
5093
+ });
5094
+ const providerDeadlineCaptureTimeoutMs = Math.max(
5095
+ ordinaryCaptureTimeoutMs,
5096
+ drainCaptureTimeoutMs
5097
+ );
5098
+ if (!(rotationLeadMs > providerDeadlineCaptureTimeoutMs + reaperPeriod)) {
5099
+ throw new Error(
5100
+ `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the largest durable snapshot or drain capture timeout plus one reaper period (${providerDeadlineCaptureTimeoutMs + reaperPeriod}), including for persisted Modal leases after a default-backend rollout.`
5101
+ );
5102
+ }
4292
5103
  if (settings.sandboxBackend === "modal") {
4293
5104
  const idleGraceMs = settings.sandboxIdleGraceMs;
4294
5105
  const lifecycle = effectiveSandboxLifecycle(settings, "modal");
4295
5106
  const providerLifetimeMs = lifecycle.hardLifetimeMs;
4296
- const rotationLeadMs = lifecycle.rotationLeadMs;
4297
5107
  const idleTimeoutMs = lifecycle.providerIdleTimeoutMs;
4298
5108
  if (!(idleTimeoutMs <= providerLifetimeMs)) {
4299
5109
  throw new Error(
@@ -4305,12 +5115,6 @@ function validateSettings(settings) {
4305
5115
  `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must be strictly less than OPENGENI_MODAL_TIMEOUT_SECONDS*1000 (${providerLifetimeMs}).`
4306
5116
  );
4307
5117
  }
4308
- const captureTimeoutMs = sandboxArchiveCaptureTimeoutMs(settings);
4309
- if (!(rotationLeadMs > captureTimeoutMs + reaperPeriod)) {
4310
- throw new Error(
4311
- `OPENGENI_SANDBOX_ROTATION_LEAD_MS (${rotationLeadMs}) must exceed the durable capture timeout plus one reaper period (${captureTimeoutMs + reaperPeriod}).`
4312
- );
4313
- }
4314
5118
  if (!(viewerTtl < idleTimeoutMs)) {
4315
5119
  throw new Error(
4316
5120
  `OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS (${viewerTtl}) must be strictly less than the effective box idle timeout (${idleTimeoutMs}): a viewer holder must be reapable before the box idles out from under it (the provider idle-timeout is the backstop).`
@@ -4340,15 +5144,29 @@ function validateSettings(settings) {
4340
5144
  "[opengeni] OPENGENI_SANDBOX_DESKTOP_ENABLED=true but neither OPENGENI_STREAM_TOKEN_SECRET nor OPENGENI_DELEGATION_SECRET is set: the desktop pixel plane will GRACEFULLY DEGRADE (DesktopStream.transport=null \u2014 no scoped stream tokens can be minted). Set OPENGENI_STREAM_TOKEN_SECRET to enable the live desktop stream."
4341
5145
  );
4342
5146
  }
5147
+ if (settings.modelCatalogSource === "code") {
5148
+ validateModelCatalogSettings(settings, source);
5149
+ } else {
5150
+ parseModelCostPolicyJson(settings.modelCostPolicyJson);
5151
+ }
5152
+ }
5153
+ function validateModelCatalogSettings(settings, source = process.env) {
5154
+ const costPolicy = parseModelCostPolicyJson(settings.modelCostPolicyJson);
5155
+ const notes = parseModelNotesJson(settings.modelNotesJson);
4343
5156
  const registryProviders = parseModelProvidersJson(settings.modelProvidersJson);
4344
5157
  const builtinId = builtinProviderId(settings);
4345
5158
  const providerIds = /* @__PURE__ */ new Set();
4346
5159
  for (const provider of registryProviders) {
4347
- if (provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" || provider.kind === "xai-subscription") {
5160
+ if (provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" || provider.kind === "vercel-gateway-organization" || provider.kind === "openrouter-workspace" || provider.kind === "openrouter-organization" || provider.kind === "xai-subscription") {
4348
5161
  throw new Error(
4349
5162
  `OPENGENI_MODEL_PROVIDERS_JSON provider kind ${provider.kind} is reserved for a reviewed OpenGeni credential broker`
4350
5163
  );
4351
5164
  }
5165
+ if (RESERVED_MODEL_PROVIDER_IDS.has(provider.id)) {
5166
+ throw new Error(
5167
+ `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} is reserved for a reviewed OpenGeni provider`
5168
+ );
5169
+ }
4352
5170
  if (provider.id === builtinId) {
4353
5171
  throw new Error(
4354
5172
  `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
@@ -4360,13 +5178,66 @@ function validateSettings(settings) {
4360
5178
  );
4361
5179
  }
4362
5180
  providerIds.add(provider.id);
4363
- if (provider.kind !== "codex-subscription" && provider.kind !== "anonymous" && !resolveProviderApiKey(provider)) {
5181
+ if (provider.kind !== "codex-subscription" && provider.kind !== "anonymous" && !resolveProviderApiKey(provider, source)) {
4364
5182
  throw new Error(
4365
5183
  `OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
4366
5184
  );
4367
5185
  }
4368
5186
  }
4369
- configuredModels(settings);
5187
+ const models = configuredModels(settings, source);
5188
+ const defaultCatalogSettings = settingsForTurnExecutionPolicy(settings, settings.openaiModel);
5189
+ const defaultCatalogModels = defaultCatalogSettings === settings ? models : configuredModels(defaultCatalogSettings, source);
5190
+ if (models.length === 0 && defaultCatalogModels.length === 0) {
5191
+ throw new Error("The resolved model catalog contains no executable models");
5192
+ }
5193
+ const defaultModelId = canonicalizeConfiguredModelId(
5194
+ defaultCatalogSettings,
5195
+ settings.openaiModel
5196
+ );
5197
+ if (!defaultCatalogModels.some((model) => model.id === defaultModelId)) {
5198
+ throw new Error(
5199
+ `The default model ${settings.openaiModel} is not executable in the resolved model catalog`
5200
+ );
5201
+ }
5202
+ const deploymentProductIds = new Set(
5203
+ models.filter((model) => model.credentialSource.kind === "deployment").map((model) => model.id)
5204
+ );
5205
+ const noteProductIds = new Set(models.map((model) => model.id));
5206
+ for (const model of configuredGatewayCatalogModels(settings)) {
5207
+ deploymentProductIds.add(model.productId);
5208
+ noteProductIds.add(model.productId);
5209
+ noteProductIds.add(model.workspaceProductId);
5210
+ }
5211
+ for (const model of configuredOpenRouterCatalogModels(settings)) {
5212
+ const productId = `${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`;
5213
+ deploymentProductIds.add(productId);
5214
+ noteProductIds.add(productId);
5215
+ noteProductIds.add(`${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`);
5216
+ }
5217
+ if (settings.modelCatalogSource === "code") {
5218
+ for (const productId of Object.keys(costPolicy)) {
5219
+ if (!deploymentProductIds.has(productId)) {
5220
+ throw new Error(
5221
+ `OPENGENI_MODEL_COST_POLICY_JSON references unknown deployment model ${productId}`
5222
+ );
5223
+ }
5224
+ }
5225
+ }
5226
+ for (const productId of Object.keys(notes)) {
5227
+ if (!noteProductIds.has(productId)) {
5228
+ throw new Error(`OPENGENI_MODEL_NOTES_JSON references unknown catalog model ${productId}`);
5229
+ }
5230
+ }
5231
+ if (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed") {
5232
+ const pricing = configuredModelPricing(settings);
5233
+ const missing = models.filter((model) => model.cost === "credits" && !pricing[model.id]).map((model) => model.id);
5234
+ if (missing.length > 0) {
5235
+ throw new Error(
5236
+ `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
5237
+ );
5238
+ }
5239
+ }
5240
+ return models;
4370
5241
  }
4371
5242
  function resolveStreamTokenSecret(settings) {
4372
5243
  const explicit = settings.streamTokenSecret?.trim();
@@ -4456,19 +5327,35 @@ export {
4456
5327
  CODEX_REALTIME_MODEL_ID,
4457
5328
  CapabilityStateV1Schema,
4458
5329
  CapabilitySupportV1,
5330
+ ConfiguredModelCostClass,
4459
5331
  DEFAULT_AGENT_INSTRUCTIONS,
4460
5332
  DEFAULT_GOAL_IDLE_BACKOFF_MAX_MS,
4461
5333
  DEFAULT_GOAL_IDLE_BACKOFF_MS,
5334
+ DEFAULT_MODAL_IMAGE_REF,
5335
+ DEFAULT_MODEL_COST_POLICY_JSON,
5336
+ DEFAULT_OPENROUTER_MODEL_ID,
4462
5337
  GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MAX_MS,
4463
5338
  GOOGLE_DRIVE_PROVIDER_RETRY_DELAY_MAX_MS,
5339
+ GatewayCatalogModel,
4464
5340
  IntegrationOAuthClientConfigSchema,
4465
5341
  McpServerConnectionRefSchema,
4466
5342
  ModelCapabilitiesV1Schema,
5343
+ ModelCatalogDocument,
5344
+ ModelCostClass,
4467
5345
  ModelProviderApi,
4468
5346
  ModelProviderWireProfile,
4469
5347
  OPENGENI_GATEWAY_MODELS,
4470
5348
  OPENGENI_GATEWAY_PROVIDER_ID,
5349
+ OPENGENI_OPENROUTER_MODELS,
4471
5350
  OPENGENI_REALTIME_MODEL_ID_PREFIX,
5351
+ OPENROUTER_BASE_URL,
5352
+ OPENROUTER_MODEL_ID_PREFIX,
5353
+ OPENROUTER_PROVIDER_ID,
5354
+ ORGANIZATION_GATEWAY_MODEL_ID_PREFIX,
5355
+ ORGANIZATION_GATEWAY_PROVIDER_ID,
5356
+ ORGANIZATION_OPENROUTER_MODEL_ID_PREFIX,
5357
+ ORGANIZATION_OPENROUTER_PROVIDER_ID,
5358
+ OpenRouterCatalogModel,
4472
5359
  RegistryProviderKind,
4473
5360
  SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
4474
5361
  SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS,
@@ -4484,14 +5371,20 @@ export {
4484
5371
  VERCEL_AI_GATEWAY_CONNECTION_ROLE,
4485
5372
  WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
4486
5373
  WORKSPACE_GATEWAY_PROVIDER_ID,
5374
+ WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
5375
+ WORKSPACE_OPENROUTER_CONNECTION_ROLE,
5376
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
5377
+ WORKSPACE_OPENROUTER_PROVIDER_ID,
4487
5378
  WORKSPACE_REALTIME_MODEL_ID_PREFIX,
4488
5379
  XAI_SUBSCRIPTION_MODEL_ID_PREFIX2 as XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
4489
5380
  allowedFirstPartyMcpToolsForSession,
4490
5381
  applyGitAuthPointerEnvironment,
5382
+ applyModelCatalogDocument,
4491
5383
  assertTurnExecutionPolicyMatchesConfigV1,
4492
5384
  builtinProviderId,
4493
5385
  calculateGatewayReportedCostBreakdown,
4494
5386
  calculateGatewayReportedCostMicros,
5387
+ calculateGatewayReportedProviderCostMicros,
4495
5388
  calculateModelUsageCostBreakdown,
4496
5389
  calculateModelUsageCostMicros,
4497
5390
  calculateVideoGenerationCreditCostMicros,
@@ -4503,16 +5396,25 @@ export {
4503
5396
  configuredAllowedModels,
4504
5397
  configuredAllowedReasoningEfforts,
4505
5398
  configuredEntitlements,
5399
+ configuredGatewayOrganizationProductModelIds,
5400
+ configuredGatewayUpstreamModelIds,
5401
+ configuredGatewayWorkspaceProductModelIds,
4506
5402
  configuredGoogleDriveSyncLimits,
5403
+ configuredModelInputIdentities,
5404
+ configuredModelNotes,
4507
5405
  configuredModelPricing,
4508
5406
  configuredModelPricingSchedules,
4509
5407
  configuredModels,
5408
+ configuredOpenRouterOrganizationProductModelIds,
5409
+ configuredOpenRouterUpstreamModelIds,
5410
+ configuredOpenRouterWorkspaceProductModelIds,
4510
5411
  configuredProviders,
4511
5412
  configuredStaticUsageLimits,
4512
5413
  contextInputBudgetTokens,
4513
5414
  dbSearchPath,
4514
5415
  defaultModelPricing,
4515
5416
  effectiveModalIdleTimeoutSeconds,
5417
+ effectiveSandboxDrainSnapshotTimeoutMs,
4516
5418
  effectiveSandboxLifecycle,
4517
5419
  environmentsEncryptionKeyBytes,
4518
5420
  firstPartyMcpBaseUrl,
@@ -4527,9 +5429,13 @@ export {
4527
5429
  hasGitHubRepositorySelection,
4528
5430
  isDirectOpenAiApiBaseUrl,
4529
5431
  isUsableVoiceInputSecret,
5432
+ modelCostClassForConfiguredModel,
4530
5433
  parseExposedPorts,
4531
5434
  parseIntegrationsOauthClientsJson,
4532
5435
  parseMcpServers,
5436
+ parseModelCatalogDocument,
5437
+ parseModelCostPolicyJson,
5438
+ parseModelNotesJson,
4533
5439
  parseModelPricingJson,
4534
5440
  parseModelProvidersJson,
4535
5441
  parseSandboxWarmRateJson,
@@ -4546,6 +5452,7 @@ export {
4546
5452
  resolveFirstPartyDelegationSecret,
4547
5453
  resolveFirstPartyMcpToolPolicy,
4548
5454
  resolveModelProvider,
5455
+ resolveModelProviderForTurn,
4549
5456
  resolveNatsCalloutConfig,
4550
5457
  resolveNatsControlPlaneAuth,
4551
5458
  resolveProviderApiKey,
@@ -4569,10 +5476,17 @@ export {
4569
5476
  startupRetryOptions,
4570
5477
  streamTokenDegraded,
4571
5478
  temporalConnectionOptions,
5479
+ validateModelCatalogSettings,
4572
5480
  voiceInputDeploymentConfigured,
4573
5481
  withCodexCatalogProvider,
5482
+ withOrganizationGatewayCatalogProvider,
5483
+ withOrganizationGatewayCredential,
5484
+ withOrganizationOpenRouterCatalogProvider,
5485
+ withOrganizationOpenRouterCredential,
4574
5486
  withWorkspaceGatewayCatalogProvider,
4575
5487
  withWorkspaceGatewayCredential,
5488
+ withWorkspaceOpenRouterCatalogProvider,
5489
+ withWorkspaceOpenRouterCredential,
4576
5490
  withXaiSubscriptionCatalogProvider
4577
5491
  };
4578
5492
  //# sourceMappingURL=index.js.map