@opengeni/config 0.5.1 → 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.d.ts +422 -15
- package/dist/index.js +877 -80
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1187 -102
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 {
|
|
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_-]+$/;
|
|
@@ -63,7 +75,7 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
63
75
|
"You are an OpenGeni workspace agent.",
|
|
64
76
|
"Follow the user's task and any enabled pack or skill instructions for the current role.",
|
|
65
77
|
"Work inside the sandbox workspace and use filesystem and shell tools when useful.",
|
|
66
|
-
"Repository resources are mounted under repos/<owner>/<repo
|
|
78
|
+
"Repository resources are mounted under repos/<host>/<owner>/<repo> unless the session specifies another collision-free mount path.",
|
|
67
79
|
"File resources are mounted under files/<file-id>/ unless the session specifies another mount path.",
|
|
68
80
|
"Attached files are mounted read-only; copy them before modifying.",
|
|
69
81
|
"Bundled skills are under .agents/ and can include infrastructure, marketing, or other role-specific guidance.",
|
|
@@ -74,13 +86,50 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
74
86
|
AGENT_INSTRUCTIONS_CORE_PLACEHOLDER
|
|
75
87
|
].join(" ");
|
|
76
88
|
var McpServerConnectionRefSchema = z.object({
|
|
77
|
-
|
|
89
|
+
// Standalone ids are UUIDs; embedded hosts may use any stable opaque id.
|
|
90
|
+
connectionId: z.string().min(1).optional(),
|
|
91
|
+
provider: z.string().min(1).max(128).optional(),
|
|
78
92
|
providerDomain: z.string().min(1),
|
|
79
93
|
kind: z.enum(["oauth2", "api_key", "app_install", "delegated"]).optional(),
|
|
80
94
|
scopes: z.array(z.string().min(1)).optional(),
|
|
81
95
|
resource: z.string().min(1).optional(),
|
|
96
|
+
selectedResources: z.array(
|
|
97
|
+
z.object({
|
|
98
|
+
id: z.string().min(1).max(512),
|
|
99
|
+
kind: z.literal("repository")
|
|
100
|
+
}).strict()
|
|
101
|
+
).min(1).max(256).superRefine((resources, context) => {
|
|
102
|
+
const seen = /* @__PURE__ */ new Set();
|
|
103
|
+
for (const [index, resource] of resources.entries()) {
|
|
104
|
+
const key = `${resource.kind}\0${resource.id}`;
|
|
105
|
+
if (seen.has(key)) {
|
|
106
|
+
context.addIssue({
|
|
107
|
+
code: "custom",
|
|
108
|
+
message: "selectedResources must not contain duplicates",
|
|
109
|
+
path: [index]
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
seen.add(key);
|
|
113
|
+
}
|
|
114
|
+
}).optional(),
|
|
82
115
|
subjectScope: z.enum(["workspace", "subject"]).optional()
|
|
83
|
-
}).strict()
|
|
116
|
+
}).strict().superRefine((reference, context) => {
|
|
117
|
+
if (!reference.selectedResources) return;
|
|
118
|
+
if (!reference.connectionId) {
|
|
119
|
+
context.addIssue({
|
|
120
|
+
code: "custom",
|
|
121
|
+
message: "selectedResources requires connectionId",
|
|
122
|
+
path: ["connectionId"]
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (!reference.provider) {
|
|
126
|
+
context.addIssue({
|
|
127
|
+
code: "custom",
|
|
128
|
+
message: "selectedResources requires provider",
|
|
129
|
+
path: ["provider"]
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
});
|
|
84
133
|
var SettingsSchema = z.object({
|
|
85
134
|
serviceName: z.string().default("opengeni"),
|
|
86
135
|
environment: z.string().default("local"),
|
|
@@ -93,7 +142,7 @@ var SettingsSchema = z.object({
|
|
|
93
142
|
// topology. Default "" → standalone: no search_path scoping, server default
|
|
94
143
|
// (`public`). When set (e.g. "opengeni"), the db handle + the managed-auth
|
|
95
144
|
// pool send `search_path = "<dbSchema>","opengeni_private","public"` so every
|
|
96
|
-
// query resolves into the dedicated schema with NO query rewrite (
|
|
145
|
+
// query resolves into the dedicated schema with NO query rewrite (schema-isolation contract F1).
|
|
97
146
|
dbSchema: z.string().default(""),
|
|
98
147
|
// Step I (§7.7). RLS posture. "force" (default) = today's FORCE-RLS via the
|
|
99
148
|
// non-owner `opengeni_app` role. "scoped" = the embedded owner-role path (the
|
|
@@ -103,6 +152,12 @@ var SettingsSchema = z.object({
|
|
|
103
152
|
temporalHost: z.string().default("127.0.0.1:7233"),
|
|
104
153
|
temporalNamespace: z.string().default("default"),
|
|
105
154
|
temporalTaskQueue: z.string().default("opengeni-runs-ts"),
|
|
155
|
+
temporalTlsEnabled: EnvBoolean.default(false),
|
|
156
|
+
temporalApiKey: z.string().optional(),
|
|
157
|
+
temporalTlsServerName: z.string().optional(),
|
|
158
|
+
temporalTlsRootCaCertificateBase64: z.string().optional(),
|
|
159
|
+
temporalTlsClientCertificateBase64: z.string().optional(),
|
|
160
|
+
temporalTlsClientPrivateKeyBase64: z.string().optional(),
|
|
106
161
|
startupDependencyRetryAttempts: z.coerce.number().int().positive().default(30),
|
|
107
162
|
startupDependencyRetryInitialDelayMs: z.coerce.number().int().positive().default(1e3),
|
|
108
163
|
startupDependencyRetryMaxDelayMs: z.coerce.number().int().positive().default(5e3),
|
|
@@ -122,18 +177,25 @@ var SettingsSchema = z.object({
|
|
|
122
177
|
staticEntitlementsJson: z.string().default("{}"),
|
|
123
178
|
staticUsageLimitsJson: z.string().default("{}"),
|
|
124
179
|
delegationSecret: z.string().optional(),
|
|
125
|
-
//
|
|
180
|
+
// sandbox workspace scoped stream-token HMAC secret (sandbox contract §C.3 / stream-token availability contract).
|
|
126
181
|
// When unset, the API falls back to `delegationSecret` (the same HMAC envelope
|
|
127
182
|
// family, `ogs_` vs `ogd_` prefix). REQUIRED-WHEN-DESKTOP, but the absence of
|
|
128
183
|
// BOTH while sandboxDesktopEnabled=true is a GRACEFUL DEGRADE (DesktopStream
|
|
129
|
-
// transport:null + a loud boot warning), NOT a hard boot-fail (
|
|
184
|
+
// transport:null + a loud boot warning), NOT a hard boot-fail (stream-token availability contract).
|
|
130
185
|
streamTokenSecret: z.string().optional(),
|
|
131
186
|
// The desktop input plane (raw stream:control writes) is OFF in v1: even a
|
|
132
187
|
// holder of stream:control gets 403 until this flips. Keeps stream:control a
|
|
133
188
|
// declared-but-inert permission so later hardening is a flag flip.
|
|
134
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),
|
|
135
193
|
toolspaceEnabled: EnvBoolean.default(false),
|
|
136
194
|
toolspaceMaxCallsPerTurn: z.coerce.number().int().positive().default(200),
|
|
195
|
+
// Optional release-coherent bootstrap hint for custom rigs/connected machines
|
|
196
|
+
// that do not carry the stock-image ogtool binary. Exact stable versions only:
|
|
197
|
+
// the agent must never guess a tag or silently install `latest`.
|
|
198
|
+
ogtoolPackageSpec: z.string().regex(/^@opengeni\/ogtool@(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u).optional(),
|
|
137
199
|
environmentsEncryptionKey: z.string().optional(),
|
|
138
200
|
integrationsEnabled: EnvBoolean.default(false),
|
|
139
201
|
integrationsStateSecret: z.string().optional(),
|
|
@@ -178,6 +240,10 @@ var SettingsSchema = z.object({
|
|
|
178
240
|
// Model-catalog auto-compact limit. When present it is clamped to
|
|
179
241
|
// 90% of the raw window, matching Codex core's auto_compact_token_limit().
|
|
180
242
|
contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
|
|
243
|
+
// Provider-neutral fallback for canonical model-facing tool-result text.
|
|
244
|
+
// The current stable Codex catalog policy is 10k tokens; the truncator adds
|
|
245
|
+
// Codex's 1.2x JSON serialization allowance when applying it.
|
|
246
|
+
modelToolOutputTruncationTokens: z.coerce.number().int().positive().default(1e4),
|
|
181
247
|
authRequired: EnvBoolean.default(false),
|
|
182
248
|
accessKey: z.string().optional(),
|
|
183
249
|
authAllowHealth: EnvBoolean.default(true),
|
|
@@ -213,15 +279,11 @@ var SettingsSchema = z.object({
|
|
|
213
279
|
// tool that BM25-discloses only the matching connectors. Default OFF — a codex
|
|
214
280
|
// turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
215
281
|
codexToolSearchEnabled: EnvBoolean.default(false),
|
|
216
|
-
//
|
|
282
|
+
// credential allocator atomic, workspace-local credential allocation. Default OFF is a
|
|
217
283
|
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
218
284
|
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
219
285
|
// rollback; the additive lease table/cursor columns become inert.
|
|
220
286
|
codexCredentialLeasingEnabled: EnvBoolean.default(false),
|
|
221
|
-
// Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
|
|
222
|
-
// rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
|
|
223
|
-
// match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
|
|
224
|
-
codexRotationNearExhaustionPct: z.coerce.number().int().min(1).max(100).default(90),
|
|
225
287
|
openaiReasoningEffort: ReasoningEffort.default("low"),
|
|
226
288
|
openaiAllowedReasoningEfforts: z.string().default("low,medium,high,xhigh"),
|
|
227
289
|
openaiResponsesTransport: z.enum(["http", "websocket"]).default("http"),
|
|
@@ -360,7 +422,7 @@ var SettingsSchema = z.object({
|
|
|
360
422
|
// recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
|
|
361
423
|
recordingEnabled: EnvBoolean.default(true),
|
|
362
424
|
recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
|
|
363
|
-
// Workbench v2 turn-end workspace capture
|
|
425
|
+
// Workbench v2 turn-end workspace capture. When on, the turn
|
|
364
426
|
// activity probes the box's changed files off the live box at turn end and
|
|
365
427
|
// persists a capture revision (blobs in @opengeni/storage) so the workbench
|
|
366
428
|
// paints cold/offline sessions with zero machine round-trips. Best-effort and
|
|
@@ -423,6 +485,15 @@ var SettingsSchema = z.object({
|
|
|
423
485
|
// EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
|
|
424
486
|
// turn the flag ON the moment anyone set the env var to disable it).
|
|
425
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),
|
|
426
497
|
// --- lazy sandbox provisioning rollout flag, default OFF ---
|
|
427
498
|
// Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
|
|
428
499
|
// property of the owned path — the SDK never creates/resumes an injected session,
|
|
@@ -442,7 +513,7 @@ var SettingsSchema = z.object({
|
|
|
442
513
|
// 404 (invisible — the surface does not exist for this deployment) and the
|
|
443
514
|
// selfhosted backend is inert; boot is unaffected. EnvBoolean (NOT
|
|
444
515
|
// z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
|
|
445
|
-
// the deploy-staging IaC secret/configmap pattern
|
|
516
|
+
// the deploy-staging IaC secret/configmap pattern.
|
|
446
517
|
sandboxSelfhostedEnabled: EnvBoolean.default(false),
|
|
447
518
|
// Gates the op-stream (streaming exec) transport to Connected Machines. The
|
|
448
519
|
// runner must ALSO advertise Capabilities.op_stream; default off, and legacy
|
|
@@ -462,7 +533,7 @@ var SettingsSchema = z.object({
|
|
|
462
533
|
selfhostedNatsUrl: z.string().optional(),
|
|
463
534
|
selfhostedRelayUrl: z.string().optional(),
|
|
464
535
|
// The HMAC secret the control plane signs the agent's relay PRODUCER token with
|
|
465
|
-
// (the `ogr_` envelope threaded into EnrollmentCredentials.relayToken; M8b/
|
|
536
|
+
// (the `ogr_` envelope threaded into EnrollmentCredentials.relayToken; M8b/design
|
|
466
537
|
// §10.5). The relay verifies the producer token with the SAME secret. Optional:
|
|
467
538
|
// when ABSENT the poll returns an empty relayToken (graceful degrade — the stream
|
|
468
539
|
// plane is simply unavailable until configured). Falls back to streamTokenSecret /
|
|
@@ -472,7 +543,7 @@ var SettingsSchema = z.object({
|
|
|
472
543
|
// The minisign PUBLIC key the agent pins for self-update verification (handed to
|
|
473
544
|
// the agent in EnrollmentCredentials; the SECRET key lives only in CI).
|
|
474
545
|
agentUpdatePublicKey: z.string().optional(),
|
|
475
|
-
// --- NATS auth-callout tenancy boundary (bring-your-own-compute M-AUTH;
|
|
546
|
+
// --- NATS auth-callout tenancy boundary (bring-your-own-compute M-AUTH; design
|
|
476
547
|
// §10.1 NATS Accounts per workspace + §17 the isolation smoke) -------------
|
|
477
548
|
// nats-server is configured with AUTH CALLOUT: an external agent connects
|
|
478
549
|
// presenting its `oge_` enrollment bearer as the connect auth-token; the server
|
|
@@ -640,14 +711,8 @@ var SettingsSchema = z.object({
|
|
|
640
711
|
allowedTools: z.array(z.string().min(1)).optional(),
|
|
641
712
|
timeoutMs: z.number().int().positive().optional(),
|
|
642
713
|
cacheToolsList: z.boolean().default(false),
|
|
643
|
-
/**
|
|
644
|
-
|
|
645
|
-
* session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
|
|
646
|
-
* tools require approval; a string[] = only the listed UNPREFIXED tool
|
|
647
|
-
* names do; absent = auto-run (the historical default). Enforced in the
|
|
648
|
-
* runtime by attaching `needsApproval` to the matching MCP tools.
|
|
649
|
-
*/
|
|
650
|
-
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(),
|
|
651
716
|
/**
|
|
652
717
|
* Extra request headers sent to this MCP server (credential injection
|
|
653
718
|
* for workspace-enabled capability MCPs). Populated at runtime from
|
|
@@ -665,21 +730,163 @@ var ModelPricingSchema = z.object({
|
|
|
665
730
|
outputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
666
731
|
marginBps: z.number().int().min(0).max(1e5).optional()
|
|
667
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
|
+
});
|
|
668
848
|
var ModelProviderApi = z.enum(["responses", "chat"]);
|
|
669
849
|
var RegistryProviderKind = z.enum(["api-key", "codex-subscription"]);
|
|
670
850
|
var RegistryModelSchema = z.object({
|
|
671
851
|
id: z.string().min(1),
|
|
672
|
-
//
|
|
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
|
|
673
857
|
label: z.string().min(1).optional(),
|
|
674
858
|
// display name; defaults to id
|
|
675
859
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
676
860
|
effectiveContextWindowTokens: z.number().int().positive().optional(),
|
|
677
861
|
autoCompactTokenLimit: z.number().int().positive().optional(),
|
|
862
|
+
// Canonical model-facing function/tool-result policy. The runtime applies
|
|
863
|
+
// the same 1.2x serialization allowance as Codex when materializing output.
|
|
864
|
+
toolOutputTruncationTokens: z.number().int().positive().optional(),
|
|
678
865
|
reasoningEffort: z.boolean().optional(),
|
|
679
|
-
//
|
|
866
|
+
// legacy compatibility input/projection
|
|
680
867
|
hostedWebSearch: z.boolean().optional(),
|
|
681
|
-
//
|
|
682
|
-
|
|
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
|
+
}
|
|
683
890
|
});
|
|
684
891
|
var RegistryProviderSchema = z.object({
|
|
685
892
|
kind: RegistryProviderKind.default("api-key"),
|
|
@@ -695,6 +902,12 @@ var RegistryProviderSchema = z.object({
|
|
|
695
902
|
// ... OR name of the env var holding the key (preferred)
|
|
696
903
|
defaultQuery: z.record(z.string(), z.string()).optional(),
|
|
697
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(),
|
|
698
911
|
models: z.array(RegistryModelSchema).min(1)
|
|
699
912
|
});
|
|
700
913
|
var IntegrationOAuthClientConfigSchema = z.object({
|
|
@@ -831,6 +1044,14 @@ function getSettings() {
|
|
|
831
1044
|
temporalHost: optional("OPENGENI_TEMPORAL_HOST"),
|
|
832
1045
|
temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
|
|
833
1046
|
temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
|
|
1047
|
+
temporalTlsEnabled: optional("OPENGENI_TEMPORAL_TLS_ENABLED"),
|
|
1048
|
+
temporalApiKey: optional("OPENGENI_TEMPORAL_API_KEY"),
|
|
1049
|
+
temporalTlsServerName: optional("OPENGENI_TEMPORAL_TLS_SERVER_NAME"),
|
|
1050
|
+
temporalTlsRootCaCertificateBase64: optional(
|
|
1051
|
+
"OPENGENI_TEMPORAL_TLS_ROOT_CA_CERTIFICATE_BASE64"
|
|
1052
|
+
),
|
|
1053
|
+
temporalTlsClientCertificateBase64: optional("OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64"),
|
|
1054
|
+
temporalTlsClientPrivateKeyBase64: optional("OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64"),
|
|
834
1055
|
startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
|
|
835
1056
|
startupDependencyRetryInitialDelayMs: optional(
|
|
836
1057
|
"OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
|
|
@@ -851,8 +1072,10 @@ function getSettings() {
|
|
|
851
1072
|
delegationSecret: optional("OPENGENI_DELEGATION_SECRET"),
|
|
852
1073
|
streamTokenSecret: optional("OPENGENI_STREAM_TOKEN_SECRET"),
|
|
853
1074
|
streamControlEnabled: optional("OPENGENI_STREAM_CONTROL_ENABLED"),
|
|
1075
|
+
sessionTurnToolReplacementEnabled: optional("OPENGENI_SESSION_TURN_TOOL_REPLACEMENT_ENABLED"),
|
|
854
1076
|
toolspaceEnabled: optional("OPENGENI_TOOLSPACE_ENABLED"),
|
|
855
1077
|
toolspaceMaxCallsPerTurn: optional("OPENGENI_TOOLSPACE_MAX_CALLS_PER_TURN"),
|
|
1078
|
+
ogtoolPackageSpec: optional("OPENGENI_OGTOOL_PACKAGE_SPEC"),
|
|
856
1079
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
857
1080
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
858
1081
|
integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
|
|
@@ -868,6 +1091,7 @@ function getSettings() {
|
|
|
868
1091
|
contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
|
|
869
1092
|
contextReservedOutputTokens: optional("OPENGENI_CONTEXT_RESERVED_OUTPUT_TOKENS"),
|
|
870
1093
|
contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
|
|
1094
|
+
modelToolOutputTruncationTokens: optional("OPENGENI_MODEL_TOOL_OUTPUT_TRUNCATION_TOKENS"),
|
|
871
1095
|
authRequired: optional("OPENGENI_AUTH_REQUIRED"),
|
|
872
1096
|
accessKey: optional("OPENGENI_ACCESS_KEY"),
|
|
873
1097
|
authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
|
|
@@ -888,7 +1112,6 @@ function getSettings() {
|
|
|
888
1112
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
889
1113
|
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
890
1114
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
891
|
-
codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
|
|
892
1115
|
openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
|
|
893
1116
|
openaiAllowedReasoningEfforts: optional("OPENGENI_OPENAI_ALLOWED_REASONING_EFFORTS"),
|
|
894
1117
|
openaiResponsesTransport: optional("OPENGENI_OPENAI_RESPONSES_TRANSPORT"),
|
|
@@ -966,6 +1189,9 @@ function getSettings() {
|
|
|
966
1189
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
967
1190
|
vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
|
|
968
1191
|
sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
|
|
1192
|
+
rigVerificationLeaseOwnershipEnabled: optional(
|
|
1193
|
+
"OPENGENI_RIG_VERIFICATION_LEASE_OWNERSHIP_ENABLED"
|
|
1194
|
+
),
|
|
969
1195
|
sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
|
|
970
1196
|
sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
|
|
971
1197
|
agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
|
|
@@ -1077,6 +1303,287 @@ function resolveProviderApiKey(provider, source = process.env) {
|
|
|
1077
1303
|
}
|
|
1078
1304
|
return void 0;
|
|
1079
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
|
+
}
|
|
1080
1587
|
function builtinProviderId(settings) {
|
|
1081
1588
|
return settings.openaiProvider === "azure" ? "azure" : "openai";
|
|
1082
1589
|
}
|
|
@@ -1084,18 +1591,22 @@ function builtinProviderLabel(settings) {
|
|
|
1084
1591
|
return settings.openaiProvider === "azure" ? "Azure OpenAI" : "OpenAI";
|
|
1085
1592
|
}
|
|
1086
1593
|
function configuredProviders(settings) {
|
|
1594
|
+
const credentialSource = builtinCredentialSource(settings);
|
|
1087
1595
|
const builtin = {
|
|
1088
1596
|
id: builtinProviderId(settings),
|
|
1089
1597
|
label: builtinProviderLabel(settings),
|
|
1090
1598
|
kind: "api-key",
|
|
1091
1599
|
api: "responses",
|
|
1092
|
-
builtin: true
|
|
1600
|
+
builtin: true,
|
|
1601
|
+
credentialSource,
|
|
1602
|
+
billing: { upstreamPayer: "deployment", metering: "opengeni_credits" }
|
|
1093
1603
|
};
|
|
1094
1604
|
if (settings.openaiProvider === "azure") {
|
|
1095
|
-
|
|
1605
|
+
const baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
|
|
1606
|
+
builtin.baseUrl = baseUrl ? normalizeRegistryBaseUrl(baseUrl, builtin.id) : void 0;
|
|
1096
1607
|
builtin.apiKey = settings.azureOpenaiApiKey ?? settings.azureOpenaiAdToken;
|
|
1097
1608
|
} else {
|
|
1098
|
-
builtin.baseUrl = settings.openaiBaseUrl;
|
|
1609
|
+
builtin.baseUrl = settings.openaiBaseUrl ? normalizeRegistryBaseUrl(settings.openaiBaseUrl, builtin.id) : void 0;
|
|
1099
1610
|
builtin.apiKey = settings.openaiApiKey;
|
|
1100
1611
|
}
|
|
1101
1612
|
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
@@ -1108,71 +1619,195 @@ function configuredProviders(settings) {
|
|
|
1108
1619
|
baseUrl: provider.baseUrl,
|
|
1109
1620
|
apiKey: resolveProviderApiKey(provider),
|
|
1110
1621
|
defaultQuery: provider.defaultQuery,
|
|
1111
|
-
defaultHeaders: provider.defaultHeaders
|
|
1622
|
+
defaultHeaders: provider.defaultHeaders,
|
|
1623
|
+
publicDefaultQueryNames: provider.publicDefaultQueryNames,
|
|
1624
|
+
publicDefaultHeaderNames: provider.publicDefaultHeaderNames,
|
|
1625
|
+
credentialSource: registryCredentialSource(provider),
|
|
1626
|
+
billing: registryBilling(provider)
|
|
1112
1627
|
})
|
|
1113
1628
|
);
|
|
1114
1629
|
return [builtin, ...registry];
|
|
1115
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
|
+
}
|
|
1116
1655
|
function policyProviderIdForModel(settings, modelId) {
|
|
1117
|
-
|
|
1656
|
+
const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
|
|
1657
|
+
if (canonicalModelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
1118
1658
|
return CODEX_PROVIDER_ID;
|
|
1119
1659
|
}
|
|
1120
|
-
const configured = configuredModels(settings).find((model) => model.id ===
|
|
1660
|
+
const configured = configuredModels(settings).find((model) => model.id === canonicalModelId);
|
|
1121
1661
|
return configured?.providerId ?? builtinProviderId(settings);
|
|
1122
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
|
+
}
|
|
1123
1713
|
function configuredModels(settings) {
|
|
1124
1714
|
const builtinId = builtinProviderId(settings);
|
|
1125
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);
|
|
1126
1720
|
const registryOwnedIds = new Set(
|
|
1127
|
-
|
|
1128
|
-
(provider) => provider.models.map((model) => model.id)
|
|
1129
|
-
)
|
|
1721
|
+
parsedRegistry.flatMap((provider) => provider.models.map((model) => model.id))
|
|
1130
1722
|
);
|
|
1131
|
-
const
|
|
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
|
+
}
|
|
1132
1731
|
const out = uniqueValues([
|
|
1133
1732
|
settings.openaiModel,
|
|
1134
1733
|
...splitCsv(settings.openaiAllowedModels)
|
|
1135
|
-
]).filter((id) => !isRegistryNamespaced(id)).map((id) =>
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
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) {
|
|
1146
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
|
+
}
|
|
1147
1764
|
for (const model of provider.models) {
|
|
1148
|
-
|
|
1149
|
-
id: model.id,
|
|
1150
|
-
label: model.label ?? model.id,
|
|
1151
|
-
providerId: provider.id,
|
|
1152
|
-
providerLabel,
|
|
1153
|
-
api: provider.api,
|
|
1154
|
-
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1155
|
-
...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
|
|
1156
|
-
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1765
|
+
const capabilities = model.capabilities ? normalizeCapabilities(model.capabilities) : legacyModelCapabilities(settings, {
|
|
1157
1766
|
reasoningEffort: model.reasoningEffort ?? false,
|
|
1158
1767
|
hostedWebSearch: model.hostedWebSearch ?? false
|
|
1159
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
|
+
);
|
|
1160
1792
|
}
|
|
1161
1793
|
}
|
|
1162
|
-
|
|
1163
|
-
return out
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
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;
|
|
1170
1804
|
}
|
|
1171
1805
|
function configuredAllowedModels(settings) {
|
|
1172
1806
|
return configuredModels(settings).map((model) => model.id);
|
|
1173
1807
|
}
|
|
1174
1808
|
function resolveModelProvider(settings, modelId) {
|
|
1175
|
-
const
|
|
1809
|
+
const canonicalModelId = canonicalizeConfiguredModelId(settings, modelId);
|
|
1810
|
+
const model = configuredModels(settings).find((candidate) => candidate.id === canonicalModelId);
|
|
1176
1811
|
if (!model) {
|
|
1177
1812
|
return void 0;
|
|
1178
1813
|
}
|
|
@@ -1184,22 +1819,97 @@ function resolveModelProvider(settings, modelId) {
|
|
|
1184
1819
|
}
|
|
1185
1820
|
return { provider, model };
|
|
1186
1821
|
}
|
|
1187
|
-
function
|
|
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
|
+
);
|
|
1188
1874
|
const registry = {};
|
|
1189
1875
|
for (const provider of parseModelProvidersJson(settings.modelProvidersJson)) {
|
|
1190
1876
|
for (const model of provider.models) {
|
|
1191
1877
|
if (model.pricing) {
|
|
1192
|
-
registry[model.id] = model.pricing;
|
|
1878
|
+
registry[model.id] = normalizeModelPricingSchedule(model.pricing);
|
|
1193
1879
|
}
|
|
1194
1880
|
}
|
|
1195
1881
|
}
|
|
1196
|
-
const configured =
|
|
1882
|
+
const configured = Object.fromEntries(
|
|
1883
|
+
Object.entries(parseModelPricingJson(settings.modelPricingJson)).map(([model, pricing]) => [
|
|
1884
|
+
model,
|
|
1885
|
+
{ default: pricing }
|
|
1886
|
+
])
|
|
1887
|
+
);
|
|
1197
1888
|
return {
|
|
1198
|
-
...
|
|
1889
|
+
...defaults,
|
|
1199
1890
|
...registry,
|
|
1200
1891
|
...configured
|
|
1201
1892
|
};
|
|
1202
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
|
+
}
|
|
1203
1913
|
function contextInputBudgetTokens(settings) {
|
|
1204
1914
|
if (settings.contextEffectiveWindowTokens !== void 0) {
|
|
1205
1915
|
return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
|
|
@@ -1217,7 +1927,8 @@ function settingsWithResolvedModelContext(settings, model) {
|
|
|
1217
1927
|
model.effectiveContextWindowTokens
|
|
1218
1928
|
)
|
|
1219
1929
|
},
|
|
1220
|
-
...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }
|
|
1930
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit },
|
|
1931
|
+
...model.toolOutputTruncationTokens === void 0 ? {} : { modelToolOutputTruncationTokens: model.toolOutputTruncationTokens }
|
|
1221
1932
|
};
|
|
1222
1933
|
}
|
|
1223
1934
|
function configuredStaticUsageLimits(settings) {
|
|
@@ -1241,14 +1952,25 @@ function configuredEntitlements(settings) {
|
|
|
1241
1952
|
};
|
|
1242
1953
|
}
|
|
1243
1954
|
function calculateModelUsageCostMicros(settings, model, usage) {
|
|
1244
|
-
const
|
|
1245
|
-
if (!
|
|
1955
|
+
const schedule = configuredModelPricingSchedules(settings)[model];
|
|
1956
|
+
if (!schedule) {
|
|
1246
1957
|
throw new Error(`Missing model pricing for ${model}`);
|
|
1247
1958
|
}
|
|
1248
1959
|
const entries = usage.requestUsageEntries && usage.requestUsageEntries.length > 0 ? usage.requestUsageEntries : [usage];
|
|
1249
|
-
const
|
|
1250
|
-
const
|
|
1251
|
-
|
|
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;
|
|
1252
1974
|
}
|
|
1253
1975
|
function configuredAllowedReasoningEfforts(settings) {
|
|
1254
1976
|
return uniqueValues([
|
|
@@ -1268,6 +1990,59 @@ function environmentsEncryptionKeyBytes(settings) {
|
|
|
1268
1990
|
}
|
|
1269
1991
|
return new Uint8Array(decoded);
|
|
1270
1992
|
}
|
|
1993
|
+
function temporalConnectionOptions(settings) {
|
|
1994
|
+
const apiKey = settings.temporalApiKey?.trim() || void 0;
|
|
1995
|
+
const serverNameOverride = settings.temporalTlsServerName?.trim() || void 0;
|
|
1996
|
+
const rootCa = decodeTemporalTlsMaterial(
|
|
1997
|
+
settings.temporalTlsRootCaCertificateBase64,
|
|
1998
|
+
"OPENGENI_TEMPORAL_TLS_ROOT_CA_CERTIFICATE_BASE64"
|
|
1999
|
+
);
|
|
2000
|
+
const clientCertificate = decodeTemporalTlsMaterial(
|
|
2001
|
+
settings.temporalTlsClientCertificateBase64,
|
|
2002
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64"
|
|
2003
|
+
);
|
|
2004
|
+
const clientPrivateKey = decodeTemporalTlsMaterial(
|
|
2005
|
+
settings.temporalTlsClientPrivateKeyBase64,
|
|
2006
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64"
|
|
2007
|
+
);
|
|
2008
|
+
if (Boolean(clientCertificate) !== Boolean(clientPrivateKey)) {
|
|
2009
|
+
throw new Error(
|
|
2010
|
+
"OPENGENI_TEMPORAL_TLS_CLIENT_CERTIFICATE_BASE64 and OPENGENI_TEMPORAL_TLS_CLIENT_PRIVATE_KEY_BASE64 must both be set or both omitted"
|
|
2011
|
+
);
|
|
2012
|
+
}
|
|
2013
|
+
const tls = {};
|
|
2014
|
+
if (serverNameOverride) {
|
|
2015
|
+
tls.serverNameOverride = serverNameOverride;
|
|
2016
|
+
}
|
|
2017
|
+
if (rootCa) {
|
|
2018
|
+
tls.serverRootCACertificate = rootCa;
|
|
2019
|
+
}
|
|
2020
|
+
if (clientCertificate && clientPrivateKey) {
|
|
2021
|
+
tls.clientCertPair = { crt: clientCertificate, key: clientPrivateKey };
|
|
2022
|
+
}
|
|
2023
|
+
const hasCustomTls = Object.keys(tls).length > 0;
|
|
2024
|
+
const tlsEnabled = settings.temporalTlsEnabled || Boolean(apiKey) || hasCustomTls;
|
|
2025
|
+
return {
|
|
2026
|
+
address: settings.temporalHost,
|
|
2027
|
+
...tlsEnabled ? { tls: hasCustomTls ? tls : true } : {},
|
|
2028
|
+
...apiKey ? { apiKey } : {}
|
|
2029
|
+
};
|
|
2030
|
+
}
|
|
2031
|
+
function decodeTemporalTlsMaterial(value, settingName) {
|
|
2032
|
+
const encoded = value?.replace(/\s/g, "");
|
|
2033
|
+
if (!encoded) {
|
|
2034
|
+
return void 0;
|
|
2035
|
+
}
|
|
2036
|
+
if (!/^[A-Za-z0-9+/]+={0,2}$/.test(encoded) || encoded.length % 4 === 1) {
|
|
2037
|
+
throw new Error(`${settingName} must contain valid base64`);
|
|
2038
|
+
}
|
|
2039
|
+
const decoded = Buffer.from(encoded, "base64");
|
|
2040
|
+
const canonical = decoded.toString("base64").replace(/=+$/, "");
|
|
2041
|
+
if (decoded.length === 0 || canonical !== encoded.replace(/=+$/, "")) {
|
|
2042
|
+
throw new Error(`${settingName} must contain valid base64`);
|
|
2043
|
+
}
|
|
2044
|
+
return new Uint8Array(decoded);
|
|
2045
|
+
}
|
|
1271
2046
|
function dbSearchPath(settings) {
|
|
1272
2047
|
const schema = settings.dbSchema?.trim();
|
|
1273
2048
|
if (!schema) {
|
|
@@ -1315,6 +2090,9 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
|
|
|
1315
2090
|
}
|
|
1316
2091
|
if (settings.toolspaceEnabled) {
|
|
1317
2092
|
environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
|
|
2093
|
+
if (settings.ogtoolPackageSpec) {
|
|
2094
|
+
environment.OPENGENI_OGTOOL_PACKAGE_SPEC ??= settings.ogtoolPackageSpec;
|
|
2095
|
+
}
|
|
1318
2096
|
if (options.workspaceId) {
|
|
1319
2097
|
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1320
2098
|
settings,
|
|
@@ -1516,7 +2294,14 @@ function parseModelProvidersJson(raw) {
|
|
|
1516
2294
|
`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
|
|
1517
2295
|
);
|
|
1518
2296
|
}
|
|
1519
|
-
|
|
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
|
+
}
|
|
1520
2305
|
});
|
|
1521
2306
|
}
|
|
1522
2307
|
function parseIntegrationsOauthClientsJson(raw) {
|
|
@@ -1675,6 +2460,7 @@ function firstPartyDocumentsMcpServerUrl(mcpUrl) {
|
|
|
1675
2460
|
return `${mcpUrl.replace(/\/+$/, "")}/docs`;
|
|
1676
2461
|
}
|
|
1677
2462
|
function validateSettings(settings) {
|
|
2463
|
+
temporalConnectionOptions(settings);
|
|
1678
2464
|
if (settings.toolspaceEnabled && !settings.delegationSecret) {
|
|
1679
2465
|
throw new Error("OPENGENI_DELEGATION_SECRET is required when OPENGENI_TOOLSPACE_ENABLED=true");
|
|
1680
2466
|
}
|
|
@@ -1921,6 +2707,7 @@ function validateSettings(settings) {
|
|
|
1921
2707
|
);
|
|
1922
2708
|
}
|
|
1923
2709
|
}
|
|
2710
|
+
configuredModels(settings);
|
|
1924
2711
|
}
|
|
1925
2712
|
function resolveStreamTokenSecret(settings) {
|
|
1926
2713
|
const explicit = settings.streamTokenSecret?.trim();
|
|
@@ -2006,21 +2793,27 @@ function delay(ms) {
|
|
|
2006
2793
|
}
|
|
2007
2794
|
export {
|
|
2008
2795
|
AGENT_INSTRUCTIONS_CORE_PLACEHOLDER,
|
|
2796
|
+
CapabilityStateV1Schema,
|
|
2797
|
+
CapabilitySupportV1,
|
|
2009
2798
|
DEFAULT_AGENT_INSTRUCTIONS,
|
|
2010
2799
|
IntegrationOAuthClientConfigSchema,
|
|
2011
2800
|
McpServerConnectionRefSchema,
|
|
2801
|
+
ModelCapabilitiesV1Schema,
|
|
2012
2802
|
ModelProviderApi,
|
|
2013
2803
|
RegistryProviderKind,
|
|
2014
2804
|
SANDBOX_REQUIRED_ENV,
|
|
2015
2805
|
applyGitAuthPointerEnvironment,
|
|
2806
|
+
assertTurnExecutionPolicyMatchesConfigV1,
|
|
2016
2807
|
builtinProviderId,
|
|
2017
2808
|
calculateModelUsageCostMicros,
|
|
2809
|
+
canonicalizeConfiguredModelId,
|
|
2018
2810
|
collectGitIdentityEnvironment,
|
|
2019
2811
|
collectSandboxEnvironment,
|
|
2020
2812
|
configuredAllowedModels,
|
|
2021
2813
|
configuredAllowedReasoningEfforts,
|
|
2022
2814
|
configuredEntitlements,
|
|
2023
2815
|
configuredModelPricing,
|
|
2816
|
+
configuredModelPricingSchedules,
|
|
2024
2817
|
configuredModels,
|
|
2025
2818
|
configuredProviders,
|
|
2026
2819
|
configuredStaticUsageLimits,
|
|
@@ -2051,14 +2844,18 @@ export {
|
|
|
2051
2844
|
resolveProviderApiKey,
|
|
2052
2845
|
resolveRelayTokenSecret,
|
|
2053
2846
|
resolveStreamTokenSecret,
|
|
2847
|
+
resolveTurnExecutionPolicyV1,
|
|
2054
2848
|
retryStartupDependency,
|
|
2055
2849
|
sandboxEnvironmentVariableNames,
|
|
2056
2850
|
sandboxLifecycleHookIds,
|
|
2057
2851
|
sandboxPreparationProfiles,
|
|
2058
2852
|
sandboxWarmRateMicrosPerSecond,
|
|
2853
|
+
selectModelPricing,
|
|
2059
2854
|
settingsWithResolvedModelContext,
|
|
2060
2855
|
stableSandboxEnvironmentForRun,
|
|
2061
2856
|
startupRetryOptions,
|
|
2062
|
-
streamTokenDegraded
|
|
2857
|
+
streamTokenDegraded,
|
|
2858
|
+
temporalConnectionOptions,
|
|
2859
|
+
withCodexCatalogProvider
|
|
2063
2860
|
};
|
|
2064
2861
|
//# sourceMappingURL=index.js.map
|