@opengeni/config 0.6.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -4,13 +4,26 @@ import {
4
4
  CAPABILITY_DESCRIPTORS,
5
5
  Entitlements,
6
6
  EntitlementsMode,
7
+ MAX_NESTED_AGENT_DEPTH,
7
8
  ProductAccessMode,
8
9
  ReasoningEffort,
9
10
  SandboxBackend,
11
+ SessionMcpApprovalPolicy,
10
12
  StaticUsageLimits,
13
+ TurnExecutionPolicyV1,
11
14
  UsageLimitsMode
12
15
  } from "@opengeni/contracts";
13
- import { CODEX_MODEL_ID_PREFIX, CODEX_PROVIDER_ID } from "@opengeni/codex/constants";
16
+ import { CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS } from "@opengeni/codex";
17
+ import {
18
+ CODEX_FALLBACK_MODEL_SLUGS,
19
+ CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
20
+ CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
21
+ CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
22
+ CODEX_MODEL_ID_PREFIX,
23
+ CODEX_PROVIDER_BASE_URL,
24
+ CODEX_PROVIDER_ID
25
+ } from "@opengeni/codex/constants";
26
+ import { createHash } from "crypto";
14
27
  import { z } from "zod";
15
28
  var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
16
29
  var registryId = /^[A-Za-z0-9_-]+$/;
@@ -175,6 +188,9 @@ var SettingsSchema = z.object({
175
188
  // holder of stream:control gets 403 until this flips. Keeps stream:control a
176
189
  // declared-but-inert permission so later hardening is a flag flip.
177
190
  streamControlEnabled: EnvBoolean.default(false),
191
+ // Existing-session explicit tool replacement is gated until every API and
192
+ // worker instance understands durable tools_provided provenance.
193
+ sessionTurnToolReplacementEnabled: EnvBoolean.default(false),
178
194
  toolspaceEnabled: EnvBoolean.default(false),
179
195
  toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
180
196
  // Optional release-coherent bootstrap hint for custom rigs/connected machines
@@ -186,6 +202,9 @@ var SettingsSchema = z.object({
186
202
  integrationsStateSecret: z.string().optional(),
187
203
  integrationsAllowPrivateNetworkTargets: EnvBoolean.default(false),
188
204
  integrationsOauthClientsJson: z.string().default("{}"),
205
+ // Undefined is meaningful: the migration boundary persists the product
206
+ // default of 3 when no deployment override is supplied.
207
+ maxNestedAgentDepth: z.coerce.number().int().nonnegative().max(MAX_NESTED_AGENT_DEPTH).optional(),
189
208
  // Session goal guard rails. Goals are designed for runs that legitimately
190
209
  // span days, so length is bounded by pathology detection (no-progress
191
210
  // streaks, budget exhaustion), never by count. goalMaxAutoContinuations is
@@ -269,6 +288,14 @@ var SettingsSchema = z.object({
269
288
  // enable. Turning it off restores the legacy sticky selector without a schema
270
289
  // rollback; the additive lease table/cursor columns become inert.
271
290
  codexCredentialLeasingEnabled: EnvBoolean.default(false),
291
+ // Decision-observability fence. When enabled, the worker emits one
292
+ // bounded, metadata-only adaptive-policy replay record alongside the unchanged
293
+ // sticky-sharded decision. It never changes placement/admission/failover.
294
+ codexFleetPolicyShadowEnabled: EnvBoolean.default(false),
295
+ // Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
296
+ // rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
297
+ // match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
298
+ codexRotationNearExhaustionPct: z.coerce.number().int().min(1).max(100).default(90),
272
299
  openaiReasoningEffort: ReasoningEffort.default("low"),
273
300
  openaiAllowedReasoningEfforts: z.string().default("low,medium,high,xhigh"),
274
301
  openaiResponsesTransport: z.enum(["http", "websocket"]).default("http"),
@@ -470,6 +497,15 @@ var SettingsSchema = z.object({
470
497
  // EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
471
498
  // turn the flag ON the moment anyone set the env var to disable it).
472
499
  sandboxOwnershipEnabled: EnvBoolean.default(false),
500
+ // --- standalone rig-verifier ownership rollout flag, default OFF ---
501
+ // Rig verification creates a throwaway provider sandbox outside the normal
502
+ // session-turn path. When enabled, that sandbox must first acquire the same
503
+ // durable lease lifecycle used by session boxes so the global orphan sweep
504
+ // recognizes its exact provider instance. Keep this separate from the general
505
+ // sandboxOwnershipEnabled rollout: every reaper worker must understand verifier
506
+ // leases before dispatch is enabled. When false the verifier fails closed before
507
+ // provider create; it never falls back to the legacy unowned path.
508
+ rigVerificationLeaseOwnershipEnabled: EnvBoolean.default(false),
473
509
  // --- lazy sandbox provisioning rollout flag, default OFF ---
474
510
  // Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
475
511
  // property of the owned path — the SDK never creates/resumes an injected session,
@@ -687,14 +723,8 @@ var SettingsSchema = z.object({
687
723
  allowedTools: z.array(z.string().min(1)).optional(),
688
724
  timeoutMs: z.number().int().positive().optional(),
689
725
  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(),
726
+ /** Runtime approval policy, overlaid from an attempt-frozen session snapshot. */
727
+ requireApproval: SessionMcpApprovalPolicy.optional(),
698
728
  /**
699
729
  * Extra request headers sent to this MCP server (credential injection
700
730
  * for workspace-enabled capability MCPs). Populated at runtime from
@@ -712,11 +742,130 @@ var ModelPricingSchema = z.object({
712
742
  outputMicrosPerMillionTokens: z.number().int().nonnegative(),
713
743
  marginBps: z.number().int().min(0).max(1e5).optional()
714
744
  });
745
+ var ModelPricingScheduleSchema = z.object({
746
+ default: ModelPricingSchema,
747
+ inputTokenTiers: z.array(
748
+ z.object({
749
+ minimumInputTokens: z.number().int().nonnegative(),
750
+ pricing: ModelPricingSchema
751
+ })
752
+ ).optional()
753
+ }).superRefine((schedule, ctx) => {
754
+ let previous = -1;
755
+ for (const [index, tier] of (schedule.inputTokenTiers ?? []).entries()) {
756
+ if (tier.minimumInputTokens <= previous) {
757
+ ctx.addIssue({
758
+ code: "custom",
759
+ path: ["inputTokenTiers", index, "minimumInputTokens"],
760
+ message: "input-token tier thresholds must be strictly increasing"
761
+ });
762
+ }
763
+ previous = tier.minimumInputTokens;
764
+ }
765
+ });
766
+ var CapabilitySupportV1 = z.enum(["supported", "unsupported", "unknown"]);
767
+ var CapabilityStateV1Schema = z.object({
768
+ upstream: CapabilitySupportV1,
769
+ runnable: z.boolean()
770
+ }).superRefine((state, ctx) => {
771
+ if (state.upstream === "unsupported" && state.runnable) {
772
+ ctx.addIssue({
773
+ code: "custom",
774
+ path: ["runnable"],
775
+ message: "an upstream-unsupported capability cannot be runnable"
776
+ });
777
+ }
778
+ });
779
+ var ModelModalityV1 = z.enum(["text", "image", "audio"]);
780
+ var ModelLatencyModeV1 = z.enum(["standard", "priority", "fast"]);
781
+ var ModelCapabilitiesV1Schema = z.object({
782
+ reasoning: CapabilityStateV1Schema.extend({
783
+ efforts: z.array(ReasoningEffort),
784
+ defaultEffort: ReasoningEffort.nullable(),
785
+ required: z.boolean()
786
+ }),
787
+ functionCalling: CapabilityStateV1Schema,
788
+ structuredOutput: CapabilityStateV1Schema,
789
+ hostedTools: z.object({
790
+ webSearch: CapabilityStateV1Schema,
791
+ xSearch: CapabilityStateV1Schema,
792
+ codeExecution: CapabilityStateV1Schema
793
+ }),
794
+ inputModalities: z.array(ModelModalityV1).min(1),
795
+ outputModalities: z.array(ModelModalityV1).min(1),
796
+ transports: z.object({
797
+ sse: CapabilityStateV1Schema,
798
+ responsesWebSocket: CapabilityStateV1Schema,
799
+ realtimeAudio: CapabilityStateV1Schema
800
+ }),
801
+ latencyModes: z.array(
802
+ z.object({
803
+ id: ModelLatencyModeV1,
804
+ upstream: CapabilitySupportV1,
805
+ runnable: z.boolean(),
806
+ billingMultiplierBps: z.number().int().positive().optional()
807
+ })
808
+ ).min(1)
809
+ }).superRefine((capabilities, ctx) => {
810
+ const efforts = new Set(capabilities.reasoning.efforts);
811
+ if (efforts.size !== capabilities.reasoning.efforts.length) {
812
+ ctx.addIssue({
813
+ code: "custom",
814
+ path: ["reasoning", "efforts"],
815
+ message: "reasoning efforts must be unique"
816
+ });
817
+ }
818
+ if (capabilities.reasoning.defaultEffort !== null && !efforts.has(capabilities.reasoning.defaultEffort)) {
819
+ ctx.addIssue({
820
+ code: "custom",
821
+ path: ["reasoning", "defaultEffort"],
822
+ message: "the default reasoning effort must be one of the supported efforts"
823
+ });
824
+ }
825
+ if (capabilities.reasoning.runnable && capabilities.reasoning.efforts.length === 0) {
826
+ ctx.addIssue({
827
+ code: "custom",
828
+ path: ["reasoning", "efforts"],
829
+ message: "a runnable reasoning capability must declare at least one effort"
830
+ });
831
+ }
832
+ for (const field of ["inputModalities", "outputModalities"]) {
833
+ if (new Set(capabilities[field]).size !== capabilities[field].length) {
834
+ ctx.addIssue({
835
+ code: "custom",
836
+ path: [field],
837
+ message: `${field} must be unique`
838
+ });
839
+ }
840
+ }
841
+ const latencyIds = /* @__PURE__ */ new Set();
842
+ for (const [index, mode] of capabilities.latencyModes.entries()) {
843
+ if (latencyIds.has(mode.id)) {
844
+ ctx.addIssue({
845
+ code: "custom",
846
+ path: ["latencyModes", index, "id"],
847
+ message: "latency mode ids must be unique"
848
+ });
849
+ }
850
+ latencyIds.add(mode.id);
851
+ if (mode.upstream === "unsupported" && mode.runnable) {
852
+ ctx.addIssue({
853
+ code: "custom",
854
+ path: ["latencyModes", index, "runnable"],
855
+ message: "an upstream-unsupported latency mode cannot be runnable"
856
+ });
857
+ }
858
+ }
859
+ });
715
860
  var ModelProviderApi = z.enum(["responses", "chat"]);
716
861
  var RegistryProviderKind = z.enum(["api-key", "codex-subscription"]);
717
862
  var RegistryModelSchema = z.object({
718
863
  id: z.string().min(1),
719
- // model id sent to the provider, e.g. "accounts/fireworks/models/glm-5p2"
864
+ // canonical OpenGeni product id
865
+ upstreamModelId: z.string().min(1).optional(),
866
+ // exact provider slug; defaults to id
867
+ aliases: z.array(z.string().min(1)).optional(),
868
+ // accepted input only; never sent upstream
720
869
  label: z.string().min(1).optional(),
721
870
  // display name; defaults to id
722
871
  contextWindowTokens: z.number().int().positive().optional(),
@@ -726,10 +875,30 @@ var RegistryModelSchema = z.object({
726
875
  // the same 1.2x serialization allowance as Codex when materializing output.
727
876
  toolOutputTruncationTokens: z.number().int().positive().optional(),
728
877
  reasoningEffort: z.boolean().optional(),
729
- // model accepts a reasoning-effort control
878
+ // legacy compatibility input/projection
730
879
  hostedWebSearch: z.boolean().optional(),
731
- // provider executes the hosted web_search tool for this model
732
- pricing: ModelPricingSchema.optional()
880
+ // legacy compatibility input/projection
881
+ capabilities: ModelCapabilitiesV1Schema.optional(),
882
+ pricing: z.union([ModelPricingSchema, ModelPricingScheduleSchema]).optional(),
883
+ // Reserved normalized contracts are derived by OpenGeni in V1. Generic
884
+ // registry JSON must not opt itself into workspace BYOK or reattribute cost.
885
+ credentialSource: z.never().optional(),
886
+ billing: z.never().optional()
887
+ }).superRefine((model, ctx) => {
888
+ if (model.capabilities && model.reasoningEffort !== void 0 && model.reasoningEffort !== model.capabilities.reasoning.runnable) {
889
+ ctx.addIssue({
890
+ code: "custom",
891
+ path: ["reasoningEffort"],
892
+ message: "legacy reasoningEffort must agree with capabilities.reasoning.runnable"
893
+ });
894
+ }
895
+ if (model.capabilities && model.hostedWebSearch !== void 0 && model.hostedWebSearch !== model.capabilities.hostedTools.webSearch.runnable) {
896
+ ctx.addIssue({
897
+ code: "custom",
898
+ path: ["hostedWebSearch"],
899
+ message: "legacy hostedWebSearch must agree with capabilities.hostedTools.webSearch.runnable"
900
+ });
901
+ }
733
902
  });
734
903
  var RegistryProviderSchema = z.object({
735
904
  kind: RegistryProviderKind.default("api-key"),
@@ -745,6 +914,12 @@ var RegistryProviderSchema = z.object({
745
914
  // ... OR name of the env var holding the key (preferred)
746
915
  defaultQuery: z.record(z.string(), z.string()).optional(),
747
916
  defaultHeaders: z.record(z.string(), z.string()).optional(),
917
+ publicDefaultQueryNames: z.array(z.string().min(1)).optional(),
918
+ publicDefaultHeaderNames: z.array(z.string().min(1)).optional(),
919
+ // V1 derives these from provider kind. Workspace BYOK is deliberately not a
920
+ // registry switch and requires a separately reviewed encrypted broker.
921
+ credentialSource: z.never().optional(),
922
+ billing: z.never().optional(),
748
923
  models: z.array(RegistryModelSchema).min(1)
749
924
  });
750
925
  var IntegrationOAuthClientConfigSchema = z.object({
@@ -909,6 +1084,7 @@ function getSettings() {
909
1084
  delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
910
1085
  streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
911
1086
  streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
1087
+ sessionTurnToolReplacementEnabled: optional("OPENGENI_SESSION_TURN_TOOL_REPLACEMENT_ENABLED"),
912
1088
  toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
913
1089
  toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
914
1090
  ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
@@ -919,6 +1095,7 @@ function getSettings() {
919
1095
  "OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
920
1096
  ),
921
1097
  integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
1098
+ maxNestedAgentDepth: optional("OPENGENI_MAX_NESTED_AGENT_DEPTH"),
922
1099
  goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
923
1100
  goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
924
1101
  agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
@@ -947,6 +1124,7 @@ function getSettings() {
947
1124
  codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
948
1125
  codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
949
1126
  codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
1127
+ codexFleetPolicyShadowEnabled: optional("OPENGENI_CODEX_FLEET_POLICY_SHADOW_ENABLED"),
950
1128
  codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
951
1129
  openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
952
1130
  openaiAllowedReasoningEfforts: optional("OPENGENI_OPENAI_ALLOWED_REASONING_EFFORTS"),
@@ -1025,6 +1203,9 @@ function getSettings() {
1025
1203
  vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
1026
1204
  vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
1027
1205
  sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
1206
+ rigVerificationLeaseOwnershipEnabled: optional(
1207
+ "OPENGENI_RIG_VERIFICATION_LEASE_OWNERSHIP_ENABLED"
1208
+ ),
1028
1209
  sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
1029
1210
  sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
1030
1211
  agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
@@ -1136,6 +1317,287 @@ function resolveProviderApiKey(provider, source = process.env) {
1136
1317
  }
1137
1318
  return void 0;
1138
1319
  }
1320
+ var HTTP_FIELD_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
1321
+ var CREDENTIAL_LIKE_NAME_PARTS = /* @__PURE__ */ new Set([
1322
+ "apikey",
1323
+ "auth",
1324
+ "authorization",
1325
+ "bearer",
1326
+ "credential",
1327
+ "cookie",
1328
+ "key",
1329
+ "password",
1330
+ "secret",
1331
+ "session",
1332
+ "signature",
1333
+ "token"
1334
+ ]);
1335
+ var REASONING_EFFORT_ORDER = new Map(
1336
+ ReasoningEffort.options.map((effort, index) => [effort, index])
1337
+ );
1338
+ var MODALITY_ORDER = new Map(["text", "image", "audio"].map((value, index) => [value, index]));
1339
+ var LATENCY_MODE_ORDER = new Map(
1340
+ ["standard", "priority", "fast"].map((value, index) => [value, index])
1341
+ );
1342
+ function normalizeRegistryBaseUrl(value, providerId) {
1343
+ const url = new URL(value);
1344
+ if (url.username || url.password) {
1345
+ throw new Error(`provider ${providerId} baseUrl must not contain userinfo`);
1346
+ }
1347
+ if (url.search) {
1348
+ throw new Error(
1349
+ `provider ${providerId} baseUrl must not contain a query; move query entries to defaultQuery`
1350
+ );
1351
+ }
1352
+ if (url.hash) {
1353
+ throw new Error(`provider ${providerId} baseUrl must not contain a fragment`);
1354
+ }
1355
+ return url.toString();
1356
+ }
1357
+ function isCredentialLikeMetadataName(name) {
1358
+ return name.toLowerCase().split(/[-_.]/u).some((part) => CREDENTIAL_LIKE_NAME_PARTS.has(part));
1359
+ }
1360
+ function normalizeHeaderMap(providerId, headers) {
1361
+ if (!headers) {
1362
+ return void 0;
1363
+ }
1364
+ const normalized = {};
1365
+ const rawByNormalized = /* @__PURE__ */ new Map();
1366
+ for (const [rawName, value] of Object.entries(headers)) {
1367
+ if (!HTTP_FIELD_NAME.test(rawName)) {
1368
+ throw new Error(
1369
+ `provider ${providerId} defaultHeaders contains invalid HTTP field name ${JSON.stringify(rawName)}`
1370
+ );
1371
+ }
1372
+ const name = rawName.toLowerCase();
1373
+ const previous = rawByNormalized.get(name);
1374
+ if (previous !== void 0) {
1375
+ throw new Error(
1376
+ `provider ${providerId} defaultHeaders names ${JSON.stringify(previous)} and ${JSON.stringify(rawName)} collide after lowercase normalization`
1377
+ );
1378
+ }
1379
+ if (name === "authorization") {
1380
+ throw new Error(
1381
+ `provider ${providerId} defaultHeaders must not override SDK-managed Authorization`
1382
+ );
1383
+ }
1384
+ rawByNormalized.set(name, rawName);
1385
+ normalized[name] = value;
1386
+ }
1387
+ return normalized;
1388
+ }
1389
+ function normalizePublicHeaderNames(providerId, names, headers) {
1390
+ if (!names) {
1391
+ return void 0;
1392
+ }
1393
+ const normalized = [];
1394
+ const seen = /* @__PURE__ */ new Set();
1395
+ for (const rawName of names) {
1396
+ if (!HTTP_FIELD_NAME.test(rawName)) {
1397
+ throw new Error(
1398
+ `provider ${providerId} publicDefaultHeaderNames contains invalid HTTP field name ${JSON.stringify(rawName)}`
1399
+ );
1400
+ }
1401
+ const name = rawName.toLowerCase();
1402
+ if (seen.has(name)) {
1403
+ throw new Error(
1404
+ `provider ${providerId} publicDefaultHeaderNames contains duplicate normalized name ${JSON.stringify(name)}`
1405
+ );
1406
+ }
1407
+ if (!(name in (headers ?? {}))) {
1408
+ throw new Error(
1409
+ `provider ${providerId} publicDefaultHeaderNames declares absent defaultHeaders entry ${JSON.stringify(name)}`
1410
+ );
1411
+ }
1412
+ if (isCredentialLikeMetadataName(name)) {
1413
+ throw new Error(
1414
+ `provider ${providerId} publicDefaultHeaderNames cannot classify credential-like name ${JSON.stringify(name)} as public`
1415
+ );
1416
+ }
1417
+ seen.add(name);
1418
+ normalized.push(name);
1419
+ }
1420
+ return normalized;
1421
+ }
1422
+ function normalizeQueryMap(providerId, query) {
1423
+ if (!query) {
1424
+ return void 0;
1425
+ }
1426
+ for (const name of Object.keys(query)) {
1427
+ if (!name) {
1428
+ throw new Error(`provider ${providerId} defaultQuery contains an empty name`);
1429
+ }
1430
+ }
1431
+ return { ...query };
1432
+ }
1433
+ function normalizePublicQueryNames(providerId, names, query) {
1434
+ if (!names) {
1435
+ return void 0;
1436
+ }
1437
+ const seen = /* @__PURE__ */ new Set();
1438
+ for (const name of names) {
1439
+ if (seen.has(name)) {
1440
+ throw new Error(
1441
+ `provider ${providerId} publicDefaultQueryNames contains duplicate name ${JSON.stringify(name)}`
1442
+ );
1443
+ }
1444
+ if (!(name in (query ?? {}))) {
1445
+ throw new Error(
1446
+ `provider ${providerId} publicDefaultQueryNames declares absent defaultQuery entry ${JSON.stringify(name)}`
1447
+ );
1448
+ }
1449
+ if (isCredentialLikeMetadataName(name)) {
1450
+ throw new Error(
1451
+ `provider ${providerId} publicDefaultQueryNames cannot classify credential-like name ${JSON.stringify(name)} as public`
1452
+ );
1453
+ }
1454
+ seen.add(name);
1455
+ }
1456
+ return [...names];
1457
+ }
1458
+ function normalizeRegistryProvider(provider) {
1459
+ const defaultHeaders = normalizeHeaderMap(provider.id, provider.defaultHeaders);
1460
+ const defaultQuery = normalizeQueryMap(provider.id, provider.defaultQuery);
1461
+ return {
1462
+ ...provider,
1463
+ baseUrl: normalizeRegistryBaseUrl(provider.baseUrl, provider.id),
1464
+ ...defaultHeaders === void 0 ? {} : { defaultHeaders },
1465
+ ...defaultQuery === void 0 ? {} : { defaultQuery },
1466
+ ...provider.publicDefaultHeaderNames === void 0 ? {} : {
1467
+ publicDefaultHeaderNames: normalizePublicHeaderNames(
1468
+ provider.id,
1469
+ provider.publicDefaultHeaderNames,
1470
+ defaultHeaders
1471
+ )
1472
+ },
1473
+ ...provider.publicDefaultQueryNames === void 0 ? {} : {
1474
+ publicDefaultQueryNames: normalizePublicQueryNames(
1475
+ provider.id,
1476
+ provider.publicDefaultQueryNames,
1477
+ defaultQuery
1478
+ )
1479
+ }
1480
+ };
1481
+ }
1482
+ function normalizeModelPricingSchedule(pricing) {
1483
+ return "default" in pricing ? pricing : { default: pricing };
1484
+ }
1485
+ function normalizeCapabilities(capabilities) {
1486
+ const parsed = ModelCapabilitiesV1Schema.parse(capabilities);
1487
+ return {
1488
+ ...parsed,
1489
+ reasoning: {
1490
+ ...parsed.reasoning,
1491
+ efforts: [...parsed.reasoning.efforts].sort(
1492
+ (left, right) => (REASONING_EFFORT_ORDER.get(left) ?? 0) - (REASONING_EFFORT_ORDER.get(right) ?? 0)
1493
+ )
1494
+ },
1495
+ inputModalities: [...parsed.inputModalities].sort(
1496
+ (left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
1497
+ ),
1498
+ outputModalities: [...parsed.outputModalities].sort(
1499
+ (left, right) => (MODALITY_ORDER.get(left) ?? 0) - (MODALITY_ORDER.get(right) ?? 0)
1500
+ ),
1501
+ latencyModes: [...parsed.latencyModes].sort(
1502
+ (left, right) => (LATENCY_MODE_ORDER.get(left.id) ?? 0) - (LATENCY_MODE_ORDER.get(right.id) ?? 0)
1503
+ )
1504
+ };
1505
+ }
1506
+ function legacyModelCapabilities(settings, input) {
1507
+ const reasoningEfforts = input.reasoningEffort ? configuredAllowedReasoningEfforts(settings) : [];
1508
+ return normalizeCapabilities({
1509
+ reasoning: {
1510
+ upstream: input.reasoningEffort ? "supported" : "unknown",
1511
+ runnable: input.reasoningEffort,
1512
+ efforts: reasoningEfforts,
1513
+ defaultEffort: input.reasoningEffort ? settings.openaiReasoningEffort : null,
1514
+ required: false
1515
+ },
1516
+ functionCalling: { upstream: "unknown", runnable: true },
1517
+ structuredOutput: { upstream: "unknown", runnable: false },
1518
+ hostedTools: {
1519
+ webSearch: {
1520
+ upstream: input.hostedWebSearch ? "supported" : "unknown",
1521
+ runnable: input.hostedWebSearch
1522
+ },
1523
+ xSearch: { upstream: "unknown", runnable: false },
1524
+ codeExecution: { upstream: "unknown", runnable: false }
1525
+ },
1526
+ inputModalities: ["text"],
1527
+ outputModalities: ["text"],
1528
+ transports: {
1529
+ sse: { upstream: "unknown", runnable: true },
1530
+ responsesWebSocket: { upstream: "unknown", runnable: false },
1531
+ realtimeAudio: { upstream: "unknown", runnable: false }
1532
+ },
1533
+ latencyModes: [{ id: "standard", upstream: "unknown", runnable: true }]
1534
+ });
1535
+ }
1536
+ function registryCredentialSource(provider) {
1537
+ return provider.kind === "codex-subscription" ? { kind: "connected_subscription", provider: "codex" } : { kind: "deployment", mechanism: "api_key" };
1538
+ }
1539
+ function registryBilling(provider) {
1540
+ return provider.kind === "codex-subscription" ? { upstreamPayer: "connected_subscription", metering: "external" } : { upstreamPayer: "deployment", metering: "opengeni_credits" };
1541
+ }
1542
+ function builtinCredentialSource(settings) {
1543
+ if (settings.openaiProvider === "azure" && !settings.azureOpenaiApiKey) {
1544
+ return { kind: "deployment", mechanism: "azure_ad_bearer" };
1545
+ }
1546
+ return { kind: "deployment", mechanism: "api_key" };
1547
+ }
1548
+ function staticRequestMetadataForDigest(provider) {
1549
+ const publicHeaders = new Set(provider.publicDefaultHeaderNames ?? []);
1550
+ const publicQuery = new Set(provider.publicDefaultQueryNames ?? []);
1551
+ return {
1552
+ headers: Object.entries(provider.defaultHeaders ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
1553
+ ([name, value]) => publicHeaders.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
1554
+ ),
1555
+ query: Object.entries(provider.defaultQuery ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(
1556
+ ([name, value]) => publicQuery.has(name) ? { name, classification: "public", value } : { name, classification: "secret" }
1557
+ )
1558
+ };
1559
+ }
1560
+ function canonicalJson(value) {
1561
+ const normalize = (input) => {
1562
+ if (Array.isArray(input)) {
1563
+ return input.map((entry) => normalize(entry));
1564
+ }
1565
+ if (input && typeof input === "object") {
1566
+ const out = {};
1567
+ for (const key of Object.keys(input).sort()) {
1568
+ const child = input[key];
1569
+ if (child !== void 0) {
1570
+ out[key] = normalize(child);
1571
+ }
1572
+ }
1573
+ return out;
1574
+ }
1575
+ return input;
1576
+ };
1577
+ return JSON.stringify(normalize(value));
1578
+ }
1579
+ function definitionVersionFor(model, provider) {
1580
+ const requestMetadata = staticRequestMetadataForDigest(provider);
1581
+ const digestInput = canonicalJson({
1582
+ schemaVersion: model.schemaVersion,
1583
+ id: model.id,
1584
+ providerId: model.providerId,
1585
+ deployment: model.deployment,
1586
+ provider: {
1587
+ adapterKind: provider.kind,
1588
+ wireApi: provider.api,
1589
+ baseUrl: provider.baseUrl ?? null,
1590
+ defaultHeaders: requestMetadata.headers,
1591
+ defaultQuery: requestMetadata.query
1592
+ },
1593
+ credentialSource: model.credentialSource,
1594
+ billing: model.billing,
1595
+ executionLimits: model.executionLimits,
1596
+ capabilities: model.capabilities,
1597
+ pricing: model.pricing ?? null
1598
+ });
1599
+ return `sha256:${createHash("sha256").update("opengeni:model-definition:v1\n", "utf8").update(digestInput, "utf8").digest("hex")}`;
1600
+ }
1139
1601
  function builtinProviderId(settings) {
1140
1602
  return settings.openaiProvider === "azure" ? "azure" : "openai";
1141
1603
  }
@@ -1143,18 +1605,22 @@ function builtinProviderLabel(settings) {
1143
1605
  return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
1144
1606
  }
1145
1607
  function configuredProviders(settings) {
1608
+ const credentialSource = builtinCredentialSource(settings);
1146
1609
  const builtin = {
1147
1610
  id: builtinProviderId(settings),
1148
1611
  label: builtinProviderLabel(settings),
1149
1612
  kind: "api-key",
1150
1613
  api: "responses",
1151
- builtin: true
1614
+ builtin: true,
1615
+ credentialSource,
1616
+ billing: { upstreamPayer: "deployment", metering: "opengeni_credits" }
1152
1617
  };
1153
1618
  if (settings.openaiProvider === "azure") {
1154
- builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
1619
+ const baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
1620
+ builtin.baseUrl = baseUrl ? normalizeRegistryBaseUrl(baseUrl, builtin.id) : void 0;
1155
1621
  builtin.apiKey = settings.azureOpenaiApiKey ?? settings.azureOpenaiAdToken;
1156
1622
  } else {
1157
- builtin.baseUrl = settings.openaiBaseUrl;
1623
+ builtin.baseUrl = settings.openaiBaseUrl ? normalizeRegistryBaseUrl(settings.openaiBaseUrl, builtin.id) : void 0;
1158
1624
  builtin.apiKey = settings.openaiApiKey;
1159
1625
  }
1160
1626
  const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
@@ -1167,73 +1633,195 @@ function configuredProviders(settings) {
1167
1633
  baseUrl: provider.baseUrl,
1168
1634
  apiKey: resolveProviderApiKey(provider),
1169
1635
  defaultQuery: provider.defaultQuery,
1170
- defaultHeaders: provider.defaultHeaders
1636
+ defaultHeaders: provider.defaultHeaders,
1637
+ publicDefaultQueryNames: provider.publicDefaultQueryNames,
1638
+ publicDefaultHeaderNames: provider.publicDefaultHeaderNames,
1639
+ credentialSource: registryCredentialSource(provider),
1640
+ billing: registryBilling(provider)
1171
1641
  })
1172
1642
  );
1173
1643
  return [builtin, ...registry];
1174
1644
  }
1645
+ function withCodexCatalogProvider(settings) {
1646
+ const providers = parseModelProvidersJson(settings.modelProvidersJson);
1647
+ if (providers.some((provider2) => provider2.id === CODEX_PROVIDER_ID)) {
1648
+ return settings;
1649
+ }
1650
+ const provider = {
1651
+ kind: "codex-subscription",
1652
+ id: CODEX_PROVIDER_ID,
1653
+ label: "Codex (ChatGPT subscription)",
1654
+ api: "responses",
1655
+ baseUrl: CODEX_PROVIDER_BASE_URL,
1656
+ models: CODEX_FALLBACK_MODEL_SLUGS.map((slug) => ({
1657
+ id: `${CODEX_MODEL_ID_PREFIX}${slug}`,
1658
+ upstreamModelId: slug,
1659
+ label: slug,
1660
+ reasoningEffort: true,
1661
+ contextWindowTokens: CODEX_MODEL_CONTEXT_WINDOW_TOKENS,
1662
+ effectiveContextWindowTokens: CODEX_MODEL_EFFECTIVE_CONTEXT_WINDOW_TOKENS,
1663
+ autoCompactTokenLimit: CODEX_MODEL_AUTO_COMPACT_TOKEN_LIMIT,
1664
+ toolOutputTruncationTokens: CODEX_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS
1665
+ }))
1666
+ };
1667
+ return { ...settings, modelProvidersJson: JSON.stringify([...providers, provider]) };
1668
+ }
1175
1669
  function policyProviderIdForModel(settings, modelId) {
1176
- if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1670
+ const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
1671
+ if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
1177
1672
  return CODEX_PROVIDER_ID;
1178
1673
  }
1179
- const configured = configuredModels(settings).find((model) => model.id === modelId);
1674
+ const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
1180
1675
  return configured?.providerId ?? builtinProviderId(settings);
1181
1676
  }
1677
+ function resolvedExecutionLimits(settings, model) {
1678
+ return {
1679
+ contextWindowTokens: model.contextWindowTokens ?? settings.contextWindowTokens,
1680
+ effectiveContextWindowTokens: model.effectiveContextWindowTokens ?? settings.contextEffectiveWindowTokens ?? null,
1681
+ autoCompactTokenLimit: model.autoCompactTokenLimit ?? settings.contextAutoCompactThresholdTokens ?? null,
1682
+ toolOutputTruncationTokens: model.toolOutputTruncationTokens ?? settings.modelToolOutputTruncationTokens ?? null
1683
+ };
1684
+ }
1685
+ function finalizeConfiguredModel(settings, provider, input) {
1686
+ const modelWithoutVersion = {
1687
+ schemaVersion: 1,
1688
+ ...input,
1689
+ executionLimits: resolvedExecutionLimits(settings, input)
1690
+ };
1691
+ return {
1692
+ ...modelWithoutVersion,
1693
+ definitionVersion: definitionVersionFor(modelWithoutVersion, provider)
1694
+ };
1695
+ }
1696
+ function assertUniqueModelIdentities(models) {
1697
+ const canonicalOwners = /* @__PURE__ */ new Map();
1698
+ for (const model of models) {
1699
+ const previous = canonicalOwners.get(model.id);
1700
+ if (previous !== void 0) {
1701
+ throw new Error(
1702
+ `OPENGENI_MODEL_PROVIDERS_JSON model id ${JSON.stringify(model.id)} is declared by both ${previous} and ${model.providerId}`
1703
+ );
1704
+ }
1705
+ canonicalOwners.set(model.id, model.providerId);
1706
+ }
1707
+ const acceptedInputs = new Map(canonicalOwners);
1708
+ for (const model of models) {
1709
+ const ownAliases = /* @__PURE__ */ new Set();
1710
+ for (const alias of model.aliases) {
1711
+ if (ownAliases.has(alias)) {
1712
+ throw new Error(
1713
+ `OPENGENI_MODEL_PROVIDERS_JSON model ${JSON.stringify(model.id)} contains duplicate alias ${JSON.stringify(alias)}`
1714
+ );
1715
+ }
1716
+ ownAliases.add(alias);
1717
+ const previous = acceptedInputs.get(alias);
1718
+ if (previous !== void 0) {
1719
+ throw new Error(
1720
+ `OPENGENI_MODEL_PROVIDERS_JSON alias ${JSON.stringify(alias)} for model ${JSON.stringify(model.id)} collides with model/provider ${previous}`
1721
+ );
1722
+ }
1723
+ acceptedInputs.set(alias, model.id);
1724
+ }
1725
+ }
1726
+ }
1182
1727
  function configuredModels(settings) {
1183
1728
  const builtinId = builtinProviderId(settings);
1184
1729
  const builtinLabel = builtinProviderLabel(settings);
1730
+ const providers = configuredProviders(settings);
1731
+ const providerById = new Map(providers.map((provider) => [provider.id, provider]));
1732
+ const pricingSchedules = configuredModelPricingSchedules(settings);
1733
+ const parsedRegistry = parseModelProvidersJson(settings.modelProvidersJson);
1185
1734
  const registryOwnedIds = new Set(
1186
- parseModelProvidersJson(settings.modelProvidersJson).flatMap(
1187
- (provider) => provider.models.map((model) => model.id)
1188
- )
1735
+ parsedRegistry.flatMap((provider) => provider.models.map((model) => model.id))
1189
1736
  );
1190
- const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
1737
+ const registryAliases = new Set(
1738
+ parsedRegistry.flatMap((provider) => provider.models.flatMap((model) => model.aliases ?? []))
1739
+ );
1740
+ const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || registryAliases.has(id) || id.includes("/") && registryOwnedIds.has(id);
1741
+ const builtinProvider = providerById.get(builtinId);
1742
+ if (!builtinProvider) {
1743
+ throw new Error(`Built-in model provider ${builtinId} is not configured`);
1744
+ }
1191
1745
  const out = uniqueValues([
1192
1746
  settings.openaiModel,
1193
1747
  ...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)) {
1748
+ ]).filter((id) => !isRegistryNamespaced(id)).map((id) => {
1749
+ const capabilities = legacyModelCapabilities(settings, {
1750
+ reasoningEffort: true,
1751
+ hostedWebSearch: settings.webSearchEnabled
1752
+ });
1753
+ return finalizeConfiguredModel(settings, builtinProvider, {
1754
+ id,
1755
+ aliases: [],
1756
+ label: id,
1757
+ providerId: builtinId,
1758
+ providerLabel: builtinLabel,
1759
+ api: "responses",
1760
+ upstreamModelId: id,
1761
+ deployment: { upstreamModelId: id, wireApi: "responses" },
1762
+ credentialSource: builtinProvider.credentialSource,
1763
+ billing: builtinProvider.billing,
1764
+ capabilities,
1765
+ ...pricingSchedules[id] === void 0 ? {} : { pricing: pricingSchedules[id] },
1766
+ contextWindowTokens: settings.contextWindowTokens,
1767
+ toolOutputTruncationTokens: settings.modelToolOutputTruncationTokens,
1768
+ reasoningEffort: capabilities.reasoning.runnable,
1769
+ hostedWebSearch: capabilities.hostedTools.webSearch.runnable
1770
+ });
1771
+ });
1772
+ for (const provider of parsedRegistry) {
1206
1773
  const providerLabel = provider.label ?? provider.id;
1774
+ const resolvedProvider = providerById.get(provider.id);
1775
+ if (!resolvedProvider) {
1776
+ throw new Error(`Registry model provider ${provider.id} is not configured`);
1777
+ }
1207
1778
  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 },
1779
+ const capabilities = model.capabilities ? normalizeCapabilities(model.capabilities) : legacyModelCapabilities(settings, {
1218
1780
  reasoningEffort: model.reasoningEffort ?? false,
1219
1781
  hostedWebSearch: model.hostedWebSearch ?? false
1220
1782
  });
1783
+ const upstreamModelId = model.upstreamModelId ?? model.id;
1784
+ out.push(
1785
+ finalizeConfiguredModel(settings, resolvedProvider, {
1786
+ id: model.id,
1787
+ aliases: [...model.aliases ?? []],
1788
+ label: model.label ?? model.id,
1789
+ providerId: provider.id,
1790
+ providerLabel,
1791
+ api: provider.api,
1792
+ upstreamModelId,
1793
+ deployment: { upstreamModelId, wireApi: provider.api },
1794
+ credentialSource: resolvedProvider.credentialSource,
1795
+ billing: resolvedProvider.billing,
1796
+ capabilities,
1797
+ ...pricingSchedules[model.id] === void 0 ? {} : { pricing: pricingSchedules[model.id] },
1798
+ ...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
1799
+ ...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
1800
+ ...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
1801
+ ...model.toolOutputTruncationTokens === void 0 ? {} : { toolOutputTruncationTokens: model.toolOutputTruncationTokens },
1802
+ reasoningEffort: capabilities.reasoning.runnable,
1803
+ hostedWebSearch: capabilities.hostedTools.webSearch.runnable
1804
+ })
1805
+ );
1221
1806
  }
1222
1807
  }
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
- });
1808
+ assertUniqueModelIdentities(out);
1809
+ return out;
1810
+ }
1811
+ function canonicalizeConfiguredModelId(settings, modelId) {
1812
+ const models = configuredModels(settings);
1813
+ const canonical = models.find((model) => model.id === modelId);
1814
+ if (canonical) {
1815
+ return canonical.id;
1816
+ }
1817
+ return models.find((model) => model.aliases.includes(modelId))?.id ?? modelId;
1231
1818
  }
1232
1819
  function configuredAllowedModels(settings) {
1233
1820
  return configuredModels(settings).map((model) => model.id);
1234
1821
  }
1235
1822
  function resolveModelProvider(settings, modelId) {
1236
- const model = configuredModels(settings).find((candidate) => candidate.id === modelId);
1823
+ const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
1824
+ const model = configuredModels(settings).find((candidate) => candidate.id === canonicalModelId);
1237
1825
  if (!model) {
1238
1826
  return void 0;
1239
1827
  }
@@ -1245,22 +1833,97 @@ function resolveModelProvider(settings, modelId) {
1245
1833
  }
1246
1834
  return { provider, model };
1247
1835
  }
1248
- function configuredModelPricing(settings) {
1836
+ function settingsForTurnExecutionPolicy(settings, modelId) {
1837
+ return settings.codexSubscriptionEnabled && modelId.startsWith(CODEX_MODEL_ID_PREFIX) ? withCodexCatalogProvider(settings) : settings;
1838
+ }
1839
+ function resolveTurnExecutionPolicyV1(settings, input) {
1840
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, input.modelId);
1841
+ const productModelId = canonicalizeConfiguredModelId(catalogSettings, input.modelId);
1842
+ const resolved = resolveModelProvider(catalogSettings, productModelId);
1843
+ if (!resolved) {
1844
+ throw new Error("Turn execution policy model is not present in the configured catalog");
1845
+ }
1846
+ if (input.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, input.requestedModelId) !== productModelId) {
1847
+ throw new Error("Turn execution policy requested model does not canonicalize to its product");
1848
+ }
1849
+ return TurnExecutionPolicyV1.parse({
1850
+ schemaVersion: 1,
1851
+ productModelId,
1852
+ requestedModelId: input.requestedModelId,
1853
+ modelSource: input.modelSource,
1854
+ reasoningEffort: input.reasoningEffort,
1855
+ reasoningSource: input.reasoningSource,
1856
+ providerId: resolved.provider.id,
1857
+ upstreamModelId: resolved.model.upstreamModelId,
1858
+ wireApi: resolved.model.api,
1859
+ credentialSource: resolved.model.credentialSource,
1860
+ billing: resolved.model.billing,
1861
+ definitionVersion: resolved.model.definitionVersion
1862
+ });
1863
+ }
1864
+ function assertTurnExecutionPolicyMatchesConfigV1(settings, policy, expected) {
1865
+ const parsed = TurnExecutionPolicyV1.parse(policy);
1866
+ const catalogSettings = settingsForTurnExecutionPolicy(settings, parsed.productModelId);
1867
+ const canonicalExpectedModel = canonicalizeConfiguredModelId(catalogSettings, expected.modelId);
1868
+ if (parsed.productModelId !== canonicalExpectedModel || parsed.reasoningEffort !== expected.reasoningEffort) {
1869
+ throw new Error("Turn execution policy does not match the accepted turn model/reasoning");
1870
+ }
1871
+ if (parsed.requestedModelId !== null && canonicalizeConfiguredModelId(catalogSettings, parsed.requestedModelId) !== parsed.productModelId) {
1872
+ throw new Error("Turn execution policy requested model does not match its product model");
1873
+ }
1874
+ const resolved = resolveModelProvider(catalogSettings, parsed.productModelId);
1875
+ if (!resolved) {
1876
+ throw new Error("Turn execution policy model is no longer configured");
1877
+ }
1878
+ 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);
1879
+ if (mismatched) {
1880
+ throw new Error("Turn execution policy does not match the current provider definition");
1881
+ }
1882
+ return { policy: parsed, provider: resolved.provider, model: resolved.model };
1883
+ }
1884
+ function configuredModelPricingSchedules(settings) {
1885
+ const defaults = Object.fromEntries(
1886
+ Object.entries(defaultModelPricing).map(([model, pricing]) => [model, { default: pricing }])
1887
+ );
1249
1888
  const registry = {};
1250
1889
  for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
1251
1890
  for (const model of provider.models) {
1252
1891
  if (model.pricing) {
1253
- registry[model.id] = model.pricing;
1892
+ registry[model.id] = normalizeModelPricingSchedule(model.pricing);
1254
1893
  }
1255
1894
  }
1256
1895
  }
1257
- const configured = parseModelPricingJson(settings.modelPricingJson);
1896
+ const configured = Object.fromEntries(
1897
+ Object.entries(parseModelPricingJson(settings.modelPricingJson)).map(([model, pricing]) => [
1898
+ model,
1899
+ { default: pricing }
1900
+ ])
1901
+ );
1258
1902
  return {
1259
- ...defaultModelPricing,
1903
+ ...defaults,
1260
1904
  ...registry,
1261
1905
  ...configured
1262
1906
  };
1263
1907
  }
1908
+ function configuredModelPricing(settings) {
1909
+ return Object.fromEntries(
1910
+ Object.entries(configuredModelPricingSchedules(settings)).map(([model, schedule]) => [
1911
+ model,
1912
+ schedule.default
1913
+ ])
1914
+ );
1915
+ }
1916
+ function selectModelPricing(schedule, inputTokens) {
1917
+ const normalizedInputTokens = Math.max(0, Math.floor(inputTokens));
1918
+ let selected = schedule.default;
1919
+ for (const tier of schedule.inputTokenTiers ?? []) {
1920
+ if (normalizedInputTokens < tier.minimumInputTokens) {
1921
+ break;
1922
+ }
1923
+ selected = tier.pricing;
1924
+ }
1925
+ return selected;
1926
+ }
1264
1927
  function contextInputBudgetTokens(settings) {
1265
1928
  if (settings.contextEffectiveWindowTokens !== void 0) {
1266
1929
  return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
@@ -1303,14 +1966,25 @@ function configuredEntitlements(settings) {
1303
1966
  };
1304
1967
  }
1305
1968
  function calculateModelUsageCostMicros(settings, model, usage) {
1306
- const pricing = configuredModelPricing(settings)[model];
1307
- if (!pricing) {
1969
+ const schedule = configuredModelPricingSchedules(settings)[model];
1970
+ if (!schedule) {
1308
1971
  throw new Error(`Missing model pricing for ${model}`);
1309
1972
  }
1310
1973
  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);
1974
+ const rawCostByPricing = /* @__PURE__ */ new Map();
1975
+ for (const entry of entries) {
1976
+ const pricing = selectModelPricing(schedule, positiveInt(entry.inputTokens));
1977
+ rawCostByPricing.set(
1978
+ pricing,
1979
+ (rawCostByPricing.get(pricing) ?? 0) + calculateEntryCostMicros(pricing, entry)
1980
+ );
1981
+ }
1982
+ let total = 0;
1983
+ for (const [pricing, rawCost] of rawCostByPricing) {
1984
+ const marginBps = pricing.marginBps ?? 0;
1985
+ total += Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
1986
+ }
1987
+ return total;
1314
1988
  }
1315
1989
  function configuredAllowedReasoningEfforts(settings) {
1316
1990
  return uniqueValues([
@@ -1634,7 +2308,14 @@ function parseModelProvidersJson(raw) {
1634
2308
  `OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
1635
2309
  );
1636
2310
  }
1637
- return result.data;
2311
+ try {
2312
+ return normalizeRegistryProvider(result.data);
2313
+ } catch (error) {
2314
+ const message = error instanceof Error ? error.message : String(error);
2315
+ throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${message}`, {
2316
+ cause: error
2317
+ });
2318
+ }
1638
2319
  });
1639
2320
  }
1640
2321
  function parseIntegrationsOauthClientsJson(raw) {
@@ -2040,6 +2721,7 @@ function validateSettings(settings) {
2040
2721
  );
2041
2722
  }
2042
2723
  }
2724
+ configuredModels(settings);
2043
2725
  }
2044
2726
  function resolveStreamTokenSecret(settings) {
2045
2727
  const explicit = settings.streamTokenSecret?.trim();
@@ -2125,21 +2807,27 @@ function delay(ms) {
2125
2807
  }
2126
2808
  export {
2127
2809
  AGENT_INSTRUCTIONS_CORE_PLACEHOLDER,
2810
+ CapabilityStateV1Schema,
2811
+ CapabilitySupportV1,
2128
2812
  DEFAULT_AGENT_INSTRUCTIONS,
2129
2813
  IntegrationOAuthClientConfigSchema,
2130
2814
  McpServerConnectionRefSchema,
2815
+ ModelCapabilitiesV1Schema,
2131
2816
  ModelProviderApi,
2132
2817
  RegistryProviderKind,
2133
2818
  SANDBOX_REQUIRED_ENV,
2134
2819
  applyGitAuthPointerEnvironment,
2820
+ assertTurnExecutionPolicyMatchesConfigV1,
2135
2821
  builtinProviderId,
2136
2822
  calculateModelUsageCostMicros,
2823
+ canonicalizeConfiguredModelId,
2137
2824
  collectGitIdentityEnvironment,
2138
2825
  collectSandboxEnvironment,
2139
2826
  configuredAllowedModels,
2140
2827
  configuredAllowedReasoningEfforts,
2141
2828
  configuredEntitlements,
2142
2829
  configuredModelPricing,
2830
+ configuredModelPricingSchedules,
2143
2831
  configuredModels,
2144
2832
  configuredProviders,
2145
2833
  configuredStaticUsageLimits,
@@ -2170,15 +2858,18 @@ export {
2170
2858
  resolveProviderApiKey,
2171
2859
  resolveRelayTokenSecret,
2172
2860
  resolveStreamTokenSecret,
2861
+ resolveTurnExecutionPolicyV1,
2173
2862
  retryStartupDependency,
2174
2863
  sandboxEnvironmentVariableNames,
2175
2864
  sandboxLifecycleHookIds,
2176
2865
  sandboxPreparationProfiles,
2177
2866
  sandboxWarmRateMicrosPerSecond,
2867
+ selectModelPricing,
2178
2868
  settingsWithResolvedModelContext,
2179
2869
  stableSandboxEnvironmentForRun,
2180
2870
  startupRetryOptions,
2181
2871
  streamTokenDegraded,
2182
- temporalConnectionOptions
2872
+ temporalConnectionOptions,
2873
+ withCodexCatalogProvider
2183
2874
  };
2184
2875
  //# sourceMappingURL=index.js.map