@opengeni/config 0.4.0 → 0.5.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.d.ts +78 -58
- package/dist/index.js +415 -204
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/index.ts +715 -340
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
StaticUsageLimits,
|
|
11
11
|
UsageLimitsMode
|
|
12
12
|
} from "@opengeni/contracts";
|
|
13
|
-
import { CODEX_MODEL_ID_PREFIX } from "@opengeni/codex/constants";
|
|
13
|
+
import { CODEX_MODEL_ID_PREFIX, CODEX_PROVIDER_ID } from "@opengeni/codex/constants";
|
|
14
14
|
import { z } from "zod";
|
|
15
15
|
var envName = /^[A-Za-z_][A-Za-z0-9_]*$/;
|
|
16
16
|
var registryId = /^[A-Za-z0-9_-]+$/;
|
|
@@ -67,9 +67,9 @@ var DEFAULT_AGENT_INSTRUCTIONS = [
|
|
|
67
67
|
"File resources are mounted under files/<file-id>/ unless the session specifies another mount path.",
|
|
68
68
|
"Attached files are mounted read-only; copy them before modifying.",
|
|
69
69
|
"Bundled skills are under .agents/ and can include infrastructure, marketing, or other role-specific guidance.",
|
|
70
|
-
"Use Checkov, Terraform, Azure CLI,
|
|
70
|
+
"Use Checkov, Terraform, Azure CLI, git provider CLIs, and repository tools when relevant; gh, glab, and az repos are pre-authenticated when the host brokers matching git credentials.",
|
|
71
71
|
"When the Azure sandbox preparation profile is enabled and service-principal variables are present, the sandbox is pre-authenticated with normal Azure CLI before work starts.",
|
|
72
|
-
"Treat code-changing work as GitOps work: create a focused branch/commit/PR when
|
|
72
|
+
"Treat code-changing work as GitOps work: create a focused branch/commit/PR when git provider credentials are available; otherwise report exact commands and blockers.",
|
|
73
73
|
"Return concise, factual summaries with files changed, commands run, and remaining blockers.",
|
|
74
74
|
AGENT_INSTRUCTIONS_CORE_PLACEHOLDER
|
|
75
75
|
].join(" ");
|
|
@@ -146,62 +146,42 @@ var SettingsSchema = z.object({
|
|
|
146
146
|
// it then acts as a hard ceiling that per-goal overrides can only lower.
|
|
147
147
|
goalMaxAutoContinuations: z.coerce.number().int().positive().optional(),
|
|
148
148
|
goalNoProgressLimit: z.coerce.number().int().positive().default(3),
|
|
149
|
+
// Temporary safety valve: spawned child sessions still run and retain their
|
|
150
|
+
// own durable events/goals, but terminal child episodes do not create parent
|
|
151
|
+
// system updates or inference work unless explicitly enabled.
|
|
152
|
+
childCompletionParentWakeEnabled: EnvBoolean.default(false),
|
|
149
153
|
// Per-segment ceiling on agent loop turns (model calls) within a single
|
|
150
154
|
// session turn. Effectively unbounded by default for the same reason as
|
|
151
155
|
// above; the graceful max-turns valve (idle + goal continuation, never a
|
|
152
156
|
// session failure) remains as inert safety should a deployment set a cap.
|
|
153
157
|
agentMaxModelCallsPerTurn: z.coerce.number().int().positive().default(1e6),
|
|
154
|
-
//
|
|
155
|
-
//
|
|
156
|
-
//
|
|
157
|
-
// RunState blob. Items and the sandbox envelope are dual-written
|
|
158
|
-
// unconditionally; this flag governs the read path only, so flipping back to
|
|
159
|
-
// "run_state" remains a safe rollback at any time.
|
|
160
|
-
sessionHistorySource: z.enum(["run_state", "items"]).default("items"),
|
|
161
|
-
// Provider-aware conversation context management (long-lived sessions
|
|
162
|
-
// otherwise grow unbounded until they overflow the model context window and
|
|
163
|
-
// hard-fail every turn). Resolution (see resolveContextCompactionMode):
|
|
164
|
-
// "auto" (default) -> "server" when openaiProvider === "openai" (the
|
|
165
|
-
// OpenAI platform Responses API honors server-side context_management),
|
|
166
|
-
// else "client" (Azure rejects context_management with a 400, so we run
|
|
167
|
-
// our own client-side compaction).
|
|
168
|
-
// "server" / "client" -> force that path regardless of provider.
|
|
169
|
-
// "off" -> neither path (legacy unbounded growth; escape hatch only).
|
|
170
|
-
contextCompactionMode: z.enum(["auto", "server", "client", "off"]).default("auto"),
|
|
171
|
-
// The model's real context window in tokens. gpt-5.5's true window is
|
|
172
|
-
// 1,050,000; it is absent from the SDK's hardcoded compaction window map (it
|
|
173
|
-
// knows only up to gpt-5.4), so the SDK's DynamicCompactionPolicy would fall
|
|
174
|
-
// back to a wrong 240k. We pass an explicit StaticCompactionPolicy threshold
|
|
175
|
-
// derived from these settings on the server path, and use the same numbers to
|
|
176
|
-
// budget the client path.
|
|
158
|
+
// The model family's real context window in tokens. OpenGeni always performs
|
|
159
|
+
// one durable, portable plaintext compaction transition; there is no
|
|
160
|
+
// provider/server/off mode ladder.
|
|
177
161
|
contextWindowTokens: z.coerce.number().int().positive().default(105e4),
|
|
162
|
+
// Optional model-catalog effective input ceiling. Codex models expose this as
|
|
163
|
+
// raw context_window * effective_context_window_percent; when absent, retain
|
|
164
|
+
// the deployment-level window-minus-reserved-output behavior.
|
|
165
|
+
contextEffectiveWindowTokens: z.coerce.number().int().positive().optional(),
|
|
178
166
|
// Proactive compaction threshold as a ratio of the model context window.
|
|
179
|
-
// Defaults to
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
167
|
+
// Defaults to 90%: compact as late as possible — retained context beats early
|
|
168
|
+
// headroom now that per-model windows are declared honestly (input-effective,
|
|
169
|
+
// empirically measured), and the fail-closed reactive compact-on-reject path
|
|
170
|
+
// absorbs any overshoot as one retried call rather than a dead session.
|
|
171
|
+
// Clamped to [0.3, 0.9] so deployments can tune the trigger without
|
|
172
|
+
// accidentally disabling compaction.
|
|
173
|
+
contextCompactionThresholdRatio: z.coerce.number().default(0.9).transform((value) => {
|
|
183
174
|
if (!Number.isFinite(value)) {
|
|
184
|
-
return 0.
|
|
175
|
+
return 0.9;
|
|
185
176
|
}
|
|
186
177
|
return Math.min(0.9, Math.max(0.3, value));
|
|
187
178
|
}),
|
|
188
179
|
// Tokens reserved for model output; subtracted from the window to get the
|
|
189
180
|
// usable input budget B = contextWindowTokens - contextReservedOutputTokens.
|
|
190
181
|
contextReservedOutputTokens: z.coerce.number().int().nonnegative().default(128e3),
|
|
191
|
-
//
|
|
192
|
-
//
|
|
193
|
-
|
|
194
|
-
contextServerCompactThresholdTokens: z.coerce.number().int().positive().optional(),
|
|
195
|
-
// Deprecated back-compat knobs. The threshold is now controlled by
|
|
196
|
-
// contextCompactionThresholdRatio; these remain parsed so older deployments do
|
|
197
|
-
// not fail boot when their env still contains them.
|
|
198
|
-
contextCompactSoftFraction: z.coerce.number().positive().max(1).default(0.7),
|
|
199
|
-
contextCompactHardFraction: z.coerce.number().positive().max(1).default(0.85),
|
|
200
|
-
// Deprecated for the client path; parsed for env/back-compat only.
|
|
201
|
-
contextKeepRecentTokens: z.coerce.number().int().positive().default(32e3),
|
|
202
|
-
// Parsed for back-compat. Client compaction uses the fixed 20k Codex summary
|
|
203
|
-
// buffer as its generated-summary output ceiling.
|
|
204
|
-
contextSummaryMaxTokens: z.coerce.number().int().positive().default(2e4),
|
|
182
|
+
// Model-catalog auto-compact limit. When present it is clamped to
|
|
183
|
+
// 90% of the raw window, matching Codex core's auto_compact_token_limit().
|
|
184
|
+
contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
|
|
205
185
|
authRequired: EnvBoolean.default(false),
|
|
206
186
|
accessKey: z.string().optional(),
|
|
207
187
|
authAllowHealth: EnvBoolean.default(true),
|
|
@@ -214,8 +194,8 @@ var SettingsSchema = z.object({
|
|
|
214
194
|
openaiProvider: z.enum(["openai", "azure"]).default("openai"),
|
|
215
195
|
openaiApiKey: z.string().optional(),
|
|
216
196
|
openaiBaseUrl: z.string().optional(),
|
|
217
|
-
openaiModel: z.string().default("gpt-5.
|
|
218
|
-
openaiAllowedModels: z.string().default("gpt-5.
|
|
197
|
+
openaiModel: z.string().default("gpt-5.6-sol"),
|
|
198
|
+
openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
|
|
219
199
|
modelPricingJson: z.string().default("{}"),
|
|
220
200
|
// Extra (non-built-in) model providers, declared by the host as a JSON
|
|
221
201
|
// provider registry. Each entry carries its own base URL, API key, wire API
|
|
@@ -237,6 +217,11 @@ var SettingsSchema = z.object({
|
|
|
237
217
|
// tool that BM25-discloses only the matching connectors. Default OFF — a codex
|
|
238
218
|
// turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
239
219
|
codexToolSearchEnabled: EnvBoolean.default(false),
|
|
220
|
+
// OPE-21 atomic, workspace-local credential allocation. Default OFF is a
|
|
221
|
+
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
222
|
+
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
223
|
+
// rollback; the additive lease table/cursor columns become inert.
|
|
224
|
+
codexCredentialLeasingEnabled: EnvBoolean.default(false),
|
|
240
225
|
// Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
|
|
241
226
|
// rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
|
|
242
227
|
// match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
|
|
@@ -274,7 +259,7 @@ var SettingsSchema = z.object({
|
|
|
274
259
|
// the non-bypassable CORE at AGENT_INSTRUCTIONS_CORE_PLACEHOLDER (or appends
|
|
275
260
|
// it when the template omits the marker), and uses the result as the agent's
|
|
276
261
|
// instructions. Defaulting to DEFAULT_AGENT_INSTRUCTIONS keeps the composed
|
|
277
|
-
// default
|
|
262
|
+
// default pinned by runtime tests.
|
|
278
263
|
agentInstructionsTemplate: z.string().default(DEFAULT_AGENT_INSTRUCTIONS),
|
|
279
264
|
azureOpenaiBaseUrl: z.string().optional(),
|
|
280
265
|
azureOpenaiEndpoint: z.string().optional(),
|
|
@@ -379,6 +364,13 @@ var SettingsSchema = z.object({
|
|
|
379
364
|
// recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
|
|
380
365
|
recordingEnabled: EnvBoolean.default(true),
|
|
381
366
|
recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
|
|
367
|
+
// Workbench v2 turn-end workspace capture (dossier §10.1). When on, the turn
|
|
368
|
+
// activity probes the box's changed files off the live box at turn end and
|
|
369
|
+
// persists a capture revision (blobs in @opengeni/storage) so the workbench
|
|
370
|
+
// paints cold/offline sessions with zero machine round-trips. Best-effort and
|
|
371
|
+
// fully behind this flag: off ⇒ capture is skipped and reads fall back to the
|
|
372
|
+
// live/wake path (status-quo behavior). Default on; explicit per environment.
|
|
373
|
+
workspaceCaptureEnabled: EnvBoolean.default(true),
|
|
382
374
|
recordingFramerate: z.coerce.number().int().positive().default(15),
|
|
383
375
|
recordingMaxSeconds: z.coerce.number().int().positive().default(600),
|
|
384
376
|
recordingMaxBytes: z.coerce.number().int().positive().default(268435456),
|
|
@@ -435,6 +427,19 @@ var SettingsSchema = z.object({
|
|
|
435
427
|
// EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
|
|
436
428
|
// turn the flag ON the moment anyone set the env var to disable it).
|
|
437
429
|
sandboxOwnershipEnabled: EnvBoolean.default(false),
|
|
430
|
+
// --- lazy sandbox provisioning rollout flag, default OFF ---
|
|
431
|
+
// Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
|
|
432
|
+
// property of the owned path — the SDK never creates/resumes an injected session,
|
|
433
|
+
// so we control when the box is established). When TRUE, a turn does NOT provision
|
|
434
|
+
// its box at turn start: the lease acquire + resume-by-id + hooks + downloads +
|
|
435
|
+
// heartbeat + recording are deferred to an in-process single-flight provisioner
|
|
436
|
+
// that runs the FIRST time a sandbox op is dispatched (via the routing proxy's
|
|
437
|
+
// resolveActiveBackend). A turn whose model never calls a sandbox-backed tool ends
|
|
438
|
+
// with NO lease row and ZERO warm-seconds. When FALSE (or ownership off) the turn
|
|
439
|
+
// provisions eagerly exactly as today — byte-for-byte. EnvBoolean (NOT
|
|
440
|
+
// z.coerce.boolean(), which coerces "false" -> true and would turn the flag ON the
|
|
441
|
+
// moment anyone set the env var to disable it).
|
|
442
|
+
sandboxLazyProvisionEnabled: EnvBoolean.default(false),
|
|
438
443
|
// --- bring-your-own-compute (selfhosted 11th backend) rollout flag, default OFF ---
|
|
439
444
|
// The keystone flag for the whole selfhosted feature (the enrollment device-flow,
|
|
440
445
|
// the NATS control plane, the relay stream tier). When FALSE the enrollment routes
|
|
@@ -443,6 +448,11 @@ var SettingsSchema = z.object({
|
|
|
443
448
|
// z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
|
|
444
449
|
// the deploy-staging IaC secret/configmap pattern (dossier §17/§25.1).
|
|
445
450
|
sandboxSelfhostedEnabled: EnvBoolean.default(false),
|
|
451
|
+
// Gates the op-stream (streaming exec) transport to Connected Machines. The
|
|
452
|
+
// runner must ALSO advertise Capabilities.op_stream; default off, and legacy
|
|
453
|
+
// request/reply exec is the permanent fallback. EnvBoolean (NOT
|
|
454
|
+
// z.coerce.boolean(), which coerces "false" -> true).
|
|
455
|
+
agentOpStreamEnabled: EnvBoolean.default(false),
|
|
446
456
|
// The HMAC secret the control plane signs the enrollment bearer credential with
|
|
447
457
|
// (the `oge_` envelope the agent presents back to the control plane). Optional:
|
|
448
458
|
// when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
|
|
@@ -499,6 +509,25 @@ var SettingsSchema = z.object({
|
|
|
499
509
|
// bus connects anonymously (local dev / a NATS with no auth_callout).
|
|
500
510
|
selfhostedNatsControlUser: z.string().optional(),
|
|
501
511
|
selfhostedNatsControlPassword: z.string().optional(),
|
|
512
|
+
// --- selfhosted (Connected Machine) control/exec op deadlines ---------------
|
|
513
|
+
// The control plane splits its op deadline in two. CONTROL ops (ping / fs / git /
|
|
514
|
+
// desktop / pty) must stay responsive so a machine's liveness is never masked by a
|
|
515
|
+
// slow op, so they use the short control timeout. EXEC gets its OWN, larger budget:
|
|
516
|
+
// a real command (compile, test run, dependency install) routinely outlives the
|
|
517
|
+
// control timeout, and before the split a long command was killed at the ~30s
|
|
518
|
+
// control wall. The agent kills the exec child at this deadline; the wire waits
|
|
519
|
+
// slightly longer (SELFHOSTED_EXEC_REPLY_GRACE_MS) for the typed timed-out reply.
|
|
520
|
+
//
|
|
521
|
+
// The exec default is a DELIBERATELY MODEST 2min (not 5): the agent-side admission
|
|
522
|
+
// pool is (until a later agent release) a FLAT 8 permits with no per-class split,
|
|
523
|
+
// so 8 slow execs holding a permit for 5 minutes would blanket-DRAIN every fs/git
|
|
524
|
+
// op — shipping the amplifier before the class-aware-admission fix. 2min still
|
|
525
|
+
// clears the large majority of the observed >30s exec tail; genuinely long jobs run
|
|
526
|
+
// in the background (see the exec-deadline hint) or raise the knob per deployment.
|
|
527
|
+
// Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 2min) and
|
|
528
|
+
// OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS (default 30s).
|
|
529
|
+
sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().positive().default(12e4),
|
|
530
|
+
sandboxSelfhostedControlTimeoutMs: z.coerce.number().int().positive().default(3e4),
|
|
502
531
|
// --- sandbox lease cadences (cadence invariant validated at boot below) ---
|
|
503
532
|
// reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
|
|
504
533
|
// box idle timeout (effectiveModalIdleTimeoutSeconds, which defaults to the hard
|
|
@@ -517,6 +546,22 @@ var SettingsSchema = z.object({
|
|
|
517
546
|
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim. Knob:
|
|
518
547
|
// OPENGENI_SANDBOX_IDLE_GRACE_MS.
|
|
519
548
|
sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
|
|
549
|
+
// MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
|
|
550
|
+
// reaper's drain-persist only protects boxes the reaper itself kills; a box
|
|
551
|
+
// that dies any other way (Modal's hard creation-time timeout on a session
|
|
552
|
+
// busy past it, provider OOM/infra death) loses everything since the last
|
|
553
|
+
// clean drain. While a turn holds the box, the turn heartbeat and turn-end
|
|
554
|
+
// both take a snapshot when at least this interval has passed since the last
|
|
555
|
+
// one (same epoch-fenced fold-onto-lease seam as the drain), bounding the
|
|
556
|
+
// worst-case loss of ANY unclean box death to this window. 0 disables.
|
|
557
|
+
// Knob: OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS. Default 15min.
|
|
558
|
+
sandboxSnapshotIntervalMs: z.coerce.number().int().min(0).default(9e5),
|
|
559
|
+
// Maximum time a best-effort /workspace snapshot capture may hold turn/reaper
|
|
560
|
+
// cleanup. A hung provider snapshot must never pin a lease holder, block
|
|
561
|
+
// graceful shutdown, or become permission to GC an older archive. Timeout is
|
|
562
|
+
// treated exactly like a failed best-effort snapshot. Knob:
|
|
563
|
+
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
564
|
+
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
|
|
520
565
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
521
566
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
522
567
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -526,6 +571,11 @@ var SettingsSchema = z.object({
|
|
|
526
571
|
// (a liveness/reaper cadence), this bounds how long one turn waits for capacity
|
|
527
572
|
// or provider creation before surfacing a clear turn.failed error.
|
|
528
573
|
sandboxWarmingTimeoutMs: z.coerce.number().int().positive().default(6e5),
|
|
574
|
+
// Rig setup-script budget (M3): the wall-clock timeout the rig-setup lifecycle
|
|
575
|
+
// hook runs its script under, distinct from the 120s per-command lifecycle
|
|
576
|
+
// default (a rig may compile/install heavy tooling on first cold create).
|
|
577
|
+
// Env: OPENGENI_RIG_SETUP_TIMEOUT_MS. Default 10min.
|
|
578
|
+
rigSetupTimeoutMs: z.coerce.number().int().positive().default(6e5),
|
|
529
579
|
// --- sandbox warm-time billing (P2.1) ---
|
|
530
580
|
// Per-backend warm rate (usd_micros/sec), like modelPricingJson: an empty {}
|
|
531
581
|
// means warm-cost is not debited (warm-seconds are still metered for audit).
|
|
@@ -586,30 +636,32 @@ var SettingsSchema = z.object({
|
|
|
586
636
|
stripePublishableKey: z.string().optional(),
|
|
587
637
|
stripeWebhookSecret: z.string().optional(),
|
|
588
638
|
stripeCreditsProductId: z.string().optional(),
|
|
589
|
-
mcpServers: z.array(
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
639
|
+
mcpServers: z.array(
|
|
640
|
+
z.object({
|
|
641
|
+
id: z.string().min(1).regex(registryId),
|
|
642
|
+
name: z.string().min(1).optional(),
|
|
643
|
+
url: z.string().url(),
|
|
644
|
+
allowedTools: z.array(z.string().min(1)).optional(),
|
|
645
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
646
|
+
cacheToolsList: z.boolean().default(false),
|
|
647
|
+
/**
|
|
648
|
+
* Human-approval policy for this server's tools, overlaid per-run from a
|
|
649
|
+
* session MCP server row (never from OPENGENI_MCP_SERVERS). `true` = all
|
|
650
|
+
* tools require approval; a string[] = only the listed UNPREFIXED tool
|
|
651
|
+
* names do; absent = auto-run (the historical default). Enforced in the
|
|
652
|
+
* runtime by attaching `needsApproval` to the matching MCP tools.
|
|
653
|
+
*/
|
|
654
|
+
requireApproval: z.union([z.boolean(), z.array(z.string().min(1))]).optional(),
|
|
655
|
+
/**
|
|
656
|
+
* Extra request headers sent to this MCP server (credential injection
|
|
657
|
+
* for workspace-enabled capability MCPs). Populated at runtime from
|
|
658
|
+
* encrypted capability-installation credentials; do not put secrets in
|
|
659
|
+
* OPENGENI_MCP_SERVERS.
|
|
660
|
+
*/
|
|
661
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
662
|
+
connectionRef: McpServerConnectionRefSchema.optional()
|
|
663
|
+
})
|
|
664
|
+
).default([])
|
|
613
665
|
});
|
|
614
666
|
var ModelPricingSchema = z.object({
|
|
615
667
|
inputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
@@ -625,6 +677,8 @@ var RegistryModelSchema = z.object({
|
|
|
625
677
|
label: z.string().min(1).optional(),
|
|
626
678
|
// display name; defaults to id
|
|
627
679
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
680
|
+
effectiveContextWindowTokens: z.number().int().positive().optional(),
|
|
681
|
+
autoCompactTokenLimit: z.number().int().positive().optional(),
|
|
628
682
|
reasoningEffort: z.boolean().optional(),
|
|
629
683
|
// model accepts a reasoning-effort control
|
|
630
684
|
hostedWebSearch: z.boolean().optional(),
|
|
@@ -653,12 +707,24 @@ var IntegrationOAuthClientConfigSchema = z.object({
|
|
|
653
707
|
tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
|
|
654
708
|
});
|
|
655
709
|
var defaultModelPricing = {
|
|
656
|
-
"gpt-5.
|
|
710
|
+
"gpt-5.6-sol": {
|
|
657
711
|
inputMicrosPerMillionTokens: 5e6,
|
|
658
712
|
cachedInputMicrosPerMillionTokens: 5e5,
|
|
659
713
|
outputMicrosPerMillionTokens: 3e7,
|
|
660
714
|
marginBps: 2500
|
|
661
715
|
},
|
|
716
|
+
"gpt-5.6-terra": {
|
|
717
|
+
inputMicrosPerMillionTokens: 25e5,
|
|
718
|
+
cachedInputMicrosPerMillionTokens: 25e4,
|
|
719
|
+
outputMicrosPerMillionTokens: 15e6,
|
|
720
|
+
marginBps: 2500
|
|
721
|
+
},
|
|
722
|
+
"gpt-5.6-luna": {
|
|
723
|
+
inputMicrosPerMillionTokens: 1e6,
|
|
724
|
+
cachedInputMicrosPerMillionTokens: 1e5,
|
|
725
|
+
outputMicrosPerMillionTokens: 6e6,
|
|
726
|
+
marginBps: 2500
|
|
727
|
+
},
|
|
662
728
|
"gpt-5.4": {
|
|
663
729
|
inputMicrosPerMillionTokens: 25e5,
|
|
664
730
|
cachedInputMicrosPerMillionTokens: 25e4,
|
|
@@ -734,21 +800,11 @@ var SANDBOX_REQUIRED_ENV = {
|
|
|
734
800
|
{ field: "modalTokenId", env: "OPENGENI_MODAL_TOKEN_ID" },
|
|
735
801
|
{ field: "modalTokenSecret", env: "OPENGENI_MODAL_TOKEN_SECRET" }
|
|
736
802
|
],
|
|
737
|
-
daytona: [
|
|
738
|
-
|
|
739
|
-
],
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
],
|
|
743
|
-
e2b: [
|
|
744
|
-
{ field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }
|
|
745
|
-
],
|
|
746
|
-
blaxel: [
|
|
747
|
-
{ field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }
|
|
748
|
-
],
|
|
749
|
-
cloudflare: [
|
|
750
|
-
{ field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }
|
|
751
|
-
],
|
|
803
|
+
daytona: [{ field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }],
|
|
804
|
+
runloop: [{ field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }],
|
|
805
|
+
e2b: [{ field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }],
|
|
806
|
+
blaxel: [{ field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }],
|
|
807
|
+
cloudflare: [{ field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }],
|
|
752
808
|
vercel: [
|
|
753
809
|
{ field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
|
|
754
810
|
{ field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
|
|
@@ -780,7 +836,9 @@ function getSettings() {
|
|
|
780
836
|
temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
|
|
781
837
|
temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
|
|
782
838
|
startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
|
|
783
|
-
startupDependencyRetryInitialDelayMs: optional(
|
|
839
|
+
startupDependencyRetryInitialDelayMs: optional(
|
|
840
|
+
"OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
|
|
841
|
+
),
|
|
784
842
|
startupDependencyRetryMaxDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_MAX_DELAY_MS"),
|
|
785
843
|
observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
|
|
786
844
|
observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
|
|
@@ -802,21 +860,19 @@ function getSettings() {
|
|
|
802
860
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
803
861
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
804
862
|
integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
|
|
805
|
-
integrationsAllowPrivateNetworkTargets: optional(
|
|
863
|
+
integrationsAllowPrivateNetworkTargets: optional(
|
|
864
|
+
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
|
|
865
|
+
),
|
|
806
866
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
807
867
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
808
868
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
869
|
+
childCompletionParentWakeEnabled: optional("OPENGENI_CHILD_COMPLETION_PARENT_WAKE_ENABLED"),
|
|
809
870
|
agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
|
|
810
|
-
sessionHistorySource: optional("OPENGENI_SESSION_HISTORY_SOURCE"),
|
|
811
|
-
contextCompactionMode: optional("OPENGENI_CONTEXT_COMPACTION_MODE"),
|
|
812
871
|
contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
|
|
872
|
+
contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
|
|
813
873
|
contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
|
|
814
874
|
contextReservedOutputTokens: optional("OPENGENI_CONTEXT_RESERVED_OUTPUT_TOKENS"),
|
|
815
|
-
|
|
816
|
-
contextCompactSoftFraction: optional("OPENGENI_CONTEXT_COMPACT_SOFT_FRACTION"),
|
|
817
|
-
contextCompactHardFraction: optional("OPENGENI_CONTEXT_COMPACT_HARD_FRACTION"),
|
|
818
|
-
contextKeepRecentTokens: optional("OPENGENI_CONTEXT_KEEP_RECENT_TOKENS"),
|
|
819
|
-
contextSummaryMaxTokens: optional("OPENGENI_CONTEXT_SUMMARY_MAX_TOKENS"),
|
|
875
|
+
contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
|
|
820
876
|
authRequired: optional("OPENGENI_AUTH_REQUIRED"),
|
|
821
877
|
accessKey: optional("OPENGENI_ACCESS_KEY"),
|
|
822
878
|
authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
|
|
@@ -835,6 +891,7 @@ function getSettings() {
|
|
|
835
891
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
836
892
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
837
893
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
894
|
+
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
838
895
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
839
896
|
codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
|
|
840
897
|
openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
|
|
@@ -874,6 +931,7 @@ function getSettings() {
|
|
|
874
931
|
computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
|
|
875
932
|
computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
|
|
876
933
|
recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
|
|
934
|
+
workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
|
|
877
935
|
recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
|
|
878
936
|
recordingFramerate: optional("OPENGENI_RECORDING_FRAMERATE"),
|
|
879
937
|
recordingMaxSeconds: optional("OPENGENI_RECORDING_MAX_SECONDS"),
|
|
@@ -913,7 +971,9 @@ function getSettings() {
|
|
|
913
971
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
914
972
|
vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
|
|
915
973
|
sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
|
|
974
|
+
sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
|
|
916
975
|
sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
|
|
976
|
+
agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
|
|
917
977
|
enrollmentSigningSecret: optional("OPENGENI_ENROLLMENT_SIGNING_SECRET"),
|
|
918
978
|
selfhostedNatsUrl: optional("OPENGENI_SELFHOSTED_NATS_URL"),
|
|
919
979
|
selfhostedRelayUrl: optional("OPENGENI_SELFHOSTED_RELAY_URL"),
|
|
@@ -925,13 +985,20 @@ function getSettings() {
|
|
|
925
985
|
selfhostedNatsCalloutPassword: optional("OPENGENI_SELFHOSTED_NATS_CALLOUT_PASSWORD"),
|
|
926
986
|
selfhostedNatsControlUser: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_USER"),
|
|
927
987
|
selfhostedNatsControlPassword: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_PASSWORD"),
|
|
988
|
+
sandboxSelfhostedExecTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS"),
|
|
989
|
+
sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
|
|
928
990
|
sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
|
|
929
991
|
sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
|
|
930
992
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
993
|
+
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
994
|
+
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
931
995
|
sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
|
|
932
996
|
sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
|
|
933
997
|
sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
|
|
934
|
-
|
|
998
|
+
rigSetupTimeoutMs: optional("OPENGENI_RIG_SETUP_TIMEOUT_MS"),
|
|
999
|
+
sandboxWarmRateMicrosPerSecondJson: optional(
|
|
1000
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"
|
|
1001
|
+
),
|
|
935
1002
|
sandboxMaxWarmSecondsPerWorkspace: optional("OPENGENI_SANDBOX_MAX_WARM_SECONDS_PER_WORKSPACE"),
|
|
936
1003
|
sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
|
|
937
1004
|
sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
|
|
@@ -1027,8 +1094,7 @@ function configuredProviders(settings) {
|
|
|
1027
1094
|
label: builtinProviderLabel(settings),
|
|
1028
1095
|
kind: "api-key",
|
|
1029
1096
|
api: "responses",
|
|
1030
|
-
builtin: true
|
|
1031
|
-
compactionMode: resolveContextCompactionMode(settings)
|
|
1097
|
+
builtin: true
|
|
1032
1098
|
};
|
|
1033
1099
|
if (settings.openaiProvider === "azure") {
|
|
1034
1100
|
builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
|
|
@@ -1037,28 +1103,41 @@ function configuredProviders(settings) {
|
|
|
1037
1103
|
builtin.baseUrl = settings.openaiBaseUrl;
|
|
1038
1104
|
builtin.apiKey = settings.openaiApiKey;
|
|
1039
1105
|
}
|
|
1040
|
-
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1106
|
+
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
1107
|
+
(provider) => ({
|
|
1108
|
+
id: provider.id,
|
|
1109
|
+
label: provider.label ?? provider.id,
|
|
1110
|
+
kind: provider.kind,
|
|
1111
|
+
api: provider.api,
|
|
1112
|
+
builtin: false,
|
|
1113
|
+
baseUrl: provider.baseUrl,
|
|
1114
|
+
apiKey: resolveProviderApiKey(provider),
|
|
1115
|
+
defaultQuery: provider.defaultQuery,
|
|
1116
|
+
defaultHeaders: provider.defaultHeaders
|
|
1117
|
+
})
|
|
1118
|
+
);
|
|
1052
1119
|
return [builtin, ...registry];
|
|
1053
1120
|
}
|
|
1121
|
+
function policyProviderIdForModel(settings, modelId) {
|
|
1122
|
+
if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
1123
|
+
return CODEX_PROVIDER_ID;
|
|
1124
|
+
}
|
|
1125
|
+
const configured = configuredModels(settings).find((model) => model.id === modelId);
|
|
1126
|
+
return configured?.providerId ?? builtinProviderId(settings);
|
|
1127
|
+
}
|
|
1054
1128
|
function configuredModels(settings) {
|
|
1055
1129
|
const builtinId = builtinProviderId(settings);
|
|
1056
1130
|
const builtinLabel = builtinProviderLabel(settings);
|
|
1057
1131
|
const registryOwnedIds = new Set(
|
|
1058
|
-
parseModelProvidersJson(settings.modelProvidersJson).flatMap(
|
|
1132
|
+
parseModelProvidersJson(settings.modelProvidersJson).flatMap(
|
|
1133
|
+
(provider) => provider.models.map((model) => model.id)
|
|
1134
|
+
)
|
|
1059
1135
|
);
|
|
1060
1136
|
const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
|
|
1061
|
-
const out = uniqueValues([
|
|
1137
|
+
const out = uniqueValues([
|
|
1138
|
+
settings.openaiModel,
|
|
1139
|
+
...splitCsv(settings.openaiAllowedModels)
|
|
1140
|
+
]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
|
|
1062
1141
|
id,
|
|
1063
1142
|
label: id,
|
|
1064
1143
|
providerId: builtinId,
|
|
@@ -1078,6 +1157,8 @@ function configuredModels(settings) {
|
|
|
1078
1157
|
providerLabel,
|
|
1079
1158
|
api: provider.api,
|
|
1080
1159
|
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1160
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
|
|
1161
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1081
1162
|
reasoningEffort: model.reasoningEffort ?? false,
|
|
1082
1163
|
hostedWebSearch: model.hostedWebSearch ?? false
|
|
1083
1164
|
});
|
|
@@ -1100,7 +1181,9 @@ function resolveModelProvider(settings, modelId) {
|
|
|
1100
1181
|
if (!model) {
|
|
1101
1182
|
return void 0;
|
|
1102
1183
|
}
|
|
1103
|
-
const provider = configuredProviders(settings).find(
|
|
1184
|
+
const provider = configuredProviders(settings).find(
|
|
1185
|
+
(candidate) => candidate.id === model.providerId
|
|
1186
|
+
);
|
|
1104
1187
|
if (!provider) {
|
|
1105
1188
|
return void 0;
|
|
1106
1189
|
}
|
|
@@ -1122,27 +1205,25 @@ function configuredModelPricing(settings) {
|
|
|
1122
1205
|
...configured
|
|
1123
1206
|
};
|
|
1124
1207
|
}
|
|
1125
|
-
function resolveContextCompactionMode(settings) {
|
|
1126
|
-
switch (settings.contextCompactionMode) {
|
|
1127
|
-
case "server":
|
|
1128
|
-
return "server";
|
|
1129
|
-
case "client":
|
|
1130
|
-
return "client";
|
|
1131
|
-
case "off":
|
|
1132
|
-
return "off";
|
|
1133
|
-
case "auto":
|
|
1134
|
-
default:
|
|
1135
|
-
return settings.openaiProvider === "openai" ? "server" : "client";
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
1208
|
function contextInputBudgetTokens(settings) {
|
|
1209
|
+
if (settings.contextEffectiveWindowTokens !== void 0) {
|
|
1210
|
+
return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
|
|
1211
|
+
}
|
|
1139
1212
|
return Math.max(0, settings.contextWindowTokens - settings.contextReservedOutputTokens);
|
|
1140
1213
|
}
|
|
1141
|
-
function
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1214
|
+
function settingsWithResolvedModelContext(settings, model) {
|
|
1215
|
+
const contextWindowTokens = model.contextWindowTokens ?? settings.contextWindowTokens;
|
|
1216
|
+
return {
|
|
1217
|
+
...settings,
|
|
1218
|
+
contextWindowTokens,
|
|
1219
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : {
|
|
1220
|
+
contextEffectiveWindowTokens: Math.min(
|
|
1221
|
+
contextWindowTokens,
|
|
1222
|
+
model.effectiveContextWindowTokens
|
|
1223
|
+
)
|
|
1224
|
+
},
|
|
1225
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }
|
|
1226
|
+
};
|
|
1146
1227
|
}
|
|
1147
1228
|
function configuredStaticUsageLimits(settings) {
|
|
1148
1229
|
return parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
|
|
@@ -1175,7 +1256,10 @@ function calculateModelUsageCostMicros(settings, model, usage) {
|
|
|
1175
1256
|
return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
1176
1257
|
}
|
|
1177
1258
|
function configuredAllowedReasoningEfforts(settings) {
|
|
1178
|
-
return uniqueValues([
|
|
1259
|
+
return uniqueValues([
|
|
1260
|
+
settings.openaiReasoningEffort,
|
|
1261
|
+
...splitCsv(settings.openaiAllowedReasoningEfforts)
|
|
1262
|
+
]).map((value) => ReasoningEffort.parse(value));
|
|
1179
1263
|
}
|
|
1180
1264
|
function environmentsEncryptionKeyBytes(settings) {
|
|
1181
1265
|
if (!settings.environmentsEncryptionKey) {
|
|
@@ -1183,7 +1267,9 @@ function environmentsEncryptionKeyBytes(settings) {
|
|
|
1183
1267
|
}
|
|
1184
1268
|
const decoded = Buffer.from(settings.environmentsEncryptionKey, "base64");
|
|
1185
1269
|
if (decoded.length !== 32) {
|
|
1186
|
-
throw new Error(
|
|
1270
|
+
throw new Error(
|
|
1271
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY must be base64 for exactly 32 bytes (generate with: openssl rand -base64 32)"
|
|
1272
|
+
);
|
|
1187
1273
|
}
|
|
1188
1274
|
return new Uint8Array(decoded);
|
|
1189
1275
|
}
|
|
@@ -1198,12 +1284,21 @@ function dbSearchPath(settings) {
|
|
|
1198
1284
|
return `${schema},opengeni_private,public`;
|
|
1199
1285
|
}
|
|
1200
1286
|
function collectGitIdentityEnvironment(settings) {
|
|
1201
|
-
return Object.fromEntries(
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1287
|
+
return Object.fromEntries(
|
|
1288
|
+
Object.entries({
|
|
1289
|
+
GIT_AUTHOR_NAME: settings.gitAuthorName,
|
|
1290
|
+
GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
|
|
1291
|
+
GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
|
|
1292
|
+
GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
|
|
1293
|
+
}).filter(
|
|
1294
|
+
(entry) => typeof entry[1] === "string" && entry[1].trim().length > 0
|
|
1295
|
+
)
|
|
1296
|
+
);
|
|
1297
|
+
}
|
|
1298
|
+
var DEFAULT_SANDBOX_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
1299
|
+
function prependPathEntry(pathValue, entry) {
|
|
1300
|
+
const parts = (pathValue ?? DEFAULT_SANDBOX_PATH).split(":").filter(Boolean);
|
|
1301
|
+
return [entry, ...parts.filter((part) => part !== entry)].join(":");
|
|
1207
1302
|
}
|
|
1208
1303
|
function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, options = {}) {
|
|
1209
1304
|
const environment = {
|
|
@@ -1215,18 +1310,35 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
|
|
|
1215
1310
|
if (settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local") {
|
|
1216
1311
|
environment.HOME ??= descriptor.workspaceRoot;
|
|
1217
1312
|
}
|
|
1218
|
-
|
|
1313
|
+
const provisionedGitHelperBackend = settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local" && settings.sandboxBackend !== "selfhosted";
|
|
1314
|
+
if (provisionedGitHelperBackend) {
|
|
1315
|
+
const home = environment.HOME ?? descriptor.workspaceRoot;
|
|
1316
|
+
environment.OPENGENI_GIT_CREDENTIALS_DIR ??= `${home}/.opengeni/git-credentials`;
|
|
1317
|
+
environment.OPENGENI_GIT_TOKEN_FILE ??= `${home}/.opengeni/git-token`;
|
|
1318
|
+
environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
|
|
1319
|
+
environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
|
|
1320
|
+
}
|
|
1219
1321
|
if (settings.toolspaceEnabled) {
|
|
1220
1322
|
environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
|
|
1221
1323
|
if (options.workspaceId) {
|
|
1222
|
-
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1324
|
+
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1325
|
+
settings,
|
|
1326
|
+
options.workspaceId
|
|
1327
|
+
);
|
|
1223
1328
|
}
|
|
1224
1329
|
}
|
|
1225
1330
|
return environment;
|
|
1226
1331
|
}
|
|
1227
1332
|
function hasGitHubRepositorySelection(resources) {
|
|
1228
1333
|
const positive = (value) => typeof value === "number" && Number.isInteger(value) && value > 0 || typeof value === "string" && /^\d+$/.test(value) && Number(value) > 0;
|
|
1229
|
-
return resources.some(
|
|
1334
|
+
return resources.some(
|
|
1335
|
+
(resource) => resource.kind === "repository" && (positive(resource.githubInstallationId) && positive(resource.githubRepositoryId) || resource.provider === "github" && positive(resource.installationId) && positive(resource.repositoryId))
|
|
1336
|
+
);
|
|
1337
|
+
}
|
|
1338
|
+
function hasGitCredentialRepositorySelection(resources) {
|
|
1339
|
+
return resources.some(
|
|
1340
|
+
(resource) => resource.kind === "repository" && (resource.provider === "github" || resource.provider === "gitlab" || resource.provider === "azure_devops" || hasGitHubRepositorySelection([resource]))
|
|
1341
|
+
);
|
|
1230
1342
|
}
|
|
1231
1343
|
function applyGitAuthPointerEnvironment(environment, identity) {
|
|
1232
1344
|
environment.GIT_ASKPASS = `${environment.HOME ?? "/workspace"}/.opengeni/askpass`;
|
|
@@ -1281,10 +1393,14 @@ function sandboxLifecycleHookIds(settings) {
|
|
|
1281
1393
|
return uniqueValues(ids);
|
|
1282
1394
|
}
|
|
1283
1395
|
function sandboxPreparationProfileNames(settings) {
|
|
1284
|
-
const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
|
|
1396
|
+
const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
|
|
1397
|
+
(value) => value.toLowerCase()
|
|
1398
|
+
);
|
|
1285
1399
|
if (profiles.includes("none")) {
|
|
1286
1400
|
if (profiles.length > 1) {
|
|
1287
|
-
throw new Error(
|
|
1401
|
+
throw new Error(
|
|
1402
|
+
"OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles"
|
|
1403
|
+
);
|
|
1288
1404
|
}
|
|
1289
1405
|
return ["none"];
|
|
1290
1406
|
}
|
|
@@ -1316,7 +1432,7 @@ function parseMcpServers(raw) {
|
|
|
1316
1432
|
return parsed;
|
|
1317
1433
|
} catch (error) {
|
|
1318
1434
|
const message = error instanceof Error ? error.message : String(error);
|
|
1319
|
-
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}
|
|
1435
|
+
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, { cause: error });
|
|
1320
1436
|
}
|
|
1321
1437
|
}
|
|
1322
1438
|
function parseModelPricingJson(raw) {
|
|
@@ -1328,7 +1444,7 @@ function parseModelPricingJson(raw) {
|
|
|
1328
1444
|
parsed = JSON.parse(raw);
|
|
1329
1445
|
} catch (error) {
|
|
1330
1446
|
const message = error instanceof Error ? error.message : String(error);
|
|
1331
|
-
throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}
|
|
1447
|
+
throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`, { cause: error });
|
|
1332
1448
|
}
|
|
1333
1449
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1334
1450
|
throw new Error("OPENGENI_MODEL_PRICING_JSON must be a JSON object keyed by model name");
|
|
@@ -1351,19 +1467,28 @@ function parseSandboxWarmRateJson(raw) {
|
|
|
1351
1467
|
parsed = JSON.parse(raw);
|
|
1352
1468
|
} catch (error) {
|
|
1353
1469
|
const message = error instanceof Error ? error.message : String(error);
|
|
1354
|
-
throw new Error(
|
|
1470
|
+
throw new Error(
|
|
1471
|
+
`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be valid JSON: ${message}`,
|
|
1472
|
+
{ cause: error }
|
|
1473
|
+
);
|
|
1355
1474
|
}
|
|
1356
1475
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1357
|
-
throw new Error(
|
|
1476
|
+
throw new Error(
|
|
1477
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be a JSON object keyed by backend name"
|
|
1478
|
+
);
|
|
1358
1479
|
}
|
|
1359
1480
|
const out = {};
|
|
1360
1481
|
for (const [backend, value] of Object.entries(parsed)) {
|
|
1361
1482
|
if (!backend.trim()) {
|
|
1362
|
-
throw new Error(
|
|
1483
|
+
throw new Error(
|
|
1484
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name"
|
|
1485
|
+
);
|
|
1363
1486
|
}
|
|
1364
1487
|
const rate = typeof value === "number" ? value : Number(value);
|
|
1365
1488
|
if (!Number.isFinite(rate) || rate < 0) {
|
|
1366
|
-
throw new Error(
|
|
1489
|
+
throw new Error(
|
|
1490
|
+
`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON rate for ${backend} must be a non-negative number`
|
|
1491
|
+
);
|
|
1367
1492
|
}
|
|
1368
1493
|
out[backend] = rate;
|
|
1369
1494
|
}
|
|
@@ -1382,7 +1507,9 @@ function parseModelProvidersJson(raw) {
|
|
|
1382
1507
|
parsed = JSON.parse(raw);
|
|
1383
1508
|
} catch (error) {
|
|
1384
1509
|
const message = error instanceof Error ? error.message : String(error);
|
|
1385
|
-
throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}
|
|
1510
|
+
throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`, {
|
|
1511
|
+
cause: error
|
|
1512
|
+
});
|
|
1386
1513
|
}
|
|
1387
1514
|
if (!Array.isArray(parsed)) {
|
|
1388
1515
|
throw new Error("OPENGENI_MODEL_PROVIDERS_JSON must be a JSON array of providers");
|
|
@@ -1390,7 +1517,9 @@ function parseModelProvidersJson(raw) {
|
|
|
1390
1517
|
return parsed.map((entry, index) => {
|
|
1391
1518
|
const result = RegistryProviderSchema.safeParse(entry);
|
|
1392
1519
|
if (!result.success) {
|
|
1393
|
-
throw new Error(
|
|
1520
|
+
throw new Error(
|
|
1521
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
|
|
1522
|
+
);
|
|
1394
1523
|
}
|
|
1395
1524
|
return result.data;
|
|
1396
1525
|
});
|
|
@@ -1404,10 +1533,14 @@ function parseIntegrationsOauthClientsJson(raw) {
|
|
|
1404
1533
|
parsed = JSON.parse(raw);
|
|
1405
1534
|
} catch (error) {
|
|
1406
1535
|
const message = error instanceof Error ? error.message : String(error);
|
|
1407
|
-
throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}
|
|
1536
|
+
throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`, {
|
|
1537
|
+
cause: error
|
|
1538
|
+
});
|
|
1408
1539
|
}
|
|
1409
1540
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1410
|
-
throw new Error(
|
|
1541
|
+
throw new Error(
|
|
1542
|
+
"OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be a JSON object keyed by authorization-server issuer or URL"
|
|
1543
|
+
);
|
|
1411
1544
|
}
|
|
1412
1545
|
const out = {};
|
|
1413
1546
|
for (const [key, value] of Object.entries(parsed)) {
|
|
@@ -1416,7 +1549,9 @@ function parseIntegrationsOauthClientsJson(raw) {
|
|
|
1416
1549
|
}
|
|
1417
1550
|
const result = IntegrationOAuthClientConfigSchema.safeParse(value);
|
|
1418
1551
|
if (!result.success) {
|
|
1419
|
-
throw new Error(
|
|
1552
|
+
throw new Error(
|
|
1553
|
+
`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`
|
|
1554
|
+
);
|
|
1420
1555
|
}
|
|
1421
1556
|
out[key] = result.data;
|
|
1422
1557
|
}
|
|
@@ -1431,7 +1566,9 @@ function parseStaticUsageLimitsJson(raw) {
|
|
|
1431
1566
|
parsed = JSON.parse(raw);
|
|
1432
1567
|
} catch (error) {
|
|
1433
1568
|
const message = error instanceof Error ? error.message : String(error);
|
|
1434
|
-
throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}
|
|
1569
|
+
throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`, {
|
|
1570
|
+
cause: error
|
|
1571
|
+
});
|
|
1435
1572
|
}
|
|
1436
1573
|
return StaticUsageLimits.parse(parsed);
|
|
1437
1574
|
}
|
|
@@ -1444,7 +1581,9 @@ function parseStaticEntitlementsJson(raw) {
|
|
|
1444
1581
|
parsed = JSON.parse(raw);
|
|
1445
1582
|
} catch (error) {
|
|
1446
1583
|
const message = error instanceof Error ? error.message : String(error);
|
|
1447
|
-
throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}
|
|
1584
|
+
throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`, {
|
|
1585
|
+
cause: error
|
|
1586
|
+
});
|
|
1448
1587
|
}
|
|
1449
1588
|
return Entitlements.parse(parsed);
|
|
1450
1589
|
}
|
|
@@ -1491,20 +1630,32 @@ function ensureBuiltInMcpServers(settings) {
|
|
|
1491
1630
|
// safe to cache / leave as-is.)
|
|
1492
1631
|
cacheToolsList: false
|
|
1493
1632
|
},
|
|
1494
|
-
...hasFiles ? [] : [
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1633
|
+
...hasFiles ? [] : [
|
|
1634
|
+
{
|
|
1635
|
+
id: "files",
|
|
1636
|
+
name: "Files",
|
|
1637
|
+
url: firstPartyMcpUrl,
|
|
1638
|
+
allowedTools: ["files_get_download_url"],
|
|
1639
|
+
cacheToolsList: true
|
|
1640
|
+
}
|
|
1641
|
+
],
|
|
1642
|
+
...hasDocs ? [] : [
|
|
1643
|
+
{
|
|
1644
|
+
id: "docs",
|
|
1645
|
+
name: "Document Search",
|
|
1646
|
+
url: firstPartyDocsMcpUrl,
|
|
1647
|
+
allowedTools: [
|
|
1648
|
+
"search_documents",
|
|
1649
|
+
"fetch_document_chunk",
|
|
1650
|
+
"list_document_bases",
|
|
1651
|
+
"knowledge_search",
|
|
1652
|
+
"knowledge_fetch",
|
|
1653
|
+
"memory_search",
|
|
1654
|
+
"memory_propose"
|
|
1655
|
+
],
|
|
1656
|
+
cacheToolsList: false
|
|
1657
|
+
}
|
|
1658
|
+
],
|
|
1508
1659
|
...existing
|
|
1509
1660
|
];
|
|
1510
1661
|
}
|
|
@@ -1534,40 +1685,58 @@ function validateSettings(settings) {
|
|
|
1534
1685
|
}
|
|
1535
1686
|
if (settings.productAccessMode === "managed") {
|
|
1536
1687
|
if (!settings.publicBaseUrl) {
|
|
1537
|
-
throw new Error(
|
|
1688
|
+
throw new Error(
|
|
1689
|
+
"OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1690
|
+
);
|
|
1538
1691
|
}
|
|
1539
1692
|
if (!settings.betterAuthSecret) {
|
|
1540
|
-
throw new Error(
|
|
1693
|
+
throw new Error(
|
|
1694
|
+
"OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1695
|
+
);
|
|
1541
1696
|
}
|
|
1542
1697
|
if (!settings.delegationSecret) {
|
|
1543
|
-
throw new Error(
|
|
1698
|
+
throw new Error(
|
|
1699
|
+
"OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1700
|
+
);
|
|
1544
1701
|
}
|
|
1545
1702
|
if (!["local", "test"].includes(settings.environment) && !settings.resendApiKey) {
|
|
1546
1703
|
throw new Error("OPENGENI_RESEND_API_KEY is required for managed mode outside local/test");
|
|
1547
1704
|
}
|
|
1548
1705
|
if (!["local", "test"].includes(settings.environment) && !settings.environmentsEncryptionKey) {
|
|
1549
|
-
throw new Error(
|
|
1706
|
+
throw new Error(
|
|
1707
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test"
|
|
1708
|
+
);
|
|
1550
1709
|
}
|
|
1551
1710
|
}
|
|
1552
1711
|
environmentsEncryptionKeyBytes(settings);
|
|
1553
1712
|
if (settings.integrationsEnabled) {
|
|
1554
1713
|
if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
|
|
1555
|
-
throw new Error(
|
|
1714
|
+
throw new Error(
|
|
1715
|
+
"OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_INTEGRATIONS_ENABLED=true and OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1716
|
+
);
|
|
1556
1717
|
}
|
|
1557
1718
|
if (settings.publicBaseUrl && !settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
1558
|
-
throw new Error(
|
|
1719
|
+
throw new Error(
|
|
1720
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
|
|
1721
|
+
);
|
|
1559
1722
|
}
|
|
1560
1723
|
if (!settings.integrationsStateSecret && !["local", "test"].includes(settings.environment)) {
|
|
1561
|
-
throw new Error(
|
|
1724
|
+
throw new Error(
|
|
1725
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
|
|
1726
|
+
);
|
|
1562
1727
|
}
|
|
1563
1728
|
}
|
|
1564
1729
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
1565
1730
|
if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
|
|
1566
|
-
throw new Error(
|
|
1731
|
+
throw new Error(
|
|
1732
|
+
"OPENGENI_PRODUCT_ACCESS_MODE=configured requires OPENGENI_DELEGATION_SECRET or OPENGENI_AUTH_REQUIRED=true outside local/test"
|
|
1733
|
+
);
|
|
1567
1734
|
}
|
|
1568
1735
|
if (settings.billingMode === "stripe") {
|
|
1569
1736
|
if (!settings.stripeSecretKey || !settings.stripeWebhookSecret) {
|
|
1570
|
-
throw new Error(
|
|
1737
|
+
throw new Error(
|
|
1738
|
+
"OPENGENI_STRIPE_SECRET_KEY and OPENGENI_STRIPE_WEBHOOK_SECRET are required when OPENGENI_BILLING_MODE=stripe"
|
|
1739
|
+
);
|
|
1571
1740
|
}
|
|
1572
1741
|
}
|
|
1573
1742
|
if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
|
|
@@ -1577,13 +1746,17 @@ function validateSettings(settings) {
|
|
|
1577
1746
|
const pricing = configuredModelPricing(settings);
|
|
1578
1747
|
const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
|
|
1579
1748
|
if (missing.length > 0) {
|
|
1580
|
-
throw new Error(
|
|
1749
|
+
throw new Error(
|
|
1750
|
+
`Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
|
|
1751
|
+
);
|
|
1581
1752
|
}
|
|
1582
1753
|
}
|
|
1583
1754
|
if (settings.usageLimitsMode === "static") {
|
|
1584
1755
|
const limits = configuredStaticUsageLimits(settings);
|
|
1585
1756
|
if (Object.keys(limits).length === 0) {
|
|
1586
|
-
throw new Error(
|
|
1757
|
+
throw new Error(
|
|
1758
|
+
"OPENGENI_STATIC_USAGE_LIMITS_JSON must define at least one cap when OPENGENI_USAGE_LIMITS_MODE=static"
|
|
1759
|
+
);
|
|
1587
1760
|
}
|
|
1588
1761
|
} else {
|
|
1589
1762
|
parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
|
|
@@ -1591,7 +1764,9 @@ function validateSettings(settings) {
|
|
|
1591
1764
|
if (settings.entitlementsMode === "static") {
|
|
1592
1765
|
const entitlements = parseStaticEntitlementsJson(settings.staticEntitlementsJson);
|
|
1593
1766
|
if (Object.keys(entitlements).length === 0) {
|
|
1594
|
-
throw new Error(
|
|
1767
|
+
throw new Error(
|
|
1768
|
+
"OPENGENI_STATIC_ENTITLEMENTS_JSON must define at least one feature when OPENGENI_ENTITLEMENTS_MODE=static"
|
|
1769
|
+
);
|
|
1595
1770
|
}
|
|
1596
1771
|
} else {
|
|
1597
1772
|
parseStaticEntitlementsJson(settings.staticEntitlementsJson);
|
|
@@ -1601,7 +1776,9 @@ function validateSettings(settings) {
|
|
|
1601
1776
|
}
|
|
1602
1777
|
if (settings.openaiProvider === "azure") {
|
|
1603
1778
|
if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiEndpoint) {
|
|
1604
|
-
throw new Error(
|
|
1779
|
+
throw new Error(
|
|
1780
|
+
"Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT"
|
|
1781
|
+
);
|
|
1605
1782
|
}
|
|
1606
1783
|
if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiDeployment) {
|
|
1607
1784
|
throw new Error("Azure OpenAI endpoint mode requires OPENGENI_AZURE_OPENAI_DEPLOYMENT");
|
|
@@ -1614,52 +1791,76 @@ function validateSettings(settings) {
|
|
|
1614
1791
|
}
|
|
1615
1792
|
}
|
|
1616
1793
|
if (Boolean(settings.modalTokenId) !== Boolean(settings.modalTokenSecret)) {
|
|
1617
|
-
throw new Error(
|
|
1794
|
+
throw new Error(
|
|
1795
|
+
"OPENGENI_MODAL_TOKEN_ID and OPENGENI_MODAL_TOKEN_SECRET must both be set or both omitted"
|
|
1796
|
+
);
|
|
1618
1797
|
}
|
|
1619
1798
|
for (const required of SANDBOX_REQUIRED_ENV[settings.sandboxBackend] ?? []) {
|
|
1620
1799
|
const value = settings[required.field];
|
|
1621
1800
|
if (value === void 0 || value === null || typeof value === "string" && value.trim().length === 0) {
|
|
1622
|
-
throw new Error(
|
|
1801
|
+
throw new Error(
|
|
1802
|
+
`${required.env} is required when OPENGENI_SANDBOX_BACKEND=${settings.sandboxBackend}`
|
|
1803
|
+
);
|
|
1623
1804
|
}
|
|
1624
1805
|
}
|
|
1625
1806
|
if (settings.objectStorageBackend === "s3-compatible" || settings.objectStorageBackend === "aws-s3") {
|
|
1626
1807
|
if (Boolean(settings.objectStorageAccessKeyId) !== Boolean(settings.objectStorageSecretAccessKey)) {
|
|
1627
|
-
throw new Error(
|
|
1808
|
+
throw new Error(
|
|
1809
|
+
"OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
|
|
1810
|
+
);
|
|
1628
1811
|
}
|
|
1629
1812
|
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
1630
|
-
throw new Error(
|
|
1813
|
+
throw new Error(
|
|
1814
|
+
"S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
|
|
1815
|
+
);
|
|
1631
1816
|
}
|
|
1632
1817
|
if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
|
|
1633
|
-
throw new Error(
|
|
1818
|
+
throw new Error(
|
|
1819
|
+
"S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
|
|
1820
|
+
);
|
|
1634
1821
|
}
|
|
1635
1822
|
if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
|
|
1636
|
-
throw new Error(
|
|
1823
|
+
throw new Error(
|
|
1824
|
+
"S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
|
|
1825
|
+
);
|
|
1637
1826
|
}
|
|
1638
1827
|
} else if (settings.objectStorageBackend === "azure-blob") {
|
|
1639
1828
|
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
1640
|
-
throw new Error(
|
|
1829
|
+
throw new Error(
|
|
1830
|
+
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
|
|
1831
|
+
);
|
|
1641
1832
|
}
|
|
1642
1833
|
if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
|
|
1643
|
-
throw new Error(
|
|
1834
|
+
throw new Error(
|
|
1835
|
+
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
|
|
1836
|
+
);
|
|
1644
1837
|
}
|
|
1645
1838
|
const hasConnectionString = Boolean(settings.objectStorageAzureConnectionString);
|
|
1646
1839
|
const hasSharedKey = Boolean(settings.objectStorageAzureAccountName) && Boolean(settings.objectStorageAzureAccountKey);
|
|
1647
1840
|
if (!hasConnectionString && !hasSharedKey) {
|
|
1648
|
-
throw new Error(
|
|
1841
|
+
throw new Error(
|
|
1842
|
+
"Azure Blob storage requires OPENGENI_OBJECT_STORAGE_AZURE_CONNECTION_STRING or OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_NAME plus OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_KEY"
|
|
1843
|
+
);
|
|
1649
1844
|
}
|
|
1650
1845
|
} else {
|
|
1651
1846
|
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
1652
|
-
throw new Error(
|
|
1847
|
+
throw new Error(
|
|
1848
|
+
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
|
|
1849
|
+
);
|
|
1653
1850
|
}
|
|
1654
1851
|
if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
|
|
1655
|
-
throw new Error(
|
|
1852
|
+
throw new Error(
|
|
1853
|
+
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
|
|
1854
|
+
);
|
|
1656
1855
|
}
|
|
1657
1856
|
if (settings.objectStorageGcsCredentialsJson) {
|
|
1658
1857
|
parseGcsCredentialsJson(settings.objectStorageGcsCredentialsJson);
|
|
1659
1858
|
}
|
|
1660
1859
|
}
|
|
1661
1860
|
if (settings.documentChunkOverlap >= settings.documentChunkSize) {
|
|
1662
|
-
throw new Error(
|
|
1861
|
+
throw new Error(
|
|
1862
|
+
"OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE"
|
|
1863
|
+
);
|
|
1663
1864
|
}
|
|
1664
1865
|
parseExposedPorts(settings.dockerExposedPorts);
|
|
1665
1866
|
sandboxEnvironmentVariableNames(settings);
|
|
@@ -1709,14 +1910,20 @@ function validateSettings(settings) {
|
|
|
1709
1910
|
const providerIds = /* @__PURE__ */ new Set();
|
|
1710
1911
|
for (const provider of registryProviders) {
|
|
1711
1912
|
if (provider.id === builtinId) {
|
|
1712
|
-
throw new Error(
|
|
1913
|
+
throw new Error(
|
|
1914
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
|
|
1915
|
+
);
|
|
1713
1916
|
}
|
|
1714
1917
|
if (providerIds.has(provider.id)) {
|
|
1715
|
-
throw new Error(
|
|
1918
|
+
throw new Error(
|
|
1919
|
+
`OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`
|
|
1920
|
+
);
|
|
1716
1921
|
}
|
|
1717
1922
|
providerIds.add(provider.id);
|
|
1718
1923
|
if (!resolveProviderApiKey(provider)) {
|
|
1719
|
-
throw new Error(
|
|
1924
|
+
throw new Error(
|
|
1925
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
|
|
1926
|
+
);
|
|
1720
1927
|
}
|
|
1721
1928
|
}
|
|
1722
1929
|
}
|
|
@@ -1794,7 +2001,9 @@ function parseGcsCredentialsJson(raw) {
|
|
|
1794
2001
|
return JSON.parse(raw);
|
|
1795
2002
|
} catch (error) {
|
|
1796
2003
|
const message = error instanceof Error ? error.message : String(error);
|
|
1797
|
-
throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}
|
|
2004
|
+
throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`, {
|
|
2005
|
+
cause: error
|
|
2006
|
+
});
|
|
1798
2007
|
}
|
|
1799
2008
|
}
|
|
1800
2009
|
function delay(ms) {
|
|
@@ -1809,6 +2018,7 @@ export {
|
|
|
1809
2018
|
RegistryProviderKind,
|
|
1810
2019
|
SANDBOX_REQUIRED_ENV,
|
|
1811
2020
|
applyGitAuthPointerEnvironment,
|
|
2021
|
+
builtinProviderId,
|
|
1812
2022
|
calculateModelUsageCostMicros,
|
|
1813
2023
|
collectGitIdentityEnvironment,
|
|
1814
2024
|
collectSandboxEnvironment,
|
|
@@ -1820,7 +2030,6 @@ export {
|
|
|
1820
2030
|
configuredProviders,
|
|
1821
2031
|
configuredStaticUsageLimits,
|
|
1822
2032
|
contextInputBudgetTokens,
|
|
1823
|
-
contextServerCompactThreshold,
|
|
1824
2033
|
dbSearchPath,
|
|
1825
2034
|
defaultModelPricing,
|
|
1826
2035
|
effectiveModalIdleTimeoutSeconds,
|
|
@@ -1828,6 +2037,7 @@ export {
|
|
|
1828
2037
|
firstPartyMcpBaseUrl,
|
|
1829
2038
|
firstPartyMcpWorkspaceUrl,
|
|
1830
2039
|
getSettings,
|
|
2040
|
+
hasGitCredentialRepositorySelection,
|
|
1831
2041
|
hasGitHubRepositorySelection,
|
|
1832
2042
|
parseExposedPorts,
|
|
1833
2043
|
parseIntegrationsOauthClientsJson,
|
|
@@ -1837,8 +2047,8 @@ export {
|
|
|
1837
2047
|
parseSandboxWarmRateJson,
|
|
1838
2048
|
parseStaticEntitlementsJson,
|
|
1839
2049
|
parseStaticUsageLimitsJson,
|
|
2050
|
+
policyProviderIdForModel,
|
|
1840
2051
|
requiredSandboxEnvForBackend,
|
|
1841
|
-
resolveContextCompactionMode,
|
|
1842
2052
|
resolveEnrollmentSigningSecret,
|
|
1843
2053
|
resolveModelProvider,
|
|
1844
2054
|
resolveNatsCalloutConfig,
|
|
@@ -1851,6 +2061,7 @@ export {
|
|
|
1851
2061
|
sandboxLifecycleHookIds,
|
|
1852
2062
|
sandboxPreparationProfiles,
|
|
1853
2063
|
sandboxWarmRateMicrosPerSecond,
|
|
2064
|
+
settingsWithResolvedModelContext,
|
|
1854
2065
|
stableSandboxEnvironmentForRun,
|
|
1855
2066
|
startupRetryOptions,
|
|
1856
2067
|
streamTokenDegraded
|