@opengeni/config 0.6.2 → 0.6.9

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
@@ -7,10 +7,22 @@ import {
7
7
  ProductAccessMode,
8
8
  ReasoningEffort,
9
9
  SandboxBackend,
10
+ SessionMcpApprovalPolicy,
10
11
  StaticUsageLimits,
12
+ TurnExecutionPolicyV1,
11
13
  UsageLimitsMode
12
14
  } from "@opengeni/contracts";
13
- import { CODEX_MODEL_ID_PREFIX, CODEX_PROVIDER_ID } from "@opengeni/codex/constants";
15
+ import { CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS } from "@opengeni/codex";
16
+ import {
17
+ CODEX_FALLBACK_MODEL_SLUGS,
18
+ CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
19
+ CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
20
+ CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
21
+ CODEX_MODEL_ID_PREFIX,
22
+ CODEX_PROVIDER_BASE_URL,
23
+ CODEX_PROVIDER_ID
24
+ } from "@opengeni/codex/constants";
25
+ import { createHash } from "crypto";
14
26
  import { z } from "zod";
15
27
  var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
16
28
  var registryId = /^[A-Za-z0-9_-]+$/;
@@ -175,6 +187,9 @@ var SettingsSchema = z.object({
175
187
  // holder of stream:control gets 403 until this flips. Keeps stream:control a
176
188
  // declared-but-inert permission so later hardening is a flag flip.
177
189
  streamControlEnabled: EnvBoolean.default(false),
190
+ // Existing-session explicit tool replacement is gated until every API and
191
+ // worker instance understands durable tools_provided provenance.
192
+ sessionTurnToolReplacementEnabled: EnvBoolean.default(false),
178
193
  toolspaceEnabled: EnvBoolean.default(false),
179
194
  toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
180
195
  // Optional release-coherent bootstrap hint for custom rigs/connected machines
@@ -470,6 +485,15 @@ var SettingsSchema = z.object({
470
485
  // EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
471
486
  // turn the flag ON the moment anyone set the env var to disable it).
472
487
  sandboxOwnershipEnabled: EnvBoolean.default(false),
488
+ // --- standalone rig-verifier ownership rollout flag, default OFF ---
489
+ // Rig verification creates a throwaway provider sandbox outside the normal
490
+ // session-turn path. When enabled, that sandbox must first acquire the same
491
+ // durable lease lifecycle used by session boxes so the global orphan sweep
492
+ // recognizes its exact provider instance. Keep this separate from the general
493
+ // sandboxOwnershipEnabled rollout: every reaper worker must understand verifier
494
+ // leases before dispatch is enabled. When false the verifier fails closed before
495
+ // provider create; it never falls back to the legacy unowned path.
496
+ rigVerificationLeaseOwnershipEnabled: EnvBoolean.default(false),
473
497
  // --- lazy sandbox provisioning rollout flag, default OFF ---
474
498
  // Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
475
499
  // property of the owned path — the SDK never creates/resumes an injected session,
@@ -687,14 +711,8 @@ var SettingsSchema = z.object({
687
711
  allowedTools: z.array(z.string().min(1)).optional(),
688
712
  timeoutMs: z.number().int().positive().optional(),
689
713
  cacheToolsList: z.boolean().default(false),
690
- /**
691
- * Human-approval policy for this server's tools, overlaid per-run from a
692
- * session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
693
- * tools require approval; a string[] = only the listed UNPREFIXED tool
694
- * names do; absent = auto-run (the historical default). Enforced in the
695
- * runtime by attaching `needsApproval` to the matching MCP tools.
696
- */
697
- requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
714
+ /** Runtime approval policy, overlaid from an attempt-frozen session snapshot. */
715
+ requireApproval: SessionMcpApprovalPolicy.optional(),
698
716
  /**
699
717
  * Extra request headers sent to this MCP server (credential injection
700
718
  * for workspace-enabled capability MCPs). Populated at runtime from
@@ -712,11 +730,130 @@ var ModelPricingSchema = z.object({
712
730
  outputMicrosPerMillionTokens: z.number().int().nonnegative(),
713
731
  marginBps: z.number().int().min(0).max(1e5).optional()
714
732
  });
733
+ var ModelPricingScheduleSchema = z.object({
734
+ default: ModelPricingSchema,
735
+ inputTokenTiers: z.array(
736
+ z.object({
737
+ minimumInputTokens: z.number().int().nonnegative(),
738
+ pricing: ModelPricingSchema
739
+ })
740
+ ).optional()
741
+ }).superRefine((schedule, ctx) => {
742
+ let previous = -1;
743
+ for (const [index, tier] of (schedule.inputTokenTiers ?? []).entries()) {
744
+ if (tier.minimumInputTokens <= previous) {
745
+ ctx.addIssue({
746
+ code: "custom",
747
+ path: ["inputTokenTiers", index, "minimumInputTokens"],
748
+ message: "input-token tier thresholds must be strictly increasing"
749
+ });
750
+ }
751
+ previous = tier.minimumInputTokens;
752
+ }
753
+ });
754
+ var CapabilitySupportV1 = z.enum(["supported", "unsupported", "unknown"]);
755
+ var CapabilityStateV1Schema = z.object({
756
+ upstream: CapabilitySupportV1,
757
+ runnable: z.boolean()
758
+ }).superRefine((state, ctx) => {
759
+ if (state.upstream === "unsupported" && state.runnable) {
760
+ ctx.addIssue({
761
+ code: "custom",
762
+ path: ["runnable"],
763
+ message: "an upstream-unsupported capability cannot be runnable"
764
+ });
765
+ }
766
+ });
767
+ var ModelModalityV1 = z.enum(["text", "image", "audio"]);
768
+ var ModelLatencyModeV1 = z.enum(["standard", "priority", "fast"]);
769
+ var ModelCapabilitiesV1Schema = z.object({
770
+ reasoning: CapabilityStateV1Schema.extend({
771
+ efforts: z.array(ReasoningEffort),
772
+ defaultEffort: ReasoningEffort.nullable(),
773
+ required: z.boolean()
774
+ }),
775
+ functionCalling: CapabilityStateV1Schema,
776
+ structuredOutput: CapabilityStateV1Schema,
777
+ hostedTools: z.object({
778
+ webSearch: CapabilityStateV1Schema,
779
+ xSearch: CapabilityStateV1Schema,
780
+ codeExecution: CapabilityStateV1Schema
781
+ }),
782
+ inputModalities: z.array(ModelModalityV1).min(1),
783
+ outputModalities: z.array(ModelModalityV1).min(1),
784
+ transports: z.object({
785
+ sse: CapabilityStateV1Schema,
786
+ responsesWebSocket: CapabilityStateV1Schema,
787
+ realtimeAudio: CapabilityStateV1Schema
788
+ }),
789
+ latencyModes: z.array(
790
+ z.object({
791
+ id: ModelLatencyModeV1,
792
+ upstream: CapabilitySupportV1,
793
+ runnable: z.boolean(),
794
+ billingMultiplierBps: z.number().int().positive().optional()
795
+ })
796
+ ).min(1)
797
+ }).superRefine((capabilities, ctx) => {
798
+ const efforts = new Set(capabilities.reasoning.efforts);
799
+ if (efforts.size !== capabilities.reasoning.efforts.length) {
800
+ ctx.addIssue({
801
+ code: "custom",
802
+ path: ["reasoning", "efforts"],
803
+ message: "reasoning efforts must be unique"
804
+ });
805
+ }
806
+ if (capabilities.reasoning.defaultEffort !== null && !efforts.has(capabilities.reasoning.defaultEffort)) {
807
+ ctx.addIssue({
808
+ code: "custom",
809
+ path: ["reasoning", "defaultEffort"],
810
+ message: "the default reasoning effort must be one of the supported efforts"
811
+ });
812
+ }
813
+ if (capabilities.reasoning.runnable && capabilities.reasoning.efforts.length === 0) {
814
+ ctx.addIssue({
815
+ code: "custom",
816
+ path: ["reasoning", "efforts"],
817
+ message: "a runnable reasoning capability must declare at least one effort"
818
+ });
819
+ }
820
+ for (const field of ["inputModalities", "outputModalities"]) {
821
+ if (new Set(capabilities[field]).size !== capabilities[field].length) {
822
+ ctx.addIssue({
823
+ code: "custom",
824
+ path: [field],
825
+ message: `${field} must be unique`
826
+ });
827
+ }
828
+ }
829
+ const latencyIds = /* @__PURE__ */ new Set();
830
+ for (const [index, mode] of capabilities.latencyModes.entries()) {
831
+ if (latencyIds.has(mode.id)) {
832
+ ctx.addIssue({
833
+ code: "custom",
834
+ path: ["latencyModes", index, "id"],
835
+ message: "latency mode ids must be unique"
836
+ });
837
+ }
838
+ latencyIds.add(mode.id);
839
+ if (mode.upstream === "unsupported" && mode.runnable) {
840
+ ctx.addIssue({
841
+ code: "custom",
842
+ path: ["latencyModes", index, "runnable"],
843
+ message: "an upstream-unsupported latency mode cannot be runnable"
844
+ });
845
+ }
846
+ }
847
+ });
715
848
  var ModelProviderApi = z.enum(["responses", "chat"]);
716
849
  var RegistryProviderKind = z.enum(["api-key", "codex-subscription"]);
717
850
  var RegistryModelSchema = z.object({
718
851
  id: z.string().min(1),
719
- // model id sent to the provider, e.g. "accounts/fireworks/models/glm-5p2"
852
+ // canonical OpenGeni product id
853
+ upstreamModelId: z.string().min(1).optional(),
854
+ // exact provider slug; defaults to id
855
+ aliases: z.array(z.string().min(1)).optional(),
856
+ // accepted input only; never sent upstream
720
857
  label: z.string().min(1).optional(),
721
858
  // display name; defaults to id
722
859
  contextWindowTokens: z.number().int().positive().optional(),
@@ -726,10 +863,30 @@ var RegistryModelSchema = z.object({
726
863
  // the same 1.2x serialization allowance as Codex when materializing output.
727
864
  toolOutputTruncationTokens: z.number().int().positive().optional(),
728
865
  reasoningEffort: z.boolean().optional(),
729
- // model accepts a reasoning-effort control
866
+ // legacy compatibility input/projection
730
867
  hostedWebSearch: z.boolean().optional(),
731
- // provider executes the hosted web_search tool for this model
732
- pricing: ModelPricingSchema.optional()
868
+ // legacy compatibility input/projection
869
+ capabilities: ModelCapabilitiesV1Schema.optional(),
870
+ pricing: z.union([ModelPricingSchema, ModelPricingScheduleSchema]).optional(),
871
+ // Reserved normalized contracts are derived by OpenGeni in V1. Generic
872
+ // registry JSON must not opt itself into workspace BYOK or reattribute cost.
873
+ credentialSource: z.never().optional(),
874
+ billing: z.never().optional()
875
+ }).superRefine((model, ctx) => {
876
+ if (model.capabilities && model.reasoningEffort !== void 0 && model.reasoningEffort !== model.capabilities.reasoning.runnable) {
877
+ ctx.addIssue({
878
+ code: "custom",
879
+ path: ["reasoningEffort"],
880
+ message: "legacy reasoningEffort must agree with capabilities.reasoning.runnable"
881
+ });
882
+ }
883
+ if (model.capabilities && model.hostedWebSearch !== void 0 && model.hostedWebSearch !== model.capabilities.hostedTools.webSearch.runnable) {
884
+ ctx.addIssue({
885
+ code: "custom",
886
+ path: ["hostedWebSearch"],
887
+ message: "legacy hostedWebSearch must agree with capabilities.hostedTools.webSearch.runnable"
888
+ });
889
+ }
733
890
  });
734
891
  var RegistryProviderSchema = z.object({
735
892
  kind: RegistryProviderKind.default("api-key"),
@@ -745,6 +902,12 @@ var RegistryProviderSchema = z.object({
745
902
  // ... OR name of the env var holding the key (preferred)
746
903
  defaultQuery: z.record(z.string(), z.string()).optional(),
747
904
  defaultHeaders: z.record(z.string(), z.string()).optional(),
905
+ publicDefaultQueryNames: z.array(z.string().min(1)).optional(),
906
+ publicDefaultHeaderNames: z.array(z.string().min(1)).optional(),
907
+ // V1 derives these from provider kind. Workspace BYOK is deliberately not a
908
+ // registry switch and requires a separately reviewed encrypted broker.
909
+ credentialSource: z.never().optional(),
910
+ billing: z.never().optional(),
748
911
  models: z.array(RegistryModelSchema).min(1)
749
912
  });
750
913
  var IntegrationOAuthClientConfigSchema = z.object({
@@ -909,6 +1072,7 @@ function getSettings() {
909
1072
  delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
910
1073
  streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
911
1074
  streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
1075
+ sessionTurnToolReplacementEnabled: optional("OPENGENI_SESSION_TURN_TOOL_REPLACEMENT_ENABLED"),
912
1076
  toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
913
1077
  toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
914
1078
  ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
@@ -1025,6 +1189,9 @@ function getSettings() {
1025
1189
  vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
1026
1190
  vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
1027
1191
  sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
1192
+ rigVerificationLeaseOwnershipEnabled: optional(
1193
+ "OPENGENI_RIG_VERIFICATION_LEASE_OWNERSHIP_ENABLED"
1194
+ ),
1028
1195
  sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
1029
1196
  sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
1030
1197
  agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
@@ -1136,6 +1303,287 @@ function resolveProviderApiKey(provider, source = process.env) {
1136
1303
  }
1137
1304
  return void 0;
1138
1305
  }
1306
+ var HTTP_FIELD_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
1307
+ var CREDENTIAL_LIKE_NAME_PARTS = /* @__PURE__ */ new Set([
1308
+ "apikey",
1309
+ "auth",
1310
+ "authorization",
1311
+ "bearer",
1312
+ "credential",
1313
+ "cookie",
1314
+ "key",
1315
+ "password",
1316
+ "secret",
1317
+ "session",
1318
+ "signature",
1319
+ "token"
1320
+ ]);
1321
+ var REASONING_EFFORT_ORDER = new Map(
1322
+ ReasoningEffort.options.map((effort, index) => [effort, index])
1323
+ );
1324
+ var MODALITY_ORDER = new Map(["text", "image", "audio"].map((value, index) => [value, index]));
1325
+ var LATENCY_MODE_ORDER = new Map(
1326
+ ["standard", "priority", "fast"].map((value, index) => [value, index])
1327
+ );
1328
+ function normalizeRegistryBaseUrl(value, providerId) {
1329
+ const url = new URL(value);
1330
+ if (url.username || url.password) {
1331
+ throw new Error(`provider ${providerId} baseUrl must not contain userinfo`);
1332
+ }
1333
+ if (url.search) {
1334
+ throw new Error(
1335
+ `provider ${providerId} baseUrl must not contain a query; move query entries to defaultQuery`
1336
+ );
1337
+ }
1338
+ if (url.hash) {
1339
+ throw new Error(`provider ${providerId} baseUrl must not contain a fragment`);
1340
+ }
1341
+ return url.toString();
1342
+ }
1343
+ function isCredentialLikeMetadataName(name) {
1344
+ return name.toLowerCase().split(/[-_.]/u).some((part) => CREDENTIAL_LIKE_NAME_PARTS.has(part));
1345
+ }
1346
+ function normalizeHeaderMap(providerId, headers) {
1347
+ if (!headers) {
1348
+ return void 0;
1349
+ }
1350
+ const normalized = {};
1351
+ const rawByNormalized = /* @__PURE__ */ new Map();
1352
+ for (const [rawName, value] of Object.entries(headers)) {
1353
+ if (!HTTP_FIELD_NAME.test(rawName)) {
1354
+ throw new Error(
1355
+ `provider ${providerId} defaultHeaders contains invalid HTTP field name ${JSON.stringify(rawName)}`
1356
+ );
1357
+ }
1358
+ const name = rawName.toLowerCase();
1359
+ const previous = rawByNormalized.get(name);
1360
+ if (previous !== void 0) {
1361
+ throw new Error(
1362
+ `provider ${providerId} defaultHeaders names ${JSON.stringify(previous)} and ${JSON.stringify(rawName)} collide after lowercase normalization`
1363
+ );
1364
+ }
1365
+ if (name === "authorization") {
1366
+ throw new Error(
1367
+ `provider ${providerId} defaultHeaders must not override SDK-managed Authorization`
1368
+ );
1369
+ }
1370
+ rawByNormalized.set(name, rawName);
1371
+ normalized[name] = value;
1372
+ }
1373
+ return normalized;
1374
+ }
1375
+ function normalizePublicHeaderNames(providerId, names, headers) {
1376
+ if (!names) {
1377
+ return void 0;
1378
+ }
1379
+ const normalized = [];
1380
+ const seen = /* @__PURE__ */ new Set();
1381
+ for (const rawName of names) {
1382
+ if (!HTTP_FIELD_NAME.test(rawName)) {
1383
+ throw new Error(
1384
+ `provider ${providerId} publicDefaultHeaderNames contains invalid HTTP field name ${JSON.stringify(rawName)}`
1385
+ );
1386
+ }
1387
+ const name = rawName.toLowerCase();
1388
+ if (seen.has(name)) {
1389
+ throw new Error(
1390
+ `provider ${providerId} publicDefaultHeaderNames contains duplicate normalized name ${JSON.stringify(name)}`
1391
+ );
1392
+ }
1393
+ if (!(name in (headers ?? {}))) {
1394
+ throw new Error(
1395
+ `provider ${providerId} publicDefaultHeaderNames declares absent defaultHeaders entry ${JSON.stringify(name)}`
1396
+ );
1397
+ }
1398
+ if (isCredentialLikeMetadataName(name)) {
1399
+ throw new Error(
1400
+ `provider ${providerId} publicDefaultHeaderNames cannot classify credential-like name ${JSON.stringify(name)} as public`
1401
+ );
1402
+ }
1403
+ seen.add(name);
1404
+ normalized.push(name);
1405
+ }
1406
+ return normalized;
1407
+ }
1408
+ function normalizeQueryMap(providerId, query) {
1409
+ if (!query) {
1410
+ return void 0;
1411
+ }
1412
+ for (const name of Object.keys(query)) {
1413
+ if (!name) {
1414
+ throw new Error(`provider ${providerId} defaultQuery contains an empty name`);
1415
+ }
1416
+ }
1417
+ return { ...query };
1418
+ }
1419
+ function normalizePublicQueryNames(providerId, names, query) {
1420
+ if (!names) {
1421
+ return void 0;
1422
+ }
1423
+ const seen = /* @__PURE__ */ new Set();
1424
+ for (const name of names) {
1425
+ if (seen.has(name)) {
1426
+ throw new Error(
1427
+ `provider ${providerId} publicDefaultQueryNames contains duplicate name ${JSON.stringify(name)}`
1428
+ );
1429
+ }
1430
+ if (!(name in (query ?? {}))) {
1431
+ throw new Error(
1432
+ `provider ${providerId} publicDefaultQueryNames declares absent defaultQuery entry ${JSON.stringify(name)}`
1433
+ );
1434
+ }
1435
+ if (isCredentialLikeMetadataName(name)) {
1436
+ throw new Error(
1437
+ `provider ${providerId} publicDefaultQueryNames cannot classify credential-like name ${JSON.stringify(name)} as public`
1438
+ );
1439
+ }
1440
+ seen.add(name);
1441
+ }
1442
+ return [...names];
1443
+ }
1444
+ function normalizeRegistryProvider(provider) {
1445
+ const defaultHeaders = normalizeHeaderMap(provider.id, provider.defaultHeaders);
1446
+ const defaultQuery = normalizeQueryMap(provider.id, provider.defaultQuery);
1447
+ return {
1448
+ ...provider,
1449
+ baseUrl: normalizeRegistryBaseUrl(provider.baseUrl, provider.id),
1450
+ ...defaultHeaders === void 0 ? {} : { defaultHeaders },
1451
+ ...defaultQuery === void 0 ? {} : { defaultQuery },
1452
+ ...provider.publicDefaultHeaderNames === void 0 ? {} : {
1453
+ publicDefaultHeaderNames: normalizePublicHeaderNames(
1454
+ provider.id,
1455
+ provider.publicDefaultHeaderNames,
1456
+ defaultHeaders
1457
+ )
1458
+ },
1459
+ ...provider.publicDefaultQueryNames === void 0 ? {} : {
1460
+ publicDefaultQueryNames: normalizePublicQueryNames(
1461
+ provider.id,
1462
+ provider.publicDefaultQueryNames,
1463
+ defaultQuery
1464
+ )
1465
+ }
1466
+ };
1467
+ }
1468
+ function normalizeModelPricingSchedule(pricing) {
1469
+ return "default" in pricing ? pricing : { default: pricing };
1470
+ }
1471
+ function normalizeCapabilities(capabilities) {
1472
+ const parsed = ModelCapabilitiesV1Schema.parse(capabilities);
1473
+ return {
1474
+ ...parsed,
1475
+ reasoning: {
1476
+ ...parsed.reasoning,
1477
+ efforts: [...parsed.reasoning.efforts].sort(
1478
+ (left, right) => (REASONING_EFFORT_ORDER.get(left) ?? 0) - (REASONING_EFFORT_ORDER.get(right) ?? 0)
1479
+ )
1480
+ },
1481
+ inputModalities: [...parsed.inputModalities].sort(
1482
+ (left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
1483
+ ),
1484
+ outputModalities: [...parsed.outputModalities].sort(
1485
+ (left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
1486
+ ),
1487
+ latencyModes: [...parsed.latencyModes].sort(
1488
+ (left, right) => (LATENCY_MODE_ORDER.get(left.id) ?? 0) - (LATENCY_MODE_ORDER.get(right.id) ?? 0)
1489
+ )
1490
+ };
1491
+ }
1492
+ function legacyModelCapabilities(settings, input) {
1493
+ const reasoningEfforts = input.reasoningEffort ? configuredAllowedReasoningEfforts(settings) : [];
1494
+ return normalizeCapabilities({
1495
+ reasoning: {
1496
+ upstream: input.reasoningEffort ? "supported" : "unknown",
1497
+ runnable: input.reasoningEffort,
1498
+ efforts: reasoningEfforts,
1499
+ defaultEffort: input.reasoningEffort ? settings.openaiReasoningEffort : null,
1500
+ required: false
1501
+ },
1502
+ functionCalling: { upstream: "unknown", runnable: true },
1503
+ structuredOutput: { upstream: "unknown", runnable: false },
1504
+ hostedTools: {
1505
+ webSearch: {
1506
+ upstream: input.hostedWebSearch ? "supported" : "unknown",
1507
+ runnable: input.hostedWebSearch
1508
+ },
1509
+ xSearch: { upstream: "unknown", runnable: false },
1510
+ codeExecution: { upstream: "unknown", runnable: false }
1511
+ },
1512
+ inputModalities: ["text"],
1513
+ outputModalities: ["text"],
1514
+ transports: {
1515
+ sse: { upstream: "unknown", runnable: true },
1516
+ responsesWebSocket: { upstream: "unknown", runnable: false },
1517
+ realtimeAudio: { upstream: "unknown", runnable: false }
1518
+ },
1519
+ latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
1520
+ });
1521
+ }
1522
+ function registryCredentialSource(provider) {
1523
+ return provider.kind === "codex-subscription" ? { kind: "connected_subscription", provider: "codex" } : { kind: "deployment", mechanism: "api_key" };
1524
+ }
1525
+ function registryBilling(provider) {
1526
+ return provider.kind === "codex-subscription" ? { upstreamPayer: "connected_subscription", metering: "external" } : { upstreamPayer: "deployment", metering: "opengeni_credits" };
1527
+ }
1528
+ function builtinCredentialSource(settings) {
1529
+ if (settings.openaiProvider === "azure" && !settings.azureOpenaiApiKey) {
1530
+ return { kind: "deployment", mechanism: "azure_ad_bearer" };
1531
+ }
1532
+ return { kind: "deployment", mechanism: "api_key" };
1533
+ }
1534
+ function staticRequestMetadataForDigest(provider) {
1535
+ const publicHeaders = new Set(provider.publicDefaultHeaderNames ?? []);
1536
+ const publicQuery = new Set(provider.publicDefaultQueryNames ?? []);
1537
+ return {
1538
+ headers: Object.entries(provider.defaultHeaders ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
1539
+ ([name, value]) => publicHeaders.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
1540
+ ),
1541
+ query: Object.entries(provider.defaultQuery ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
1542
+ ([name, value]) => publicQuery.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
1543
+ )
1544
+ };
1545
+ }
1546
+ function canonicalJson(value) {
1547
+ const normalize = (input) => {
1548
+ if (Array.isArray(input)) {
1549
+ return input.map((entry) => normalize(entry));
1550
+ }
1551
+ if (input && typeof input === "object") {
1552
+ const out = {};
1553
+ for (const key of Object.keys(input).sort()) {
1554
+ const child = input[key];
1555
+ if (child !== void 0) {
1556
+ out[key] = normalize(child);
1557
+ }
1558
+ }
1559
+ return out;
1560
+ }
1561
+ return input;
1562
+ };
1563
+ return JSON.stringify(normalize(value));
1564
+ }
1565
+ function definitionVersionFor(model, provider) {
1566
+ const requestMetadata = staticRequestMetadataForDigest(provider);
1567
+ const digestInput = canonicalJson({
1568
+ schemaVersion: model.schemaVersion,
1569
+ id: model.id,
1570
+ providerId: model.providerId,
1571
+ deployment: model.deployment,
1572
+ provider: {
1573
+ adapterKind: provider.kind,
1574
+ wireApi: provider.api,
1575
+ baseUrl: provider.baseUrl ?? null,
1576
+ defaultHeaders: requestMetadata.headers,
1577
+ defaultQuery: requestMetadata.query
1578
+ },
1579
+ credentialSource: model.credentialSource,
1580
+ billing: model.billing,
1581
+ executionLimits: model.executionLimits,
1582
+ capabilities: model.capabilities,
1583
+ pricing: model.pricing ?? null
1584
+ });
1585
+ return `sha256:${createHash("sha256").update("opengeni:model-definition:v1\n", "utf8").update(digestInput, "utf8").digest("hex")}`;
1586
+ }
1139
1587
  function builtinProviderId(settings) {
1140
1588
  return settings.openaiProvider === "azure" ? "azure" : "openai";
1141
1589
  }
@@ -1143,18 +1591,22 @@ function builtinProviderLabel(settings) {
1143
1591
  return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
1144
1592
  }
1145
1593
  function configuredProviders(settings) {
1594
+ const credentialSource = builtinCredentialSource(settings);
1146
1595
  const builtin = {
1147
1596
  id: builtinProviderId(settings),
1148
1597
  label: builtinProviderLabel(settings),
1149
1598
  kind: "api-key",
1150
1599
  api: "responses",
1151
- builtin: true
1600
+ builtin: true,
1601
+ credentialSource,
1602
+ billing: { upstreamPayer: "deployment", metering: "opengeni_credits" }
1152
1603
  };
1153
1604
  if (settings.openaiProvider === "azure") {
1154
- builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
1605
+ const baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
1606
+ builtin.baseUrl = baseUrl ? normalizeRegistryBaseUrl(baseUrl, builtin.id) : void 0;
1155
1607
  builtin.apiKey = settings.azureOpenaiApiKey ?? settings.azureOpenaiAdToken;
1156
1608
  } else {
1157
- builtin.baseUrl = settings.openaiBaseUrl;
1609
+ builtin.baseUrl = settings.openaiBaseUrl ? normalizeRegistryBaseUrl(settings.openaiBaseUrl, builtin.id) : void 0;
1158
1610
  builtin.apiKey = settings.openaiApiKey;
1159
1611
  }
1160
1612
  const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
@@ -1167,73 +1619,195 @@ function configuredProviders(settings) {
1167
1619
  baseUrl: provider.baseUrl,
1168
1620
  apiKey: resolveProviderApiKey(provider),
1169
1621
  defaultQuery: provider.defaultQuery,
1170
- defaultHeaders: provider.defaultHeaders
1622
+ defaultHeaders: provider.defaultHeaders,
1623
+ publicDefaultQueryNames: provider.publicDefaultQueryNames,
1624
+ publicDefaultHeaderNames: provider.publicDefaultHeaderNames,
1625
+ credentialSource: registryCredentialSource(provider),
1626
+ billing: registryBilling(provider)
1171
1627
  })
1172
1628
  );
1173
1629
  return [builtin, ...registry];
1174
1630
  }
1631
+ function withCodexCatalogProvider(settings) {
1632
+ const providers = parseModelProvidersJson(settings.modelProvidersJson);
1633
+ if (providers.some((provider2) => provider2.id === CODEX_PROVIDER_ID)) {
1634
+ return settings;
1635
+ }
1636
+ const provider = {
1637
+ kind: "codex-subscription",
1638
+ id: CODEX_PROVIDER_ID,
1639
+ label: "Codex (ChatGPT subscription)",
1640
+ api: "responses",
1641
+ baseUrl: CODEX_PROVIDER_BASE_URL,
1642
+ models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => ({
1643
+ id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
1644
+ upstreamModelId: slug,
1645
+ label: slug,
1646
+ reasoningEffort: true,
1647
+ contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
1648
+ effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
1649
+ autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
1650
+ toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS
1651
+ }))
1652
+ };
1653
+ return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
1654
+ }
1175
1655
  function policyProviderIdForModel(settings, modelId) {
1176
- if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1656
+ const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
1657
+ if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1177
1658
  return CODEX_PROVIDER_ID;
1178
1659
  }
1179
- const configured = configuredModels(settings).find((model) => model.id === modelId);
1660
+ const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
1180
1661
  return configured?.providerId ?? builtinProviderId(settings);
1181
1662
  }
1663
+ function resolvedExecutionLimits(settings, model) {
1664
+ return {
1665
+ contextWindowTokens: model.contextWindowTokens ?? settings.contextWindowTokens,
1666
+ effectiveContextWindowTokens: model.effectiveContextWindowTokens ?? settings.contextEffectiveWindowTokens ?? null,
1667
+ autoCompactTokenLimit: model.autoCompactTokenLimit ?? settings.contextAutoCompactThresholdTokens ?? null,
1668
+ toolOutputTruncationTokens: model.toolOutputTruncationTokens ?? settings.modelToolOutputTruncationTokens ?? null
1669
+ };
1670
+ }
1671
+ function finalizeConfiguredModel(settings, provider, input) {
1672
+ const modelWithoutVersion = {
1673
+ schemaVersion: 1,
1674
+ ...input,
1675
+ executionLimits: resolvedExecutionLimits(settings, input)
1676
+ };
1677
+ return {
1678
+ ...modelWithoutVersion,
1679
+ definitionVersion: definitionVersionFor(modelWithoutVersion, provider)
1680
+ };
1681
+ }
1682
+ function assertUniqueModelIdentities(models) {
1683
+ const canonicalOwners = /* @__PURE__ */ new Map();
1684
+ for (const model of models) {
1685
+ const previous = canonicalOwners.get(model.id);
1686
+ if (previous !== void 0) {
1687
+ throw new Error(
1688
+ `OPENGENI_MODEL_PROVIDERS_JSON model id ${JSON.stringify(model.id)} is declared by both ${previous} and ${model.providerId}`
1689
+ );
1690
+ }
1691
+ canonicalOwners.set(model.id, model.providerId);
1692
+ }
1693
+ const acceptedInputs = new Map(canonicalOwners);
1694
+ for (const model of models) {
1695
+ const ownAliases = /* @__PURE__ */ new Set();
1696
+ for (const alias of model.aliases) {
1697
+ if (ownAliases.has(alias)) {
1698
+ throw new Error(
1699
+ `OPENGENI_MODEL_PROVIDERS_JSON model ${JSON.stringify(model.id)} contains duplicate alias ${JSON.stringify(alias)}`
1700
+ );
1701
+ }
1702
+ ownAliases.add(alias);
1703
+ const previous = acceptedInputs.get(alias);
1704
+ if (previous !== void 0) {
1705
+ throw new Error(
1706
+ `OPENGENI_MODEL_PROVIDERS_JSON alias ${JSON.stringify(alias)} for model ${JSON.stringify(model.id)} collides with model/provider ${previous}`
1707
+ );
1708
+ }
1709
+ acceptedInputs.set(alias, model.id);
1710
+ }
1711
+ }
1712
+ }
1182
1713
  function configuredModels(settings) {
1183
1714
  const builtinId = builtinProviderId(settings);
1184
1715
  const builtinLabel = builtinProviderLabel(settings);
1716
+ const providers = configuredProviders(settings);
1717
+ const providerById = new Map(providers.map((provider) => [provider.id, provider]));
1718
+ const pricingSchedules = configuredModelPricingSchedules(settings);
1719
+ const parsedRegistry = parseModelProvidersJson(settings.modelProvidersJson);
1185
1720
  const registryOwnedIds = new Set(
1186
- parseModelProvidersJson(settings.modelProvidersJson).flatMap(
1187
- (provider) => provider.models.map((model) => model.id)
1188
- )
1721
+ parsedRegistry.flatMap((provider) => provider.models.map((model) => model.id))
1189
1722
  );
1190
- const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
1723
+ const registryAliases = new Set(
1724
+ parsedRegistry.flatMap((provider) => provider.models.flatMap((model) => model.aliases ?? []))
1725
+ );
1726
+ const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || registryAliases.has(id) || id.includes("/") && registryOwnedIds.has(id);
1727
+ const builtinProvider = providerById.get(builtinId);
1728
+ if (!builtinProvider) {
1729
+ throw new Error(`Built-in model provider ${builtinId} is not configured`);
1730
+ }
1191
1731
  const out = uniqueValues([
1192
1732
  settings.openaiModel,
1193
1733
  ...splitCsv(settings.openaiAllowedModels)
1194
- ]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
1195
- id,
1196
- label: id,
1197
- providerId: builtinId,
1198
- providerLabel: builtinLabel,
1199
- api: "responses",
1200
- contextWindowTokens: settings.contextWindowTokens,
1201
- toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
1202
- reasoningEffort: true,
1203
- hostedWebSearch: settings.webSearchEnabled
1204
- }));
1205
- for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
1734
+ ]).filter((id) => !isRegistryNamespaced(id)).map((id) => {
1735
+ const capabilities = legacyModelCapabilities(settings, {
1736
+ reasoningEffort: true,
1737
+ hostedWebSearch: settings.webSearchEnabled
1738
+ });
1739
+ return finalizeConfiguredModel(settings, builtinProvider, {
1740
+ id,
1741
+ aliases: [],
1742
+ label: id,
1743
+ providerId: builtinId,
1744
+ providerLabel: builtinLabel,
1745
+ api: "responses",
1746
+ upstreamModelId: id,
1747
+ deployment: { upstreamModelId: id, wireApi: "responses" },
1748
+ credentialSource: builtinProvider.credentialSource,
1749
+ billing: builtinProvider.billing,
1750
+ capabilities,
1751
+ ...pricingSchedules[id] === void 0 ? {} : { pricing: pricingSchedules[id] },
1752
+ contextWindowTokens: settings.contextWindowTokens,
1753
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
1754
+ reasoningEffort: capabilities.reasoning.runnable,
1755
+ hostedWebSearch: capabilities.hostedTools.webSearch.runnable
1756
+ });
1757
+ });
1758
+ for (const provider of parsedRegistry) {
1206
1759
  const providerLabel = provider.label ?? provider.id;
1760
+ const resolvedProvider = providerById.get(provider.id);
1761
+ if (!resolvedProvider) {
1762
+ throw new Error(`Registry model provider ${provider.id} is not configured`);
1763
+ }
1207
1764
  for (const model of provider.models) {
1208
- out.push({
1209
- id: model.id,
1210
- label: model.label ?? model.id,
1211
- providerId: provider.id,
1212
- providerLabel,
1213
- api: provider.api,
1214
- ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1215
- ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1216
- ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1217
- ...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
1765
+ const capabilities = model.capabilities ? normalizeCapabilities(model.capabilities) : legacyModelCapabilities(settings, {
1218
1766
  reasoningEffort: model.reasoningEffort ?? false,
1219
1767
  hostedWebSearch: model.hostedWebSearch ?? false
1220
1768
  });
1769
+ const upstreamModelId = model.upstreamModelId ?? model.id;
1770
+ out.push(
1771
+ finalizeConfiguredModel(settings, resolvedProvider, {
1772
+ id: model.id,
1773
+ aliases: [...model.aliases ?? []],
1774
+ label: model.label ?? model.id,
1775
+ providerId: provider.id,
1776
+ providerLabel,
1777
+ api: provider.api,
1778
+ upstreamModelId,
1779
+ deployment: { upstreamModelId, wireApi: provider.api },
1780
+ credentialSource: resolvedProvider.credentialSource,
1781
+ billing: resolvedProvider.billing,
1782
+ capabilities,
1783
+ ...pricingSchedules[model.id] === void 0 ? {} : { pricing: pricingSchedules[model.id] },
1784
+ ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1785
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1786
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1787
+ ...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
1788
+ reasoningEffort: capabilities.reasoning.runnable,
1789
+ hostedWebSearch: capabilities.hostedTools.webSearch.runnable
1790
+ })
1791
+ );
1221
1792
  }
1222
1793
  }
1223
- const seen = /* @__PURE__ */ new Set();
1224
- return out.filter((model) => {
1225
- if (seen.has(model.id)) {
1226
- return false;
1227
- }
1228
- seen.add(model.id);
1229
- return true;
1230
- });
1794
+ assertUniqueModelIdentities(out);
1795
+ return out;
1796
+ }
1797
+ function canonicalizeConfiguredModelId(settings, modelId) {
1798
+ const models = configuredModels(settings);
1799
+ const canonical = models.find((model) => model.id === modelId);
1800
+ if (canonical) {
1801
+ return canonical.id;
1802
+ }
1803
+ return models.find((model) => model.aliases.includes(modelId))?.id ?? modelId;
1231
1804
  }
1232
1805
  function configuredAllowedModels(settings) {
1233
1806
  return configuredModels(settings).map((model) => model.id);
1234
1807
  }
1235
1808
  function resolveModelProvider(settings, modelId) {
1236
- const model = configuredModels(settings).find((candidate) => candidate.id === modelId);
1809
+ const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
1810
+ const model = configuredModels(settings).find((candidate) => candidate.id === canonicalModelId);
1237
1811
  if (!model) {
1238
1812
  return void 0;
1239
1813
  }
@@ -1245,22 +1819,97 @@ function resolveModelProvider(settings, modelId) {
1245
1819
  }
1246
1820
  return { provider, model };
1247
1821
  }
1248
- function configuredModelPricing(settings) {
1822
+ function settingsForTurnExecutionPolicy(settings, modelId) {
1823
+ return settings.codexSubscriptionEnabled && modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? withCodexCatalogProvider(settings) : settings;
1824
+ }
1825
+ function resolveTurnExecutionPolicyV1(settings, input) {
1826
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
1827
+ const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
1828
+ const resolved = resolveModelProvider(catalogSettings, productModelId);
1829
+ if (!resolved) {
1830
+ throw new Error("Turn execution policy model is not present in the configured catalog");
1831
+ }
1832
+ if (input.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, input.requestedModelId) !== productModelId) {
1833
+ throw new Error("Turn execution policy requested model does not canonicalize to its product");
1834
+ }
1835
+ return TurnExecutionPolicyV1.parse({
1836
+ schemaVersion: 1,
1837
+ productModelId,
1838
+ requestedModelId: input.requestedModelId,
1839
+ modelSource: input.modelSource,
1840
+ reasoningEffort: input.reasoningEffort,
1841
+ reasoningSource: input.reasoningSource,
1842
+ providerId: resolved.provider.id,
1843
+ upstreamModelId: resolved.model.upstreamModelId,
1844
+ wireApi: resolved.model.api,
1845
+ credentialSource: resolved.model.credentialSource,
1846
+ billing: resolved.model.billing,
1847
+ definitionVersion: resolved.model.definitionVersion
1848
+ });
1849
+ }
1850
+ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
1851
+ const parsed = TurnExecutionPolicyV1.parse(policy);
1852
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, parsed.productModelId);
1853
+ const canonicalExpectedModel = canonicalizeConfiguredModelId(catalogSettings, expected.modelId);
1854
+ if (parsed.productModelId !== canonicalExpectedModel || parsed.reasoningEffort !== expected.reasoningEffort) {
1855
+ throw new Error("Turn execution policy does not match the accepted turn model/reasoning");
1856
+ }
1857
+ if (parsed.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, parsed.requestedModelId) !== parsed.productModelId) {
1858
+ throw new Error("Turn execution policy requested model does not match its product model");
1859
+ }
1860
+ const resolved = resolveModelProvider(catalogSettings, parsed.productModelId);
1861
+ if (!resolved) {
1862
+ throw new Error("Turn execution policy model is no longer configured");
1863
+ }
1864
+ 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);
1865
+ if (mismatched) {
1866
+ throw new Error("Turn execution policy does not match the current provider definition");
1867
+ }
1868
+ return { policy: parsed, provider: resolved.provider, model: resolved.model };
1869
+ }
1870
+ function configuredModelPricingSchedules(settings) {
1871
+ const defaults = Object.fromEntries(
1872
+ Object.entries(defaultModelPricing).map(([model, pricing]) => [model, { default: pricing }])
1873
+ );
1249
1874
  const registry = {};
1250
1875
  for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
1251
1876
  for (const model of provider.models) {
1252
1877
  if (model.pricing) {
1253
- registry[model.id] = model.pricing;
1878
+ registry[model.id] = normalizeModelPricingSchedule(model.pricing);
1254
1879
  }
1255
1880
  }
1256
1881
  }
1257
- const configured = parseModelPricingJson(settings.modelPricingJson);
1882
+ const configured = Object.fromEntries(
1883
+ Object.entries(parseModelPricingJson(settings.modelPricingJson)).map(([model, pricing]) => [
1884
+ model,
1885
+ { default: pricing }
1886
+ ])
1887
+ );
1258
1888
  return {
1259
- ...defaultModelPricing,
1889
+ ...defaults,
1260
1890
  ...registry,
1261
1891
  ...configured
1262
1892
  };
1263
1893
  }
1894
+ function configuredModelPricing(settings) {
1895
+ return Object.fromEntries(
1896
+ Object.entries(configuredModelPricingSchedules(settings)).map(([model, schedule]) => [
1897
+ model,
1898
+ schedule.default
1899
+ ])
1900
+ );
1901
+ }
1902
+ function selectModelPricing(schedule, inputTokens) {
1903
+ const normalizedInputTokens = Math.max(0, Math.floor(inputTokens));
1904
+ let selected = schedule.default;
1905
+ for (const tier of schedule.inputTokenTiers ?? []) {
1906
+ if (normalizedInputTokens < tier.minimumInputTokens) {
1907
+ break;
1908
+ }
1909
+ selected = tier.pricing;
1910
+ }
1911
+ return selected;
1912
+ }
1264
1913
  function contextInputBudgetTokens(settings) {
1265
1914
  if (settings.contextEffectiveWindowTokens !== void 0) {
1266
1915
  return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
@@ -1303,14 +1952,25 @@ function configuredEntitlements(settings) {
1303
1952
  };
1304
1953
  }
1305
1954
  function calculateModelUsageCostMicros(settings, model, usage) {
1306
- const pricing = configuredModelPricing(settings)[model];
1307
- if (!pricing) {
1955
+ const schedule = configuredModelPricingSchedules(settings)[model];
1956
+ if (!schedule) {
1308
1957
  throw new Error(`Missing model pricing for ${model}`);
1309
1958
  }
1310
1959
  const entries = usage.requestUsageEntries && usage.requestUsageEntries.length > 0 ? usage.requestUsageEntries : [usage];
1311
- const rawCost = entries.reduce((sum, entry) => sum + calculateEntryCostMicros(pricing, entry), 0);
1312
- const marginBps = pricing.marginBps ?? 0;
1313
- return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1960
+ const rawCostByPricing = /* @__PURE__ */ new Map();
1961
+ for (const entry of entries) {
1962
+ const pricing = selectModelPricing(schedule, positiveInt(entry.inputTokens));
1963
+ rawCostByPricing.set(
1964
+ pricing,
1965
+ (rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry)
1966
+ );
1967
+ }
1968
+ let total = 0;
1969
+ for (const [pricing, rawCost] of rawCostByPricing) {
1970
+ const marginBps = pricing.marginBps ?? 0;
1971
+ total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1972
+ }
1973
+ return total;
1314
1974
  }
1315
1975
  function configuredAllowedReasoningEfforts(settings) {
1316
1976
  return uniqueValues([
@@ -1634,7 +2294,14 @@ function parseModelProvidersJson(raw) {
1634
2294
  `OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
1635
2295
  );
1636
2296
  }
1637
- return result.data;
2297
+ try {
2298
+ return normalizeRegistryProvider(result.data);
2299
+ } catch (error) {
2300
+ const message = error instanceof Error ? error.message : String(error);
2301
+ throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${message}`, {
2302
+ cause: error
2303
+ });
2304
+ }
1638
2305
  });
1639
2306
  }
1640
2307
  function parseIntegrationsOauthClientsJson(raw) {
@@ -2040,6 +2707,7 @@ function validateSettings(settings) {
2040
2707
  );
2041
2708
  }
2042
2709
  }
2710
+ configuredModels(settings);
2043
2711
  }
2044
2712
  function resolveStreamTokenSecret(settings) {
2045
2713
  const explicit = settings.streamTokenSecret?.trim();
@@ -2125,21 +2793,27 @@ function delay(ms) {
2125
2793
  }
2126
2794
  export {
2127
2795
  AGENT_INSTRUCTIONS_CORE_PLACEHOLDER,
2796
+ CapabilityStateV1Schema,
2797
+ CapabilitySupportV1,
2128
2798
  DEFAULT_AGENT_INSTRUCTIONS,
2129
2799
  IntegrationOAuthClientConfigSchema,
2130
2800
  McpServerConnectionRefSchema,
2801
+ ModelCapabilitiesV1Schema,
2131
2802
  ModelProviderApi,
2132
2803
  RegistryProviderKind,
2133
2804
  SANDBOX_REQUIRED_ENV,
2134
2805
  applyGitAuthPointerEnvironment,
2806
+ assertTurnExecutionPolicyMatchesConfigV1,
2135
2807
  builtinProviderId,
2136
2808
  calculateModelUsageCostMicros,
2809
+ canonicalizeConfiguredModelId,
2137
2810
  collectGitIdentityEnvironment,
2138
2811
  collectSandboxEnvironment,
2139
2812
  configuredAllowedModels,
2140
2813
  configuredAllowedReasoningEfforts,
2141
2814
  configuredEntitlements,
2142
2815
  configuredModelPricing,
2816
+ configuredModelPricingSchedules,
2143
2817
  configuredModels,
2144
2818
  configuredProviders,
2145
2819
  configuredStaticUsageLimits,
@@ -2170,15 +2844,18 @@ export {
2170
2844
  resolveProviderApiKey,
2171
2845
  resolveRelayTokenSecret,
2172
2846
  resolveStreamTokenSecret,
2847
+ resolveTurnExecutionPolicyV1,
2173
2848
  retryStartupDependency,
2174
2849
  sandboxEnvironmentVariableNames,
2175
2850
  sandboxLifecycleHookIds,
2176
2851
  sandboxPreparationProfiles,
2177
2852
  sandboxWarmRateMicrosPerSecond,
2853
+ selectModelPricing,
2178
2854
  settingsWithResolvedModelContext,
2179
2855
  stableSandboxEnvironmentForRun,
2180
2856
  startupRetryOptions,
2181
2857
  streamTokenDegraded,
2182
- temporalConnectionOptions
2858
+ temporalConnectionOptions,
2859
+ withCodexCatalogProvider
2183
2860
  };
2184
2861
  //# sourceMappingURL=index.js.map