@opengeni/config 0.22.0 → 0.22.5-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.
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;
@@ -511,6 +515,24 @@ var SettingsSchema = z.object({
511
515
  // keep subscription model routing while disabling Codex voice input.
512
516
  voiceInputCodexExperimentalEnabled: EnvBoolean.default(false),
513
517
  modelPricingJson: z.string().default("{}"),
518
+ // Supported-model membership source. Database mode is resolved by the async
519
+ // core overlay; getSettings remains synchronous and env-only.
520
+ modelCatalogSource: z.enum(["code", "database"]).default("code"),
521
+ // Deployment-owned workspace-facing price policy. This is deliberately
522
+ // separate from catalog membership and upstream credential ownership.
523
+ // Shape: { "product/model-id": "free" | "credits" }.
524
+ modelCostPolicyJson: z.string().default("{}"),
525
+ // Optional per-product agent guidance. Database mode replaces this with the
526
+ // singleton document's validated modelNotes map.
527
+ modelNotesJson: z.string().default("{}"),
528
+ // Managed OpenRouter credential. The curated model table is injected in
529
+ // code/catalog-document resolution and never read from host provider JSON.
530
+ openrouterApiKey: z.string().optional(),
531
+ // Internal, secret-free catalog overlays populated only by
532
+ // applyModelCatalogDocument. They intentionally have no OPENGENI_* env
533
+ // binding so database mode cannot be bypassed with a second source.
534
+ resolvedGatewayModelsJson: z.string().optional(),
535
+ resolvedOpenRouterModelsJson: z.string().optional(),
514
536
  // Extra (non-built-in) model providers, declared by the host as a JSON
515
537
  // provider registry. Each entry carries its own base URL, API key, wire API
516
538
  // ("responses" | "chat") and the models it exposes. The models a client may
@@ -651,6 +673,13 @@ var SettingsSchema = z.object({
651
673
  // larger timeout—is what lets a session outlive one finite provider box.
652
674
  // Knob: OPENGENI_MODAL_TIMEOUT_SECONDS.
653
675
  modalTimeoutSeconds: z.coerce.number().int().positive().max(86400).default(86400),
676
+ // Optional provider reservations for every new Modal sandbox. CPU is measured
677
+ // in physical cores and may be fractional; memory is measured in MiB. Leave
678
+ // both unset to preserve Modal's provider defaults. The Agents adapter stores
679
+ // the resolved values in its session state so snapshot replacement and exact
680
+ // resume cannot silently change the reservation.
681
+ modalSandboxCpu: z.coerce.number().positive().optional(),
682
+ modalSandboxMemoryMiB: z.coerce.number().int().positive().optional(),
654
683
  modalTokenId: z.string().optional(),
655
684
  modalTokenSecret: z.string().optional(),
656
685
  modalEnvironment: z.string().optional(),
@@ -688,8 +717,8 @@ var SettingsSchema = z.object({
688
717
  // cell advertises mode "interactive" — the noVNC viewer can drive mouse+keyboard
689
718
  // into :0 (x11vnc runs without -viewonly). Turn it OFF for a genuinely read-only
690
719
  // deployment: the cell reports mode "read-only" and the client disables the
691
- // "Take control" affordance. Independent of computerUseReadOnly (the AGENT
692
- // driver); this gates the HUMAN viewer plane.
720
+ // "Take control" affordance. This gates the HUMAN viewer plane; agent
721
+ // interaction is authorized through managed ComputerSession tools.
693
722
  sandboxDesktopInteractive: EnvBoolean.default(true),
694
723
  // REAL PTY terminal toggle (P5.t): gates the ttyd pty-ws plane (7681) the API
695
724
  // mints over the SAME tunnel as the desktop. Defaults ON — the interactive
@@ -703,13 +732,7 @@ var SettingsSchema = z.object({
703
732
  // down→up restart. Defaults match the proven spike geometry (1280x800).
704
733
  streamResolutionWidth: z.coerce.number().int().positive().default(1280),
705
734
  streamResolutionHeight: z.coerce.number().int().positive().default(800),
706
- // P4.3 computer-use: the agent drives the SAME :0 humans watch (xdotool/XTEST +
707
- // scrot). Gated by sandboxDesktopEnabled + a desktop-capable backend in
708
- // buildAgentCapabilities; computerUseReadOnly:false is the agent-driver default
709
- // (it must click/type — the human viewer plane is the read-only one).
710
- computerUseEnabled: EnvBoolean.default(true),
711
- computerUseReadOnly: EnvBoolean.default(false),
712
- // P4.3 recording loop: ffmpeg x11grab of :0 → mp4/webm → @opengeni/storage.
735
+ // Recording loop: ffmpeg x11grab of :0 mp4/webm → @opengeni/storage.
713
736
  // recordingMaxBytes caps the in-memory finalize buffer (≤ storage single-PUT);
714
737
  // recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
715
738
  recordingEnabled: EnvBoolean.default(true),
@@ -1065,6 +1088,10 @@ var SettingsSchema = z.object({
1065
1088
  betterAuthAllowedHosts: z.string().default(""),
1066
1089
  betterAuthCookieDomain: z.string().optional(),
1067
1090
  betterAuthTrustedOrigins: z.string().default(""),
1091
+ managedAuthGoogleClientId: z.string().optional(),
1092
+ managedAuthGoogleClientSecret: z.string().optional(),
1093
+ managedAuthGithubClientId: z.string().optional(),
1094
+ managedAuthGithubClientSecret: z.string().optional(),
1068
1095
  // Rolling browser login-slot compatibility. Repository/deployment default is
1069
1096
  // deliberately legacy; changing to broker is an operator-authorized rollout.
1070
1097
  managedAuthSessionSetMode: z.enum(["legacy", "dual", "broker"]).default("legacy"),
@@ -1357,7 +1384,8 @@ var RegistryProviderKind = z.enum([
1357
1384
  "codex-subscription",
1358
1385
  "xai-subscription",
1359
1386
  "vercel-gateway-managed",
1360
- "vercel-gateway-workspace"
1387
+ "vercel-gateway-workspace",
1388
+ "openrouter-workspace"
1361
1389
  ]);
1362
1390
  var RegistryModelSchema = z.object({
1363
1391
  id: z.string().min(1),
@@ -1470,6 +1498,285 @@ var RegistryProviderSchema = z.object({
1470
1498
  });
1471
1499
  }
1472
1500
  });
1501
+ var OPENGENI_GATEWAY_PROVIDER_ID = "opengeni-gateway";
1502
+ var WORKSPACE_GATEWAY_PROVIDER_ID = "workspace-gateway";
1503
+ var WORKSPACE_GATEWAY_MODEL_ID_PREFIX = "workspace-gateway/";
1504
+ var OPENROUTER_PROVIDER_ID = "openrouter";
1505
+ var OPENROUTER_MODEL_ID_PREFIX = "openrouter/";
1506
+ var WORKSPACE_OPENROUTER_PROVIDER_ID = "workspace-openrouter";
1507
+ var WORKSPACE_OPENROUTER_MODEL_ID_PREFIX = "workspace-openrouter/";
1508
+ var OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1";
1509
+ var RESERVED_MODEL_PROVIDER_IDS = /* @__PURE__ */ new Set([
1510
+ "openai",
1511
+ "azure",
1512
+ CODEX_PROVIDER_ID,
1513
+ XAI_SUBSCRIPTION_PROVIDER_ID,
1514
+ OPENGENI_GATEWAY_PROVIDER_ID,
1515
+ WORKSPACE_GATEWAY_PROVIDER_ID,
1516
+ OPENROUTER_PROVIDER_ID,
1517
+ WORKSPACE_OPENROUTER_PROVIDER_ID
1518
+ ]);
1519
+ var ModelCostClass = z.enum(["free", "credits"]);
1520
+ var ConfiguredModelCostClass = z.enum(["free", "credits", "subscription", "workspace"]);
1521
+ var ModelNote = z.string().max(500).refine((value) => !/[\r\n|]/u.test(value), {
1522
+ message: "model notes must not contain newlines or the | field separator"
1523
+ });
1524
+ function parseModelCostPolicyJson(raw) {
1525
+ let parsed;
1526
+ try {
1527
+ parsed = JSON.parse(raw);
1528
+ } catch (error) {
1529
+ throw new Error(
1530
+ `OPENGENI_MODEL_COST_POLICY_JSON must be valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1531
+ { cause: error }
1532
+ );
1533
+ }
1534
+ return z.record(z.string().min(1), ModelCostClass).parse(parsed);
1535
+ }
1536
+ function parseModelNotesJson(raw) {
1537
+ let parsed;
1538
+ try {
1539
+ parsed = JSON.parse(raw);
1540
+ } catch (error) {
1541
+ throw new Error(
1542
+ `OPENGENI_MODEL_NOTES_JSON must be valid JSON: ${error instanceof Error ? error.message : String(error)}`,
1543
+ { cause: error }
1544
+ );
1545
+ }
1546
+ return z.record(z.string().min(1), ModelNote).parse(parsed);
1547
+ }
1548
+ function configuredModelNotes(settings) {
1549
+ return parseModelNotesJson(settings.modelNotesJson);
1550
+ }
1551
+ var GatewayCatalogModel = z.object({
1552
+ productId: z.string().min(1),
1553
+ workspaceProductId: z.string().min(1).startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX),
1554
+ upstreamModelId: z.string().min(1),
1555
+ label: z.string().min(1),
1556
+ shortLabel: z.string().min(1).max(64).optional(),
1557
+ providers: z.array(z.string().min(1)).min(1),
1558
+ implicitCaching: z.boolean().default(false),
1559
+ vision: z.boolean().default(false),
1560
+ inputFileMediaTypes: z.array(z.string().min(1)).default([]),
1561
+ contextWindowTokens: z.number().int().positive().default(1e6),
1562
+ effectiveContextWindowTokens: z.number().int().positive().default(9e5),
1563
+ autoCompactTokenLimit: z.number().int().positive().default(85e4),
1564
+ pricing: z.union([ModelPricingSchema, ModelPricingScheduleSchema]).optional(),
1565
+ credentialSource: z.never().optional(),
1566
+ billing: z.never().optional(),
1567
+ apiKey: z.never().optional()
1568
+ }).strict();
1569
+ var OpenRouterCatalogModel = z.object({
1570
+ upstreamModelId: z.string().min(1).endsWith(":free"),
1571
+ label: z.string().min(1),
1572
+ shortLabel: z.string().min(1).max(64).optional(),
1573
+ aliases: z.array(z.string().min(1)).default([]),
1574
+ capabilities: ModelCapabilitiesV1Schema,
1575
+ contextWindowTokens: z.number().int().positive().optional(),
1576
+ effectiveContextWindowTokens: z.number().int().positive().optional(),
1577
+ autoCompactTokenLimit: z.number().int().positive().optional(),
1578
+ toolOutputTruncationTokens: z.number().int().positive().optional(),
1579
+ credentialSource: z.never().optional(),
1580
+ billing: z.never().optional(),
1581
+ pricing: z.never().optional(),
1582
+ apiKey: z.never().optional()
1583
+ }).strict();
1584
+ var DeploymentRegistryBaseUrl = z.string().url().superRefine((value, context) => {
1585
+ const url = new URL(value);
1586
+ if (url.username || url.password) {
1587
+ context.addIssue({
1588
+ code: "custom",
1589
+ message: "database catalog provider baseUrl must not contain userinfo"
1590
+ });
1591
+ }
1592
+ if (url.search) {
1593
+ context.addIssue({
1594
+ code: "custom",
1595
+ message: "database catalog provider baseUrl must not contain a query"
1596
+ });
1597
+ }
1598
+ if (url.hash) {
1599
+ context.addIssue({
1600
+ code: "custom",
1601
+ message: "database catalog provider baseUrl must not contain a fragment"
1602
+ });
1603
+ }
1604
+ });
1605
+ var DeploymentRegistryProviderKind = z.enum(["api-key", "anonymous"]);
1606
+ var DeploymentRegistryModelSchema = RegistryModelSchema.safeExtend({
1607
+ pricing: z.never().optional()
1608
+ }).strict();
1609
+ var DeploymentRegistryProviderSchema = RegistryProviderSchema.safeExtend({
1610
+ kind: DeploymentRegistryProviderKind.default("api-key"),
1611
+ baseUrl: DeploymentRegistryBaseUrl,
1612
+ models: z.array(DeploymentRegistryModelSchema).min(1),
1613
+ apiKey: z.never().optional(),
1614
+ apiKeyEnv: z.never().optional(),
1615
+ defaultHeaders: z.never().optional(),
1616
+ defaultQuery: z.never().optional(),
1617
+ publicDefaultHeaderNames: z.never().optional(),
1618
+ publicDefaultQueryNames: z.never().optional()
1619
+ }).strict();
1620
+ var DeploymentGatewayCatalogModelSchema = GatewayCatalogModel.safeExtend({
1621
+ pricing: z.never().optional()
1622
+ }).strict();
1623
+ var ModelCatalogDocument = z.object({
1624
+ schemaVersion: z.literal(1),
1625
+ /** Canonical deployment default. Omission preserves the V1 first-built-in
1626
+ * fallback for existing documents; operators should set this explicitly
1627
+ * when cutting over a registry or connected-subscription default. */
1628
+ defaultModel: z.string().min(1).optional(),
1629
+ builtInModels: z.array(z.string().min(1)).min(1),
1630
+ registryProviders: z.array(DeploymentRegistryProviderSchema).default([]),
1631
+ gatewayModels: z.array(DeploymentGatewayCatalogModelSchema).default([]),
1632
+ openrouterModels: z.array(OpenRouterCatalogModel).default([]),
1633
+ modelNotes: z.record(z.string().min(1), ModelNote).default({}),
1634
+ billing: z.never().optional(),
1635
+ enabled: z.never().optional(),
1636
+ apiKey: z.never().optional(),
1637
+ bands: z.never().optional()
1638
+ }).strict().superRefine((document, context) => {
1639
+ const productIds = /* @__PURE__ */ new Set();
1640
+ const providerIds = /* @__PURE__ */ new Set();
1641
+ const gatewayUpstreamIds = /* @__PURE__ */ new Set();
1642
+ const add = (id, path) => {
1643
+ if (/[\u000A\u000D|]/u.test(id)) {
1644
+ context.addIssue({
1645
+ code: "custom",
1646
+ path,
1647
+ message: "catalog product ids must not contain newlines or the | field separator"
1648
+ });
1649
+ }
1650
+ if (productIds.has(id)) {
1651
+ context.addIssue({
1652
+ code: "custom",
1653
+ path,
1654
+ message: `duplicate product id ${id}`
1655
+ });
1656
+ }
1657
+ productIds.add(id);
1658
+ };
1659
+ document.builtInModels.forEach((id, index) => add(id, ["builtInModels", index]));
1660
+ document.registryProviders.forEach((provider, providerIndex) => {
1661
+ if (RESERVED_MODEL_PROVIDER_IDS.has(provider.id)) {
1662
+ context.addIssue({
1663
+ code: "custom",
1664
+ path: ["registryProviders", providerIndex, "id"],
1665
+ message: `provider id ${provider.id} is reserved for a reviewed OpenGeni provider`
1666
+ });
1667
+ }
1668
+ if (providerIds.has(provider.id)) {
1669
+ context.addIssue({
1670
+ code: "custom",
1671
+ path: ["registryProviders", providerIndex, "id"],
1672
+ message: `duplicate provider id ${provider.id}`
1673
+ });
1674
+ }
1675
+ providerIds.add(provider.id);
1676
+ provider.models.forEach(
1677
+ (model, modelIndex) => add(model.id, ["registryProviders", providerIndex, "models", modelIndex, "id"])
1678
+ );
1679
+ });
1680
+ document.gatewayModels.forEach((model, index) => {
1681
+ if (gatewayUpstreamIds.has(model.upstreamModelId)) {
1682
+ context.addIssue({
1683
+ code: "custom",
1684
+ path: ["gatewayModels", index, "upstreamModelId"],
1685
+ message: `duplicate Gateway upstream model id ${model.upstreamModelId}`
1686
+ });
1687
+ }
1688
+ gatewayUpstreamIds.add(model.upstreamModelId);
1689
+ add(model.productId, ["gatewayModels", index, "productId"]);
1690
+ add(model.workspaceProductId, ["gatewayModels", index, "workspaceProductId"]);
1691
+ });
1692
+ document.openrouterModels.forEach(
1693
+ (model, index) => add(`${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`, [
1694
+ "openrouterModels",
1695
+ index,
1696
+ "upstreamModelId"
1697
+ ])
1698
+ );
1699
+ if (document.defaultModel && /[\u000A\u000D|]/u.test(document.defaultModel)) {
1700
+ context.addIssue({
1701
+ code: "custom",
1702
+ path: ["defaultModel"],
1703
+ message: "catalog default model must not contain newlines or the | field separator"
1704
+ });
1705
+ }
1706
+ if (document.defaultModel && !productIds.has(document.defaultModel) && !document.defaultModel.startsWith(CODEX_MODEL_ID_PREFIX) && !document.defaultModel.startsWith(XAI_SUBSCRIPTION_MODEL_ID_PREFIX)) {
1707
+ context.addIssue({
1708
+ code: "custom",
1709
+ path: ["defaultModel"],
1710
+ message: "catalog default model must reference deployment catalog membership or a connected-subscription product"
1711
+ });
1712
+ }
1713
+ for (const productId of Object.keys(document.modelNotes)) {
1714
+ if (!productIds.has(productId)) {
1715
+ context.addIssue({
1716
+ code: "custom",
1717
+ path: ["modelNotes", productId],
1718
+ message: "model note references a product id outside the deployment catalog"
1719
+ });
1720
+ }
1721
+ }
1722
+ });
1723
+ function parseModelCatalogDocument(value) {
1724
+ return ModelCatalogDocument.parse(value);
1725
+ }
1726
+ function deploymentRegistryProvidersWithHostCredentials(settings, providers) {
1727
+ const hostProviders = new Map(
1728
+ parseModelProvidersJson(settings.modelProvidersJson).map((provider) => [provider.id, provider])
1729
+ );
1730
+ return providers.map((provider) => {
1731
+ if (provider.kind !== "api-key") return provider;
1732
+ const host = hostProviders.get(provider.id);
1733
+ if (!host || host.kind !== "api-key") {
1734
+ throw new Error(
1735
+ `database model catalog provider ${provider.id} has no matching host-authorized api-key transport`
1736
+ );
1737
+ }
1738
+ const transportIdentity = (candidate) => ({
1739
+ kind: candidate.kind,
1740
+ baseUrl: candidate.baseUrl,
1741
+ api: candidate.api,
1742
+ wireProfile: candidate.wireProfile
1743
+ });
1744
+ if (canonicalJson(transportIdentity(provider)) !== canonicalJson(transportIdentity(host))) {
1745
+ throw new Error(
1746
+ `database model catalog provider ${provider.id} does not match its host-authorized transport`
1747
+ );
1748
+ }
1749
+ return {
1750
+ ...provider,
1751
+ ...host.defaultHeaders === void 0 ? {} : { defaultHeaders: host.defaultHeaders },
1752
+ ...host.defaultQuery === void 0 ? {} : { defaultQuery: host.defaultQuery },
1753
+ ...host.publicDefaultHeaderNames === void 0 ? {} : { publicDefaultHeaderNames: host.publicDefaultHeaderNames },
1754
+ ...host.publicDefaultQueryNames === void 0 ? {} : { publicDefaultQueryNames: host.publicDefaultQueryNames },
1755
+ ...host.apiKey === void 0 ? {} : { apiKey: host.apiKey },
1756
+ ...host.apiKeyEnv === void 0 ? {} : { apiKeyEnv: host.apiKeyEnv }
1757
+ };
1758
+ });
1759
+ }
1760
+ function applyModelCatalogDocument(settings, rawDocument) {
1761
+ const document = parseModelCatalogDocument(rawDocument);
1762
+ const defaultModel = document.defaultModel ?? document.builtInModels[0];
1763
+ const resolved = {
1764
+ ...settings,
1765
+ openaiModel: defaultModel,
1766
+ // Keep the complete built-in membership, including the default. The worker
1767
+ // replaces openaiModel with the exact turn model; the run-scoped router
1768
+ // needs one stable built-in id in this allow-list so a bare provider model
1769
+ // is not temporarily claimed by OpenAI/Azure during name re-resolution.
1770
+ openaiAllowedModels: document.builtInModels.join(","),
1771
+ modelProvidersJson: JSON.stringify(
1772
+ deploymentRegistryProvidersWithHostCredentials(settings, document.registryProviders)
1773
+ ),
1774
+ resolvedGatewayModelsJson: JSON.stringify(document.gatewayModels),
1775
+ resolvedOpenRouterModelsJson: JSON.stringify(document.openrouterModels),
1776
+ modelNotesJson: JSON.stringify(document.modelNotes)
1777
+ };
1778
+ return resolved;
1779
+ }
1473
1780
  var IntegrationOAuthClientConfigSchema = z.object({
1474
1781
  clientId: z.string().min(1),
1475
1782
  clientSecret: z.string().min(1).optional(),
@@ -1477,11 +1784,10 @@ var IntegrationOAuthClientConfigSchema = z.object({
1477
1784
  });
1478
1785
  var VERCEL_AI_GATEWAY_BASE_URL = "https://ai-gateway.vercel.sh/v1";
1479
1786
  var VERCEL_AI_GATEWAY_AI_SDK_BASE_URL = "https://ai-gateway.vercel.sh/v4/ai";
1480
- var OPENGENI_GATEWAY_PROVIDER_ID = "opengeni-gateway";
1481
- var WORKSPACE_GATEWAY_PROVIDER_ID = "workspace-gateway";
1482
- var WORKSPACE_GATEWAY_MODEL_ID_PREFIX = "workspace-gateway/";
1483
1787
  var VERCEL_AI_GATEWAY_CONNECTION_DOMAIN = "ai-gateway.vercel.sh";
1484
1788
  var VERCEL_AI_GATEWAY_CONNECTION_ROLE = "vercel_ai_gateway";
1789
+ var WORKSPACE_OPENROUTER_CONNECTION_DOMAIN = "openrouter.ai";
1790
+ var WORKSPACE_OPENROUTER_CONNECTION_ROLE = "openrouter";
1485
1791
  var CODEX_REALTIME_MODEL_ID = "gpt-live-1-boulder-alpha";
1486
1792
  var SUPERGROK_REALTIME_MODEL_ID = "supergrok/grok-voice-think-fast-2.0";
1487
1793
  var OPENGENI_REALTIME_MODEL_ID_PREFIX = "opengeni-gateway/";
@@ -1540,6 +1846,98 @@ var OPENGENI_GATEWAY_MODELS = {
1540
1846
  implicitCaching: true
1541
1847
  }
1542
1848
  };
1849
+ var OPENGENI_OPENROUTER_MODELS = [
1850
+ OpenRouterCatalogModel.parse({
1851
+ upstreamModelId: "nvidia/nemotron-3-super-120b-a12b:free",
1852
+ label: "Nemotron 3 Super 120B",
1853
+ shortLabel: "Nemotron 3 Super",
1854
+ aliases: [],
1855
+ capabilities: {
1856
+ reasoning: {
1857
+ upstream: "supported",
1858
+ // OpenRouter advertises the reasoning controls, but the catalogue does
1859
+ // not publish this model's accepted effort vocabulary. Preserve that
1860
+ // upstream fact without exposing an unverified runnable selector.
1861
+ runnable: false,
1862
+ efforts: [],
1863
+ defaultEffort: null,
1864
+ required: false
1865
+ },
1866
+ functionCalling: { upstream: "supported", runnable: true },
1867
+ structuredOutput: { upstream: "supported", runnable: true },
1868
+ hostedTools: {
1869
+ webSearch: { upstream: "unknown", runnable: false },
1870
+ xSearch: { upstream: "unknown", runnable: false },
1871
+ codeExecution: { upstream: "unknown", runnable: false },
1872
+ imageGeneration: { upstream: "unknown", runnable: false }
1873
+ },
1874
+ inputModalities: ["text"],
1875
+ inputFileMediaTypes: [],
1876
+ outputModalities: ["text"],
1877
+ transports: {
1878
+ sse: { upstream: "supported", runnable: true },
1879
+ responsesWebSocket: { upstream: "unknown", runnable: false },
1880
+ realtimeAudio: { upstream: "unsupported", runnable: false }
1881
+ },
1882
+ latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
1883
+ },
1884
+ contextWindowTokens: 262144,
1885
+ effectiveContextWindowTokens: 235929,
1886
+ autoCompactTokenLimit: 22e4
1887
+ })
1888
+ ];
1889
+ function defaultGatewayCatalogModels() {
1890
+ return [
1891
+ {
1892
+ ...OPENGENI_GATEWAY_MODELS.deepseek,
1893
+ vision: false,
1894
+ inputFileMediaTypes: [],
1895
+ contextWindowTokens: 1e6,
1896
+ effectiveContextWindowTokens: 9e5,
1897
+ autoCompactTokenLimit: 85e4
1898
+ },
1899
+ {
1900
+ ...OPENGENI_GATEWAY_MODELS.kimi,
1901
+ vision: true,
1902
+ inputFileMediaTypes: ["application/pdf"],
1903
+ contextWindowTokens: 1e6,
1904
+ effectiveContextWindowTokens: 9e5,
1905
+ autoCompactTokenLimit: 85e4
1906
+ }
1907
+ ].map((model) => GatewayCatalogModel.parse(model));
1908
+ }
1909
+ function configuredGatewayCatalogModels(settings) {
1910
+ if (settings.resolvedGatewayModelsJson === void 0) {
1911
+ return defaultGatewayCatalogModels();
1912
+ }
1913
+ return z.array(GatewayCatalogModel).parse(JSON.parse(settings.resolvedGatewayModelsJson));
1914
+ }
1915
+ function configuredGatewayUpstreamModelIds(settings) {
1916
+ return configuredGatewayCatalogModels(settings).map((model) => model.upstreamModelId);
1917
+ }
1918
+ function configuredGatewayWorkspaceProductModelIds(settings) {
1919
+ return configuredGatewayCatalogModels(settings).map((model) => model.workspaceProductId);
1920
+ }
1921
+ function configuredModelInputIdentities(settings) {
1922
+ return configuredModels(settings).flatMap((model) => [model.id, ...model.aliases]);
1923
+ }
1924
+ function configuredOpenRouterCatalogModels(settings) {
1925
+ if (settings.resolvedOpenRouterModelsJson === void 0) {
1926
+ return [...OPENGENI_OPENROUTER_MODELS];
1927
+ }
1928
+ return z.array(OpenRouterCatalogModel).parse(JSON.parse(settings.resolvedOpenRouterModelsJson));
1929
+ }
1930
+ function configuredOpenRouterUpstreamModelIds(settings) {
1931
+ return configuredOpenRouterCatalogModels(settings).map((model) => model.upstreamModelId);
1932
+ }
1933
+ function workspaceOpenRouterProductId(modelId) {
1934
+ return `${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${modelId.startsWith(OPENROUTER_MODEL_ID_PREFIX) ? modelId.slice(OPENROUTER_MODEL_ID_PREFIX.length) : modelId}`;
1935
+ }
1936
+ function configuredOpenRouterWorkspaceProductModelIds(settings) {
1937
+ return configuredOpenRouterCatalogModels(settings).flatMap(
1938
+ (model) => [model.upstreamModelId, ...model.aliases].map(workspaceOpenRouterProductId)
1939
+ );
1940
+ }
1543
1941
  var defaultModelPricing = {
1544
1942
  "gpt-5.6-sol": {
1545
1943
  default: {
@@ -1688,11 +2086,14 @@ function objectStorageConfiguredForWorkspaceArchives(settings) {
1688
2086
  }
1689
2087
  }
1690
2088
  }
1691
- function optional(name) {
1692
- const value = process.env[name];
2089
+ function optionalEnvironmentValue(name, source) {
2090
+ const value = source[name];
1693
2091
  return value && value.trim().length > 0 ? value : void 0;
1694
2092
  }
1695
- function getSettings() {
2093
+ function getSettings(source = process.env) {
2094
+ const optional = (name) => optionalEnvironmentValue(name, source);
2095
+ const modelCatalogSource = optional("OPENGENI_MODEL_CATALOG_SOURCE");
2096
+ const modelCostPolicyJson = optional("OPENGENI_MODEL_COST_POLICY_JSON") ?? (modelCatalogSource === "database" ? "{}" : DEFAULT_MODEL_COST_POLICY_JSON);
1696
2097
  const raw = {
1697
2098
  serviceName: optional("OPENGENI_SERVICE_NAME"),
1698
2099
  environment: optional("OPENGENI_ENVIRONMENT"),
@@ -1877,6 +2278,10 @@ function getSettings() {
1877
2278
  voiceInputAzureAdToken: optional("OPENGENI_VOICE_INPUT_AZURE_AD_TOKEN"),
1878
2279
  voiceInputCodexExperimentalEnabled: optional("OPENGENI_VOICE_INPUT_CODEX_EXPERIMENTAL"),
1879
2280
  modelPricingJson: optional("OPENGENI_MODEL_PRICING_JSON"),
2281
+ modelCatalogSource,
2282
+ modelCostPolicyJson,
2283
+ modelNotesJson: optional("OPENGENI_MODEL_NOTES_JSON"),
2284
+ openrouterApiKey: optional("OPENGENI_OPENROUTER_API_KEY"),
1880
2285
  modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
1881
2286
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
1882
2287
  supergrokSubscriptionEnabled: optional("OPENGENI_SUPERGROK_SUBSCRIPTION_ENABLED"),
@@ -1915,6 +2320,8 @@ function getSettings() {
1915
2320
  modalImageId: optional("OPENGENI_MODAL_IMAGE_ID"),
1916
2321
  modalImageRegistrySecret: optional("OPENGENI_MODAL_IMAGE_REGISTRY_SECRET"),
1917
2322
  modalTimeoutSeconds: optional("OPENGENI_MODAL_TIMEOUT_SECONDS"),
2323
+ modalSandboxCpu: optional("OPENGENI_MODAL_SANDBOX_CPU"),
2324
+ modalSandboxMemoryMiB: optional("OPENGENI_MODAL_SANDBOX_MEMORY_MIB"),
1918
2325
  modalTokenId: optional("OPENGENI_MODAL_TOKEN_ID"),
1919
2326
  modalTokenSecret: optional("OPENGENI_MODAL_TOKEN_SECRET"),
1920
2327
  modalEnvironment: optional("OPENGENI_MODAL_ENVIRONMENT"),
@@ -1925,8 +2332,6 @@ function getSettings() {
1925
2332
  sandboxTerminalEnabled: optional("OPENGENI_SANDBOX_TERMINAL_ENABLED"),
1926
2333
  streamResolutionWidth: optional("OPENGENI_STREAM_RESOLUTION_WIDTH"),
1927
2334
  streamResolutionHeight: optional("OPENGENI_STREAM_RESOLUTION_HEIGHT"),
1928
- computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
1929
- computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
1930
2335
  recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
1931
2336
  workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
1932
2337
  recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
@@ -2082,6 +2487,10 @@ function getSettings() {
2082
2487
  betterAuthAllowedHosts: optional("OPENGENI_BETTER_AUTH_ALLOWED_HOSTS"),
2083
2488
  betterAuthCookieDomain: optional("OPENGENI_BETTER_AUTH_COOKIE_DOMAIN"),
2084
2489
  betterAuthTrustedOrigins: optional("OPENGENI_BETTER_AUTH_TRUSTED_ORIGINS"),
2490
+ managedAuthGoogleClientId: optional("OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_ID"),
2491
+ managedAuthGoogleClientSecret: optional("OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_SECRET"),
2492
+ managedAuthGithubClientId: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_ID"),
2493
+ managedAuthGithubClientSecret: optional("OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_SECRET"),
2085
2494
  managedAuthSessionSetMode: optional("OPENGENI_MANAGED_AUTH_SESSION_SET_MODE"),
2086
2495
  resendApiKey: optional("OPENGENI_RESEND_API_KEY"),
2087
2496
  emailFrom: optional("OPENGENI_EMAIL_FROM"),
@@ -2098,7 +2507,7 @@ function getSettings() {
2098
2507
  sandboxRotationLeadMs: raw.sandboxRotationLeadMs === void 0 && parsed.sandboxBackend === "modal" ? Math.min(36e5, Math.floor(parsed.modalTimeoutSeconds * 1e3 / 2)) : parsed.sandboxRotationLeadMs,
2099
2508
  mcpServers: ensureBuiltInMcpServers(parsed)
2100
2509
  };
2101
- validateSettings(settings);
2510
+ validateSettings(settings, source);
2102
2511
  return settings;
2103
2512
  }
2104
2513
  var LOCAL_FIRST_PARTY_DELEGATION_SECRET = "opengeni-local-first-party-delegation-secret-v1";
@@ -2413,10 +2822,8 @@ function legacyModelCapabilities(settings, input) {
2413
2822
  latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
2414
2823
  });
2415
2824
  }
2416
- function gatewayRequestPolicyForUpstreamModel(upstreamModelId) {
2417
- const model = Object.values(OPENGENI_GATEWAY_MODELS).find(
2418
- (candidate) => candidate.upstreamModelId === upstreamModelId
2419
- );
2825
+ function gatewayRequestPolicyForUpstreamModel(upstreamModelId, models = defaultGatewayCatalogModels()) {
2826
+ const model = models.find((candidate) => candidate.upstreamModelId === upstreamModelId);
2420
2827
  if (!model) {
2421
2828
  return void 0;
2422
2829
  }
@@ -2447,26 +2854,74 @@ function gatewayModelCapabilities(settings, input) {
2447
2854
  latencyModes: [{ id: "standard", upstream: "supported", runnable: true }]
2448
2855
  });
2449
2856
  }
2857
+ function openRouterCustomModelCapabilities(settings) {
2858
+ const legacy = legacyModelCapabilities(settings, {
2859
+ reasoningEffort: false,
2860
+ hostedWebSearch: false
2861
+ });
2862
+ return normalizeCapabilities({
2863
+ ...legacy,
2864
+ functionCalling: { upstream: "supported", runnable: true },
2865
+ inputModalities: ["text"],
2866
+ inputFileMediaTypes: [],
2867
+ transports: {
2868
+ ...legacy.transports,
2869
+ sse: { upstream: "supported", runnable: true }
2870
+ },
2871
+ promptCaching: { upstream: "unsupported", runnable: false, mode: "none" },
2872
+ latencyModes: [{ id: "standard", upstream: "supported", runnable: true }]
2873
+ });
2874
+ }
2450
2875
  function gatewayRegistryProvider(settings, input) {
2451
2876
  const workspace = input.kind === "vercel-gateway-workspace";
2452
- const models = [OPENGENI_GATEWAY_MODELS.deepseek, OPENGENI_GATEWAY_MODELS.kimi].map((model) => {
2453
- const kimi = model === OPENGENI_GATEWAY_MODELS.kimi;
2877
+ const curated = configuredGatewayCatalogModels(settings);
2878
+ const upstreamIds = new Set(curated.map((model) => model.upstreamModelId));
2879
+ const productIds = new Set(
2880
+ parseModelProvidersJson(settings.modelProvidersJson).filter((provider) => provider.id !== WORKSPACE_GATEWAY_PROVIDER_ID).flatMap(
2881
+ (provider) => provider.models.flatMap((model) => [model.id, ...model.aliases ?? []])
2882
+ )
2883
+ );
2884
+ const models = curated.map((model) => {
2885
+ productIds.add(workspace ? model.workspaceProductId : model.productId);
2454
2886
  return {
2455
2887
  id: workspace ? model.workspaceProductId : model.productId,
2456
2888
  upstreamModelId: model.upstreamModelId,
2457
2889
  label: model.label,
2458
- shortLabel: model.shortLabel,
2890
+ ...model.shortLabel ? { shortLabel: model.shortLabel } : {},
2459
2891
  capabilities: gatewayModelCapabilities(settings, {
2460
2892
  implicitCaching: model.implicitCaching,
2461
- vision: kimi,
2462
- inputFileMediaTypes: kimi ? ["application/pdf"] : []
2893
+ vision: model.vision,
2894
+ inputFileMediaTypes: model.inputFileMediaTypes
2463
2895
  }),
2464
- contextWindowTokens: 1e6,
2465
- effectiveContextWindowTokens: 9e5,
2466
- autoCompactTokenLimit: 85e4,
2467
- toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
2896
+ contextWindowTokens: model.contextWindowTokens,
2897
+ effectiveContextWindowTokens: model.effectiveContextWindowTokens,
2898
+ autoCompactTokenLimit: model.autoCompactTokenLimit,
2899
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
2900
+ ...model.pricing === void 0 ? {} : { pricing: model.pricing }
2468
2901
  };
2469
2902
  });
2903
+ if (workspace) {
2904
+ for (const custom of input.customModels ?? []) {
2905
+ const productId = `${WORKSPACE_GATEWAY_MODEL_ID_PREFIX}${custom.upstreamModelId}`;
2906
+ if (upstreamIds.has(custom.upstreamModelId) || productIds.has(productId)) continue;
2907
+ upstreamIds.add(custom.upstreamModelId);
2908
+ productIds.add(productId);
2909
+ models.push({
2910
+ id: productId,
2911
+ upstreamModelId: custom.upstreamModelId,
2912
+ label: custom.label?.trim() || custom.upstreamModelId,
2913
+ capabilities: gatewayModelCapabilities(settings, {
2914
+ implicitCaching: false,
2915
+ vision: false,
2916
+ inputFileMediaTypes: []
2917
+ }),
2918
+ contextWindowTokens: 1e6,
2919
+ effectiveContextWindowTokens: 9e5,
2920
+ autoCompactTokenLimit: 85e4,
2921
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
2922
+ });
2923
+ }
2924
+ }
2470
2925
  return {
2471
2926
  kind: input.kind,
2472
2927
  id: workspace ? WORKSPACE_GATEWAY_PROVIDER_ID : OPENGENI_GATEWAY_PROVIDER_ID,
@@ -2481,47 +2936,138 @@ function gatewayRegistryProvider(settings, input) {
2481
2936
  models
2482
2937
  };
2483
2938
  }
2939
+ function openRouterRegistryProvider(settings, input) {
2940
+ const workspace = input.kind === "openrouter-workspace";
2941
+ const curated = configuredOpenRouterCatalogModels(settings);
2942
+ const upstreamIds = new Set(curated.map((model) => model.upstreamModelId));
2943
+ const productIds = new Set(
2944
+ parseModelProvidersJson(settings.modelProvidersJson).filter((provider) => provider.id !== WORKSPACE_OPENROUTER_PROVIDER_ID).flatMap(
2945
+ (provider) => provider.models.flatMap((model) => [model.id, ...model.aliases ?? []])
2946
+ )
2947
+ );
2948
+ const models = curated.map((model) => {
2949
+ const id = workspace ? workspaceOpenRouterProductId(model.upstreamModelId) : `${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`;
2950
+ const aliases = workspace ? model.aliases.map(workspaceOpenRouterProductId) : model.aliases;
2951
+ productIds.add(id);
2952
+ for (const alias of aliases) productIds.add(alias);
2953
+ return {
2954
+ id,
2955
+ upstreamModelId: model.upstreamModelId,
2956
+ aliases,
2957
+ label: model.label,
2958
+ ...model.shortLabel ? { shortLabel: model.shortLabel } : {},
2959
+ capabilities: model.capabilities,
2960
+ ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
2961
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
2962
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
2963
+ toolOutputTruncationTokens: model.toolOutputTruncationTokens ?? settings.modelToolOutputTruncationTokens
2964
+ };
2965
+ });
2966
+ if (workspace) {
2967
+ for (const custom of input.customModels ?? []) {
2968
+ const productId = `${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${custom.upstreamModelId}`;
2969
+ if (upstreamIds.has(custom.upstreamModelId) || productIds.has(productId)) continue;
2970
+ upstreamIds.add(custom.upstreamModelId);
2971
+ productIds.add(productId);
2972
+ models.push({
2973
+ id: productId,
2974
+ upstreamModelId: custom.upstreamModelId,
2975
+ aliases: [],
2976
+ label: custom.label?.trim() || custom.upstreamModelId,
2977
+ capabilities: openRouterCustomModelCapabilities(settings),
2978
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens
2979
+ });
2980
+ }
2981
+ }
2982
+ if (models.length === 0) return null;
2983
+ const defaultHeaders = {
2984
+ "x-title": "OpenGeni",
2985
+ ...settings.publicBaseUrl ? { "http-referer": settings.publicBaseUrl } : {}
2986
+ };
2987
+ return {
2988
+ kind: input.kind,
2989
+ id: workspace ? WORKSPACE_OPENROUTER_PROVIDER_ID : OPENROUTER_PROVIDER_ID,
2990
+ label: workspace ? "Your OpenRouter" : "OpenRouter",
2991
+ api: "chat",
2992
+ wireProfile: "openai",
2993
+ baseUrl: OPENROUTER_BASE_URL,
2994
+ ...input.apiKey ? { apiKey: input.apiKey } : {},
2995
+ defaultHeaders,
2996
+ publicDefaultHeaderNames: Object.keys(defaultHeaders),
2997
+ models
2998
+ };
2999
+ }
2484
3000
  function configuredRegistryProviders(settings) {
2485
3001
  const providers = parseModelProvidersJson(settings.modelProvidersJson);
2486
- if (!settings.vercelAiGatewayApiKey) {
2487
- return providers;
2488
- }
2489
- if (providers.some((provider) => provider.id === OPENGENI_GATEWAY_PROVIDER_ID)) {
2490
- throw new Error(
2491
- `${OPENGENI_GATEWAY_PROVIDER_ID} is reserved for OPENGENI_VERCEL_AI_GATEWAY_API_KEY`
3002
+ const injected = [...providers];
3003
+ if (settings.vercelAiGatewayApiKey && configuredGatewayCatalogModels(settings).length > 0) {
3004
+ injected.push(
3005
+ gatewayRegistryProvider(settings, {
3006
+ kind: "vercel-gateway-managed",
3007
+ apiKey: settings.vercelAiGatewayApiKey
3008
+ })
2492
3009
  );
2493
3010
  }
2494
- return [
2495
- ...providers,
2496
- gatewayRegistryProvider(settings, {
2497
- kind: "vercel-gateway-managed",
2498
- apiKey: settings.vercelAiGatewayApiKey
2499
- })
2500
- ];
3011
+ const openrouter = settings.openrouterApiKey ? openRouterRegistryProvider(settings, {
3012
+ kind: "openrouter-managed",
3013
+ apiKey: settings.openrouterApiKey
3014
+ }) : null;
3015
+ if (openrouter) injected.push(openrouter);
3016
+ return injected;
2501
3017
  }
2502
- function withWorkspaceGatewayCatalogProvider(settings) {
3018
+ function withWorkspaceGatewayCatalogProvider(settings, customModels = []) {
2503
3019
  const providers = parseModelProvidersJson(settings.modelProvidersJson);
2504
- if (providers.some((provider) => provider.id === WORKSPACE_GATEWAY_PROVIDER_ID)) {
2505
- return settings;
2506
- }
3020
+ const withoutWorkspace = providers.filter(
3021
+ (provider) => provider.id !== WORKSPACE_GATEWAY_PROVIDER_ID
3022
+ );
3023
+ const curatedCount = configuredGatewayCatalogModels(settings).length;
3024
+ if (curatedCount === 0 && customModels.length === 0) return settings;
2507
3025
  return {
2508
3026
  ...settings,
2509
3027
  modelProvidersJson: JSON.stringify([
2510
- ...providers,
2511
- gatewayRegistryProvider(settings, { kind: "vercel-gateway-workspace" })
3028
+ ...withoutWorkspace,
3029
+ gatewayRegistryProvider(settings, {
3030
+ kind: "vercel-gateway-workspace",
3031
+ customModels
3032
+ })
2512
3033
  ])
2513
3034
  };
2514
3035
  }
2515
- function withWorkspaceGatewayCredential(settings, apiKey) {
3036
+ function withWorkspaceGatewayCredential(settings, apiKey, customModels = []) {
2516
3037
  if (!apiKey.trim()) {
2517
3038
  throw new Error("workspace AI Gateway credential is empty");
2518
3039
  }
2519
- const catalogSettings = withWorkspaceGatewayCatalogProvider(settings);
3040
+ const catalogSettings = withWorkspaceGatewayCatalogProvider(settings, customModels);
2520
3041
  const providers = parseModelProvidersJson(catalogSettings.modelProvidersJson).map(
2521
3042
  (provider) => provider.id === WORKSPACE_GATEWAY_PROVIDER_ID ? { ...provider, apiKey } : provider
2522
3043
  );
2523
3044
  return { ...catalogSettings, modelProvidersJson: JSON.stringify(providers) };
2524
3045
  }
3046
+ function withWorkspaceOpenRouterCatalogProvider(settings, customModels = []) {
3047
+ const providers = parseModelProvidersJson(settings.modelProvidersJson);
3048
+ const withoutWorkspace = providers.filter(
3049
+ (provider2) => provider2.id !== WORKSPACE_OPENROUTER_PROVIDER_ID
3050
+ );
3051
+ const provider = openRouterRegistryProvider(settings, {
3052
+ kind: "openrouter-workspace",
3053
+ customModels
3054
+ });
3055
+ if (!provider) return settings;
3056
+ return {
3057
+ ...settings,
3058
+ modelProvidersJson: JSON.stringify([...withoutWorkspace, provider])
3059
+ };
3060
+ }
3061
+ function withWorkspaceOpenRouterCredential(settings, apiKey, customModels = []) {
3062
+ if (!apiKey.trim()) {
3063
+ throw new Error("workspace OpenRouter credential is empty");
3064
+ }
3065
+ const catalogSettings = withWorkspaceOpenRouterCatalogProvider(settings, customModels);
3066
+ const providers = parseModelProvidersJson(catalogSettings.modelProvidersJson).map(
3067
+ (provider) => provider.id === WORKSPACE_OPENROUTER_PROVIDER_ID ? { ...provider, apiKey } : provider
3068
+ );
3069
+ return { ...catalogSettings, modelProvidersJson: JSON.stringify(providers) };
3070
+ }
2525
3071
  var GPT56_FAST_BILLING_MULTIPLIER_BPS = 2e4;
2526
3072
  function productLabelForModelId(modelId) {
2527
3073
  const slug = modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? modelId.slice(CODEX_MODEL_ID_PREFIX.length) : modelId;
@@ -2634,31 +3180,53 @@ function assertLatencyModeRunnable(settings, modelId, latencyMode) {
2634
3180
  }
2635
3181
  }
2636
3182
  function registryCredentialSource(provider) {
2637
- if (provider.kind === "anonymous") {
2638
- return { kind: "deployment", mechanism: "none" };
2639
- }
2640
- if (provider.kind === "codex-subscription") {
2641
- return { kind: "connected_subscription", provider: "codex" };
2642
- }
2643
- if (provider.kind === "xai-subscription") {
2644
- return { kind: "connected_subscription", provider: "xai" };
2645
- }
2646
- if (provider.kind === "vercel-gateway-workspace") {
2647
- return { kind: "workspace_connection", mechanism: "api_key" };
3183
+ switch (provider.kind) {
3184
+ case "anonymous":
3185
+ return { kind: "deployment", mechanism: "none" };
3186
+ case "codex-subscription":
3187
+ return { kind: "connected_subscription", provider: "codex" };
3188
+ case "xai-subscription":
3189
+ return { kind: "connected_subscription", provider: "xai" };
3190
+ case "vercel-gateway-workspace":
3191
+ case "openrouter-workspace":
3192
+ return { kind: "workspace_connection", mechanism: "api_key" };
3193
+ case "api-key":
3194
+ case "vercel-gateway-managed":
3195
+ case "openrouter-managed":
3196
+ return { kind: "deployment", mechanism: "api_key" };
3197
+ default: {
3198
+ const _exhaustive = provider.kind;
3199
+ return _exhaustive;
3200
+ }
2648
3201
  }
2649
- return { kind: "deployment", mechanism: "api_key" };
2650
3202
  }
2651
3203
  function registryBilling(provider) {
2652
- if (provider.kind === "anonymous") {
2653
- return { upstreamPayer: "deployment", metering: "external" };
2654
- }
2655
- if (provider.kind === "codex-subscription" || provider.kind === "xai-subscription") {
2656
- return { upstreamPayer: "connected_subscription", metering: "external" };
2657
- }
2658
- if (provider.kind === "vercel-gateway-workspace") {
2659
- return { upstreamPayer: "workspace", metering: "external" };
3204
+ switch (provider.kind) {
3205
+ case "anonymous":
3206
+ case "openrouter-managed":
3207
+ return { upstreamPayer: "deployment", metering: "external" };
3208
+ case "codex-subscription":
3209
+ case "xai-subscription":
3210
+ return { upstreamPayer: "connected_subscription", metering: "external" };
3211
+ case "vercel-gateway-workspace":
3212
+ case "openrouter-workspace":
3213
+ return { upstreamPayer: "workspace", metering: "external" };
3214
+ case "api-key":
3215
+ case "vercel-gateway-managed":
3216
+ return { upstreamPayer: "deployment", metering: "opengeni_credits" };
3217
+ default: {
3218
+ const _exhaustive = provider.kind;
3219
+ return _exhaustive;
3220
+ }
2660
3221
  }
2661
- return { upstreamPayer: "deployment", metering: "opengeni_credits" };
3222
+ }
3223
+ function configuredCostForModel(settings, productModelId, credentialSource) {
3224
+ if (credentialSource.kind === "workspace_connection") return "workspace";
3225
+ if (credentialSource.kind === "connected_subscription") return "subscription";
3226
+ return parseModelCostPolicyJson(settings.modelCostPolicyJson)[productModelId] ?? "credits";
3227
+ }
3228
+ function modelCostClassForConfiguredModel(_settings, model) {
3229
+ return model.cost;
2662
3230
  }
2663
3231
  function builtinCredentialSource(settings) {
2664
3232
  if (settings.openaiProvider === "azure" && !settings.azureOpenaiApiKey) {
@@ -2697,8 +3265,9 @@ function canonicalJson(value) {
2697
3265
  };
2698
3266
  return JSON.stringify(normalize(value));
2699
3267
  }
2700
- function definitionVersionFor(model, provider) {
3268
+ function definitionVersionFor(model, provider, options = {}) {
2701
3269
  const requestMetadata = staticRequestMetadataForDigest(provider);
3270
+ const includeWireProfile = options.includeWireProfile ?? true;
2702
3271
  const digestInput = canonicalJson({
2703
3272
  schemaVersion: model.schemaVersion,
2704
3273
  id: model.id,
@@ -2707,7 +3276,7 @@ function definitionVersionFor(model, provider) {
2707
3276
  provider: {
2708
3277
  adapterKind: provider.kind,
2709
3278
  wireApi: provider.api,
2710
- wireProfile: provider.wireProfile,
3279
+ ...includeWireProfile ? { wireProfile: provider.wireProfile } : {},
2711
3280
  baseUrl: provider.baseUrl ?? null,
2712
3281
  defaultHeaders: requestMetadata.headers,
2713
3282
  defaultQuery: requestMetadata.query
@@ -2716,18 +3285,29 @@ function definitionVersionFor(model, provider) {
2716
3285
  billing: model.billing,
2717
3286
  executionLimits: model.executionLimits,
2718
3287
  capabilities: model.capabilities,
3288
+ // Workspace-facing free/credits classification is a separate live
3289
+ // deployment policy. Operators must drain/fence accepted turns before
3290
+ // changing it; it is intentionally not a second executable-definition
3291
+ // freeze inside TurnExecutionPolicyV1.
2719
3292
  ...model.requestPolicy ? { requestPolicy: model.requestPolicy } : {},
2720
3293
  pricing: model.pricing ?? null
2721
3294
  });
2722
3295
  return `sha256:${createHash("sha256").update("opengeni:model-definition:v1\n", "utf8").update(digestInput, "utf8").digest("hex")}`;
2723
3296
  }
3297
+ function legacyImplicitOpenAiDefinitionVersionFor(model, provider) {
3298
+ if (provider.wireProfile !== "openai") return null;
3299
+ const { definitionVersion: _definitionVersion, ...modelWithoutVersion } = model;
3300
+ return definitionVersionFor(modelWithoutVersion, provider, {
3301
+ includeWireProfile: false
3302
+ });
3303
+ }
2724
3304
  function builtinProviderId(settings) {
2725
3305
  return settings.openaiProvider === "azure" ? "azure" : "openai";
2726
3306
  }
2727
3307
  function builtinProviderLabel(settings) {
2728
3308
  return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
2729
3309
  }
2730
- function configuredProviders(settings) {
3310
+ function configuredProviders(settings, source = process.env) {
2731
3311
  const credentialSource = builtinCredentialSource(settings);
2732
3312
  const builtin = {
2733
3313
  id: builtinProviderId(settings),
@@ -2756,7 +3336,7 @@ function configuredProviders(settings) {
2756
3336
  wireProfile: provider.wireProfile,
2757
3337
  builtin: false,
2758
3338
  baseUrl: provider.baseUrl,
2759
- apiKey: resolveProviderApiKey(provider),
3339
+ apiKey: resolveProviderApiKey(provider, source),
2760
3340
  defaultQuery: provider.defaultQuery,
2761
3341
  defaultHeaders: provider.defaultHeaders,
2762
3342
  publicDefaultQueryNames: provider.publicDefaultQueryNames,
@@ -2839,8 +3419,14 @@ function withXaiSubscriptionCatalogProvider(settings) {
2839
3419
  { id: "standard", upstream: "supported", runnable: true },
2840
3420
  { id: "fast", upstream: "supported", runnable: true }
2841
3421
  ];
2842
- capabilities.hostedTools.xSearch = { upstream: "supported", runnable: true };
2843
- capabilities.hostedTools.imageGeneration = { upstream: "supported", runnable: true };
3422
+ capabilities.hostedTools.xSearch = {
3423
+ upstream: "supported",
3424
+ runnable: true
3425
+ };
3426
+ capabilities.hostedTools.imageGeneration = {
3427
+ upstream: "supported",
3428
+ runnable: true
3429
+ };
2844
3430
  return {
2845
3431
  id: `${XAI_SUBSCRIPTION_MODEL_ID_PREFIX}${slug}`,
2846
3432
  upstreamModelId: slug,
@@ -2856,7 +3442,10 @@ function withXaiSubscriptionCatalogProvider(settings) {
2856
3442
  };
2857
3443
  })
2858
3444
  };
2859
- return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
3445
+ return {
3446
+ ...settings,
3447
+ modelProvidersJson: JSON.stringify([...providers, provider])
3448
+ };
2860
3449
  }
2861
3450
  function policyProviderIdForModel(settings, modelId) {
2862
3451
  const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
@@ -2869,6 +3458,9 @@ function policyProviderIdForModel(settings, modelId) {
2869
3458
  if (canonicalModelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
2870
3459
  return WORKSPACE_GATEWAY_PROVIDER_ID;
2871
3460
  }
3461
+ if (canonicalModelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX)) {
3462
+ return WORKSPACE_OPENROUTER_PROVIDER_ID;
3463
+ }
2872
3464
  const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
2873
3465
  return configured?.providerId ?? builtinProviderId(settings);
2874
3466
  }
@@ -2881,10 +3473,14 @@ function resolvedExecutionLimits(settings, model) {
2881
3473
  };
2882
3474
  }
2883
3475
  function finalizeConfiguredModel(settings, provider, input) {
2884
- const requestPolicy = provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" ? gatewayRequestPolicyForUpstreamModel(input.upstreamModelId) : void 0;
3476
+ const requestPolicy = provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" ? gatewayRequestPolicyForUpstreamModel(
3477
+ input.upstreamModelId,
3478
+ configuredGatewayCatalogModels(settings)
3479
+ ) : void 0;
2885
3480
  const modelWithoutVersion = {
2886
3481
  schemaVersion: 1,
2887
3482
  ...input,
3483
+ cost: configuredCostForModel(settings, input.id, input.credentialSource),
2888
3484
  ...requestPolicy ? { requestPolicy } : {},
2889
3485
  executionLimits: resolvedExecutionLimits(settings, input)
2890
3486
  };
@@ -2924,10 +3520,10 @@ function assertUniqueModelIdentities(models) {
2924
3520
  }
2925
3521
  }
2926
3522
  }
2927
- function configuredModels(settings) {
3523
+ function configuredModels(settings, source = process.env) {
2928
3524
  const builtinId = builtinProviderId(settings);
2929
3525
  const builtinLabel = builtinProviderLabel(settings);
2930
- const providers = configuredProviders(settings);
3526
+ const providers = configuredProviders(settings, source);
2931
3527
  const providerById = new Map(providers.map((provider) => [provider.id, provider]));
2932
3528
  const pricingSchedules = configuredModelPricingSchedules(settings);
2933
3529
  const parsedRegistry = configuredRegistryProviders(settings);
@@ -3016,7 +3612,10 @@ function configuredModels(settings) {
3016
3612
  }
3017
3613
  }
3018
3614
  assertUniqueModelIdentities(out);
3019
- return out;
3615
+ const defaultIndex = out.findIndex(
3616
+ (model) => model.id === settings.openaiModel || model.aliases.includes(settings.openaiModel)
3617
+ );
3618
+ return defaultIndex > 0 ? [out[defaultIndex], ...out.slice(0, defaultIndex), ...out.slice(defaultIndex + 1)] : out;
3020
3619
  }
3021
3620
  function canonicalizeConfiguredModelId(settings, modelId) {
3022
3621
  const models = configuredModels(settings);
@@ -3051,10 +3650,26 @@ function settingsForTurnExecutionPolicy(settings, modelId) {
3051
3650
  return withXaiSubscriptionCatalogProvider(settings);
3052
3651
  }
3053
3652
  if (modelId.startsWith(WORKSPACE_GATEWAY_MODEL_ID_PREFIX)) {
3653
+ if (resolveModelProvider(settings, modelId)) {
3654
+ return settings;
3655
+ }
3054
3656
  return withWorkspaceGatewayCatalogProvider(settings);
3055
3657
  }
3658
+ if (modelId.startsWith(WORKSPACE_OPENROUTER_MODEL_ID_PREFIX)) {
3659
+ if (resolveModelProvider(settings, modelId)) {
3660
+ return settings;
3661
+ }
3662
+ return withWorkspaceOpenRouterCatalogProvider(settings);
3663
+ }
3056
3664
  return settings;
3057
3665
  }
3666
+ function resolveModelProviderForTurn(settings, modelId) {
3667
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, modelId);
3668
+ return resolveModelProvider(
3669
+ catalogSettings,
3670
+ canonicalizeConfiguredModelId(catalogSettings, modelId)
3671
+ );
3672
+ }
3058
3673
  function resolveTurnExecutionPolicyV1(settings, input) {
3059
3674
  const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
3060
3675
  const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
@@ -3103,7 +3718,12 @@ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
3103
3718
  if (!resolved) {
3104
3719
  throw new Error("Turn execution policy model is no longer configured");
3105
3720
  }
3106
- const mismatched = parsed.providerId !== resolved.provider.id || parsed.upstreamModelId !== resolved.model.upstreamModelId || parsed.wireApi !== resolved.model.api || parsed.definitionVersion !== resolved.model.definitionVersion || canonicalJson(parsed.credentialSource) !== canonicalJson(resolved.model.credentialSource) || canonicalJson(parsed.billing) !== canonicalJson(resolved.model.billing);
3721
+ const legacyImplicitOpenAiDefinitionVersion = legacyImplicitOpenAiDefinitionVersionFor(
3722
+ resolved.model,
3723
+ resolved.provider
3724
+ );
3725
+ const definitionVersionMatches = parsed.definitionVersion === resolved.model.definitionVersion || parsed.definitionVersion === legacyImplicitOpenAiDefinitionVersion;
3726
+ const mismatched = parsed.providerId !== resolved.provider.id || parsed.upstreamModelId !== resolved.model.upstreamModelId || parsed.wireApi !== resolved.model.api || !definitionVersionMatches || canonicalJson(parsed.credentialSource) !== canonicalJson(resolved.model.credentialSource) || canonicalJson(parsed.billing) !== canonicalJson(resolved.model.billing);
3107
3727
  if (mismatched) {
3108
3728
  throw new Error("Turn execution policy does not match the current provider definition");
3109
3729
  }
@@ -3240,12 +3860,7 @@ function calculateModelUsageCostBreakdown(settings, model, usage, options) {
3240
3860
  function calculateGatewayReportedCostMicros(settings, model, inferenceCostUsd, options) {
3241
3861
  return calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options).creditCostMicros;
3242
3862
  }
3243
- function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
3244
- const schedule = configuredModelPricingSchedules(settings)[model];
3245
- if (!schedule) {
3246
- throw new Error(`Missing model pricing for ${model}`);
3247
- }
3248
- const pricing = selectModelPricing(schedule, positiveInt(options?.inputTokens));
3863
+ function parseGatewayReportedCostDecimal(inferenceCostUsd) {
3249
3864
  const match = /^(0|[1-9]\d*)(?:\.(\d{1,18}))?$/.exec(inferenceCostUsd);
3250
3865
  if (!match) {
3251
3866
  throw new Error("Invalid AI Gateway inference cost");
@@ -3254,16 +3869,35 @@ function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd
3254
3869
  const decimalDigits = BigInt(`${match[1]}${fraction}`);
3255
3870
  const decimalScale = 10n ** BigInt(fraction.length);
3256
3871
  const providerNumerator = decimalDigits * 1000000n;
3257
- const providerMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
3872
+ const providerCostMicros = (providerNumerator + decimalScale - 1n) / decimalScale;
3873
+ if (providerCostMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
3874
+ throw new Error("AI Gateway inference cost exceeds the supported billing range");
3875
+ }
3876
+ return {
3877
+ providerNumerator,
3878
+ decimalScale,
3879
+ providerCostMicros: Number(providerCostMicros)
3880
+ };
3881
+ }
3882
+ function calculateGatewayReportedProviderCostMicros(inferenceCostUsd) {
3883
+ return parseGatewayReportedCostDecimal(inferenceCostUsd).providerCostMicros;
3884
+ }
3885
+ function calculateGatewayReportedCostBreakdown(settings, model, inferenceCostUsd, options) {
3886
+ const schedule = configuredModelPricingSchedules(settings)[model];
3887
+ if (!schedule) {
3888
+ throw new Error(`Missing model pricing for ${model}`);
3889
+ }
3890
+ const pricing = selectModelPricing(schedule, positiveInt(options?.inputTokens));
3891
+ const { providerNumerator, decimalScale, providerCostMicros } = parseGatewayReportedCostDecimal(inferenceCostUsd);
3258
3892
  const marginBps = BigInt(1e4 + (pricing.marginBps ?? 0));
3259
3893
  const numerator = providerNumerator * marginBps;
3260
3894
  const denominator = decimalScale * 10000n;
3261
3895
  const creditMicros = (numerator + denominator - 1n) / denominator;
3262
- if (providerMicros > BigInt(Number.MAX_SAFE_INTEGER) || creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
3896
+ if (creditMicros > BigInt(Number.MAX_SAFE_INTEGER)) {
3263
3897
  throw new Error("AI Gateway inference cost exceeds the supported billing range");
3264
3898
  }
3265
3899
  return {
3266
- providerCostMicros: Number(providerMicros),
3900
+ providerCostMicros,
3267
3901
  creditCostMicros: Number(creditMicros)
3268
3902
  };
3269
3903
  }
@@ -3843,7 +4477,7 @@ function isDigestPinnedModalDesktopImage(settings) {
3843
4477
  if (settings.modalImageId) return true;
3844
4478
  return typeof settings.modalImageRef === "string" && MODAL_DESKTOP_IMAGE_DIGEST_REF.test(settings.modalImageRef);
3845
4479
  }
3846
- function validateSettings(settings) {
4480
+ function validateSettings(settings, source = process.env) {
3847
4481
  temporalConnectionOptions(settings);
3848
4482
  if (settings.goalIdleBackoffMs.some((delayMs) => delayMs > settings.goalIdleBackoffMaxMs)) {
3849
4483
  throw new Error(
@@ -3891,6 +4525,37 @@ function validateSettings(settings) {
3891
4525
  );
3892
4526
  }
3893
4527
  }
4528
+ if (Boolean(settings.managedAuthGoogleClientId) !== Boolean(settings.managedAuthGoogleClientSecret)) {
4529
+ throw new Error(
4530
+ "OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_ID and OPENGENI_MANAGED_AUTH_GOOGLE_CLIENT_SECRET must be configured together"
4531
+ );
4532
+ }
4533
+ if (Boolean(settings.managedAuthGithubClientId) !== Boolean(settings.managedAuthGithubClientSecret)) {
4534
+ throw new Error(
4535
+ "OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_ID and OPENGENI_MANAGED_AUTH_GITHUB_CLIENT_SECRET must be configured together"
4536
+ );
4537
+ }
4538
+ const managedSocialAuthConfigured = Boolean(
4539
+ settings.managedAuthGoogleClientId || settings.managedAuthGithubClientId
4540
+ );
4541
+ if (managedSocialAuthConfigured) {
4542
+ if (settings.productAccessMode !== "managed") {
4543
+ throw new Error(
4544
+ "Managed Google/GitHub authentication requires OPENGENI_PRODUCT_ACCESS_MODE=managed"
4545
+ );
4546
+ }
4547
+ const publicOrigin = canonicalPublicOrigin(settings.publicBaseUrl);
4548
+ if (!publicOrigin) {
4549
+ throw new Error(
4550
+ "OPENGENI_PUBLIC_BASE_URL must be a credential-free HTTP(S) origin when managed social authentication is configured"
4551
+ );
4552
+ }
4553
+ if (!publicOrigin.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
4554
+ throw new Error(
4555
+ "OPENGENI_PUBLIC_BASE_URL must use https when managed social authentication is configured outside local/test"
4556
+ );
4557
+ }
4558
+ }
3894
4559
  environmentsEncryptionKeyBytes(settings);
3895
4560
  if (settings.integrationsEnabled) {
3896
4561
  if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
@@ -3947,11 +4612,6 @@ function validateSettings(settings) {
3947
4612
  "OPENGENI_INTEGRATIONS_ENABLED=true is required when personal GitHub OAuth is enabled"
3948
4613
  );
3949
4614
  }
3950
- if (settings.productAccessMode !== "managed") {
3951
- throw new Error(
3952
- "OPENGENI_GITHUB_PERSONAL_OAUTH_ENABLED=true requires OPENGENI_PRODUCT_ACCESS_MODE=managed"
3953
- );
3954
- }
3955
4615
  if (!settings.githubPersonalOauthClientId || !settings.githubPersonalOauthClientSecret) {
3956
4616
  throw new Error(
3957
4617
  "personal GitHub OAuth requires OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_ID and OPENGENI_GITHUB_PERSONAL_OAUTH_CLIENT_SECRET"
@@ -4086,15 +4746,6 @@ function validateSettings(settings) {
4086
4746
  if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
4087
4747
  throw new Error("OPENGENI_BILLING_MODE=stripe requires OPENGENI_PRODUCT_ACCESS_MODE=managed");
4088
4748
  }
4089
- if (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed") {
4090
- const pricing = configuredModelPricing(settings);
4091
- const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
4092
- if (missing.length > 0) {
4093
- throw new Error(
4094
- `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
4095
- );
4096
- }
4097
- }
4098
4749
  if (settings.usageLimitsMode === "static") {
4099
4750
  const limits = configuredStaticUsageLimits(settings);
4100
4751
  if (Object.keys(limits).length === 0) {
@@ -4294,15 +4945,29 @@ function validateSettings(settings) {
4294
4945
  "[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."
4295
4946
  );
4296
4947
  }
4948
+ if (settings.modelCatalogSource === "code") {
4949
+ validateModelCatalogSettings(settings, source);
4950
+ } else {
4951
+ parseModelCostPolicyJson(settings.modelCostPolicyJson);
4952
+ }
4953
+ }
4954
+ function validateModelCatalogSettings(settings, source = process.env) {
4955
+ const costPolicy = parseModelCostPolicyJson(settings.modelCostPolicyJson);
4956
+ const notes = parseModelNotesJson(settings.modelNotesJson);
4297
4957
  const registryProviders = parseModelProvidersJson(settings.modelProvidersJson);
4298
4958
  const builtinId = builtinProviderId(settings);
4299
4959
  const providerIds = /* @__PURE__ */ new Set();
4300
4960
  for (const provider of registryProviders) {
4301
- if (provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" || provider.kind === "xai-subscription") {
4961
+ if (provider.kind === "vercel-gateway-managed" || provider.kind === "vercel-gateway-workspace" || provider.kind === "openrouter-workspace" || provider.kind === "xai-subscription") {
4302
4962
  throw new Error(
4303
4963
  `OPENGENI_MODEL_PROVIDERS_JSON provider kind ${provider.kind} is reserved for a reviewed OpenGeni credential broker`
4304
4964
  );
4305
4965
  }
4966
+ if (RESERVED_MODEL_PROVIDER_IDS.has(provider.id)) {
4967
+ throw new Error(
4968
+ `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} is reserved for a reviewed OpenGeni provider`
4969
+ );
4970
+ }
4306
4971
  if (provider.id === builtinId) {
4307
4972
  throw new Error(
4308
4973
  `OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
@@ -4314,13 +4979,66 @@ function validateSettings(settings) {
4314
4979
  );
4315
4980
  }
4316
4981
  providerIds.add(provider.id);
4317
- if (provider.kind !== "codex-subscription" && provider.kind !== "anonymous" && !resolveProviderApiKey(provider)) {
4982
+ if (provider.kind !== "codex-subscription" && provider.kind !== "anonymous" && !resolveProviderApiKey(provider, source)) {
4318
4983
  throw new Error(
4319
4984
  `OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
4320
4985
  );
4321
4986
  }
4322
4987
  }
4323
- configuredModels(settings);
4988
+ const models = configuredModels(settings, source);
4989
+ const defaultCatalogSettings = settingsForTurnExecutionPolicy(settings, settings.openaiModel);
4990
+ const defaultCatalogModels = defaultCatalogSettings === settings ? models : configuredModels(defaultCatalogSettings, source);
4991
+ if (models.length === 0 && defaultCatalogModels.length === 0) {
4992
+ throw new Error("The resolved model catalog contains no executable models");
4993
+ }
4994
+ const defaultModelId = canonicalizeConfiguredModelId(
4995
+ defaultCatalogSettings,
4996
+ settings.openaiModel
4997
+ );
4998
+ if (!defaultCatalogModels.some((model) => model.id === defaultModelId)) {
4999
+ throw new Error(
5000
+ `The default model ${settings.openaiModel} is not executable in the resolved model catalog`
5001
+ );
5002
+ }
5003
+ const deploymentProductIds = new Set(
5004
+ models.filter((model) => model.credentialSource.kind === "deployment").map((model) => model.id)
5005
+ );
5006
+ const noteProductIds = new Set(models.map((model) => model.id));
5007
+ for (const model of configuredGatewayCatalogModels(settings)) {
5008
+ deploymentProductIds.add(model.productId);
5009
+ noteProductIds.add(model.productId);
5010
+ noteProductIds.add(model.workspaceProductId);
5011
+ }
5012
+ for (const model of configuredOpenRouterCatalogModels(settings)) {
5013
+ const productId = `${OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`;
5014
+ deploymentProductIds.add(productId);
5015
+ noteProductIds.add(productId);
5016
+ noteProductIds.add(`${WORKSPACE_OPENROUTER_MODEL_ID_PREFIX}${model.upstreamModelId}`);
5017
+ }
5018
+ if (settings.modelCatalogSource === "code") {
5019
+ for (const productId of Object.keys(costPolicy)) {
5020
+ if (!deploymentProductIds.has(productId)) {
5021
+ throw new Error(
5022
+ `OPENGENI_MODEL_COST_POLICY_JSON references unknown deployment model ${productId}`
5023
+ );
5024
+ }
5025
+ }
5026
+ }
5027
+ for (const productId of Object.keys(notes)) {
5028
+ if (!noteProductIds.has(productId)) {
5029
+ throw new Error(`OPENGENI_MODEL_NOTES_JSON references unknown catalog model ${productId}`);
5030
+ }
5031
+ }
5032
+ if (settings.billingMode === "stripe" || settings.usageLimitsMode === "managed") {
5033
+ const pricing = configuredModelPricing(settings);
5034
+ const missing = models.filter((model) => model.cost === "credits" && !pricing[model.id]).map((model) => model.id);
5035
+ if (missing.length > 0) {
5036
+ throw new Error(
5037
+ `Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
5038
+ );
5039
+ }
5040
+ }
5041
+ return models;
4324
5042
  }
4325
5043
  function resolveStreamTokenSecret(settings) {
4326
5044
  const explicit = settings.streamTokenSecret?.trim();
@@ -4410,19 +5128,30 @@ export {
4410
5128
  CODEX_REALTIME_MODEL_ID,
4411
5129
  CapabilityStateV1Schema,
4412
5130
  CapabilitySupportV1,
5131
+ ConfiguredModelCostClass,
4413
5132
  DEFAULT_AGENT_INSTRUCTIONS,
4414
5133
  DEFAULT_GOAL_IDLE_BACKOFF_MAX_MS,
4415
5134
  DEFAULT_GOAL_IDLE_BACKOFF_MS,
5135
+ DEFAULT_MODEL_COST_POLICY_JSON,
5136
+ DEFAULT_OPENROUTER_MODEL_ID,
4416
5137
  GOOGLE_DRIVE_PROVIDER_REQUEST_TIMEOUT_MAX_MS,
4417
5138
  GOOGLE_DRIVE_PROVIDER_RETRY_DELAY_MAX_MS,
5139
+ GatewayCatalogModel,
4418
5140
  IntegrationOAuthClientConfigSchema,
4419
5141
  McpServerConnectionRefSchema,
4420
5142
  ModelCapabilitiesV1Schema,
5143
+ ModelCatalogDocument,
5144
+ ModelCostClass,
4421
5145
  ModelProviderApi,
4422
5146
  ModelProviderWireProfile,
4423
5147
  OPENGENI_GATEWAY_MODELS,
4424
5148
  OPENGENI_GATEWAY_PROVIDER_ID,
5149
+ OPENGENI_OPENROUTER_MODELS,
4425
5150
  OPENGENI_REALTIME_MODEL_ID_PREFIX,
5151
+ OPENROUTER_BASE_URL,
5152
+ OPENROUTER_MODEL_ID_PREFIX,
5153
+ OPENROUTER_PROVIDER_ID,
5154
+ OpenRouterCatalogModel,
4426
5155
  RegistryProviderKind,
4427
5156
  SANDBOX_ARCHIVE_CAPTURE_MAX_TIMEOUT_MS,
4428
5157
  SANDBOX_ARCHIVE_CAPTURE_SETTLEMENT_GRACE_MS,
@@ -4438,14 +5167,20 @@ export {
4438
5167
  VERCEL_AI_GATEWAY_CONNECTION_ROLE,
4439
5168
  WORKSPACE_GATEWAY_MODEL_ID_PREFIX,
4440
5169
  WORKSPACE_GATEWAY_PROVIDER_ID,
5170
+ WORKSPACE_OPENROUTER_CONNECTION_DOMAIN,
5171
+ WORKSPACE_OPENROUTER_CONNECTION_ROLE,
5172
+ WORKSPACE_OPENROUTER_MODEL_ID_PREFIX,
5173
+ WORKSPACE_OPENROUTER_PROVIDER_ID,
4441
5174
  WORKSPACE_REALTIME_MODEL_ID_PREFIX,
4442
5175
  XAI_SUBSCRIPTION_MODEL_ID_PREFIX2 as XAI_SUBSCRIPTION_MODEL_ID_PREFIX,
4443
5176
  allowedFirstPartyMcpToolsForSession,
4444
5177
  applyGitAuthPointerEnvironment,
5178
+ applyModelCatalogDocument,
4445
5179
  assertTurnExecutionPolicyMatchesConfigV1,
4446
5180
  builtinProviderId,
4447
5181
  calculateGatewayReportedCostBreakdown,
4448
5182
  calculateGatewayReportedCostMicros,
5183
+ calculateGatewayReportedProviderCostMicros,
4449
5184
  calculateModelUsageCostBreakdown,
4450
5185
  calculateModelUsageCostMicros,
4451
5186
  calculateVideoGenerationCreditCostMicros,
@@ -4457,10 +5192,16 @@ export {
4457
5192
  configuredAllowedModels,
4458
5193
  configuredAllowedReasoningEfforts,
4459
5194
  configuredEntitlements,
5195
+ configuredGatewayUpstreamModelIds,
5196
+ configuredGatewayWorkspaceProductModelIds,
4460
5197
  configuredGoogleDriveSyncLimits,
5198
+ configuredModelInputIdentities,
5199
+ configuredModelNotes,
4461
5200
  configuredModelPricing,
4462
5201
  configuredModelPricingSchedules,
4463
5202
  configuredModels,
5203
+ configuredOpenRouterUpstreamModelIds,
5204
+ configuredOpenRouterWorkspaceProductModelIds,
4464
5205
  configuredProviders,
4465
5206
  configuredStaticUsageLimits,
4466
5207
  contextInputBudgetTokens,
@@ -4481,9 +5222,13 @@ export {
4481
5222
  hasGitHubRepositorySelection,
4482
5223
  isDirectOpenAiApiBaseUrl,
4483
5224
  isUsableVoiceInputSecret,
5225
+ modelCostClassForConfiguredModel,
4484
5226
  parseExposedPorts,
4485
5227
  parseIntegrationsOauthClientsJson,
4486
5228
  parseMcpServers,
5229
+ parseModelCatalogDocument,
5230
+ parseModelCostPolicyJson,
5231
+ parseModelNotesJson,
4487
5232
  parseModelPricingJson,
4488
5233
  parseModelProvidersJson,
4489
5234
  parseSandboxWarmRateJson,
@@ -4500,6 +5245,7 @@ export {
4500
5245
  resolveFirstPartyDelegationSecret,
4501
5246
  resolveFirstPartyMcpToolPolicy,
4502
5247
  resolveModelProvider,
5248
+ resolveModelProviderForTurn,
4503
5249
  resolveNatsCalloutConfig,
4504
5250
  resolveNatsControlPlaneAuth,
4505
5251
  resolveProviderApiKey,
@@ -4523,10 +5269,13 @@ export {
4523
5269
  startupRetryOptions,
4524
5270
  streamTokenDegraded,
4525
5271
  temporalConnectionOptions,
5272
+ validateModelCatalogSettings,
4526
5273
  voiceInputDeploymentConfigured,
4527
5274
  withCodexCatalogProvider,
4528
5275
  withWorkspaceGatewayCatalogProvider,
4529
5276
  withWorkspaceGatewayCredential,
5277
+ withWorkspaceOpenRouterCatalogProvider,
5278
+ withWorkspaceOpenRouterCredential,
4530
5279
  withXaiSubscriptionCatalogProvider
4531
5280
  };
4532
5281
  //# sourceMappingURL=index.js.map