@opengeni/config 0.4.0 → 0.5.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +77 -58
- package/dist/index.js +410 -204
- package/dist/index.js.map +1 -1
- package/package.json +11 -11
- package/src/index.ts +710 -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(" ");
|
|
@@ -151,57 +151,33 @@ var SettingsSchema = z.object({
|
|
|
151
151
|
// above; the graceful max-turns valve (idle + goal continuation, never a
|
|
152
152
|
// session failure) remains as inert safety should a deployment set a cap.
|
|
153
153
|
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.
|
|
154
|
+
// The model family's real context window in tokens. OpenGeni always performs
|
|
155
|
+
// one durable, portable plaintext compaction transition; there is no
|
|
156
|
+
// provider/server/off mode ladder.
|
|
177
157
|
contextWindowTokens: z.coerce.number().int().positive().default(105e4),
|
|
158
|
+
// Optional model-catalog effective input ceiling. Codex models expose this as
|
|
159
|
+
// raw context_window * effective_context_window_percent; when absent, retain
|
|
160
|
+
// the deployment-level window-minus-reserved-output behavior.
|
|
161
|
+
contextEffectiveWindowTokens: z.coerce.number().int().positive().optional(),
|
|
178
162
|
// Proactive compaction threshold as a ratio of the model context window.
|
|
179
|
-
// Defaults to
|
|
180
|
-
//
|
|
181
|
-
//
|
|
182
|
-
|
|
163
|
+
// Defaults to 90%: compact as late as possible — retained context beats early
|
|
164
|
+
// headroom now that per-model windows are declared honestly (input-effective,
|
|
165
|
+
// empirically measured), and the fail-closed reactive compact-on-reject path
|
|
166
|
+
// absorbs any overshoot as one retried call rather than a dead session.
|
|
167
|
+
// Clamped to [0.3, 0.9] so deployments can tune the trigger without
|
|
168
|
+
// accidentally disabling compaction.
|
|
169
|
+
contextCompactionThresholdRatio: z.coerce.number().default(0.9).transform((value) => {
|
|
183
170
|
if (!Number.isFinite(value)) {
|
|
184
|
-
return 0.
|
|
171
|
+
return 0.9;
|
|
185
172
|
}
|
|
186
173
|
return Math.min(0.9, Math.max(0.3, value));
|
|
187
174
|
}),
|
|
188
175
|
// Tokens reserved for model output; subtracted from the window to get the
|
|
189
176
|
// usable input budget B = contextWindowTokens - contextReservedOutputTokens.
|
|
190
177
|
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),
|
|
178
|
+
// Model-catalog auto-compact limit. When present it is clamped to
|
|
179
|
+
// 90% of the raw window, matching Codex core's auto_compact_token_limit().
|
|
180
|
+
contextAutoCompactThresholdTokens: z.coerce.number().int().positive().optional(),
|
|
205
181
|
authRequired: EnvBoolean.default(false),
|
|
206
182
|
accessKey: z.string().optional(),
|
|
207
183
|
authAllowHealth: EnvBoolean.default(true),
|
|
@@ -214,8 +190,8 @@ var SettingsSchema = z.object({
|
|
|
214
190
|
openaiProvider: z.enum(["openai", "azure"]).default("openai"),
|
|
215
191
|
openaiApiKey: z.string().optional(),
|
|
216
192
|
openaiBaseUrl: z.string().optional(),
|
|
217
|
-
openaiModel: z.string().default("gpt-5.
|
|
218
|
-
openaiAllowedModels: z.string().default("gpt-5.
|
|
193
|
+
openaiModel: z.string().default("gpt-5.6-sol"),
|
|
194
|
+
openaiAllowedModels: z.string().default("gpt-5.6-sol,gpt-5.6-terra,gpt-5.6-luna"),
|
|
219
195
|
modelPricingJson: z.string().default("{}"),
|
|
220
196
|
// Extra (non-built-in) model providers, declared by the host as a JSON
|
|
221
197
|
// provider registry. Each entry carries its own base URL, API key, wire API
|
|
@@ -237,6 +213,11 @@ var SettingsSchema = z.object({
|
|
|
237
213
|
// tool that BM25-discloses only the matching connectors. Default OFF — a codex
|
|
238
214
|
// turn is byte-for-byte unchanged until enabled. OPENGENI_CODEX_TOOL_SEARCH_ENABLED
|
|
239
215
|
codexToolSearchEnabled: EnvBoolean.default(false),
|
|
216
|
+
// OPE-21 atomic, workspace-local credential allocation. Default OFF is a
|
|
217
|
+
// deliberate rolling-deploy fence: migrate + roll every worker first, then
|
|
218
|
+
// enable. Turning it off restores the legacy sticky selector without a schema
|
|
219
|
+
// rollback; the additive lease table/cursor columns become inert.
|
|
220
|
+
codexCredentialLeasingEnabled: EnvBoolean.default(false),
|
|
240
221
|
// Multi-account P3 (auto-rotation): an account is "near exhaustion" — ineligible to be
|
|
241
222
|
// rotated TO — when EITHER usage window (5h/weekly) is at/over this percent. Default 90 to
|
|
242
223
|
// match the UI danger flip (UsageBar danger at pct >= 90). OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT.
|
|
@@ -274,7 +255,7 @@ var SettingsSchema = z.object({
|
|
|
274
255
|
// the non-bypassable CORE at AGENT_INSTRUCTIONS_CORE_PLACEHOLDER (or appends
|
|
275
256
|
// it when the template omits the marker), and uses the result as the agent's
|
|
276
257
|
// instructions. Defaulting to DEFAULT_AGENT_INSTRUCTIONS keeps the composed
|
|
277
|
-
// default
|
|
258
|
+
// default pinned by runtime tests.
|
|
278
259
|
agentInstructionsTemplate: z.string().default(DEFAULT_AGENT_INSTRUCTIONS),
|
|
279
260
|
azureOpenaiBaseUrl: z.string().optional(),
|
|
280
261
|
azureOpenaiEndpoint: z.string().optional(),
|
|
@@ -379,6 +360,13 @@ var SettingsSchema = z.object({
|
|
|
379
360
|
// recordingMaxSeconds is the ffmpeg -t hard ceiling (bounds a multi-day turn).
|
|
380
361
|
recordingEnabled: EnvBoolean.default(true),
|
|
381
362
|
recordingDefaultCodec: z.enum(["h264-mp4", "vp9-webm"]).default("h264-mp4"),
|
|
363
|
+
// Workbench v2 turn-end workspace capture (dossier §10.1). When on, the turn
|
|
364
|
+
// activity probes the box's changed files off the live box at turn end and
|
|
365
|
+
// persists a capture revision (blobs in @opengeni/storage) so the workbench
|
|
366
|
+
// paints cold/offline sessions with zero machine round-trips. Best-effort and
|
|
367
|
+
// fully behind this flag: off ⇒ capture is skipped and reads fall back to the
|
|
368
|
+
// live/wake path (status-quo behavior). Default on; explicit per environment.
|
|
369
|
+
workspaceCaptureEnabled: EnvBoolean.default(true),
|
|
382
370
|
recordingFramerate: z.coerce.number().int().positive().default(15),
|
|
383
371
|
recordingMaxSeconds: z.coerce.number().int().positive().default(600),
|
|
384
372
|
recordingMaxBytes: z.coerce.number().int().positive().default(268435456),
|
|
@@ -435,6 +423,19 @@ var SettingsSchema = z.object({
|
|
|
435
423
|
// EnvBoolean (NOT z.coerce.boolean(), which would coerce "false" -> true and
|
|
436
424
|
// turn the flag ON the moment anyone set the env var to disable it).
|
|
437
425
|
sandboxOwnershipEnabled: EnvBoolean.default(false),
|
|
426
|
+
// --- lazy sandbox provisioning rollout flag, default OFF ---
|
|
427
|
+
// Only effective when sandboxOwnershipEnabled is ALSO on (lazy provisioning is a
|
|
428
|
+
// property of the owned path — the SDK never creates/resumes an injected session,
|
|
429
|
+
// so we control when the box is established). When TRUE, a turn does NOT provision
|
|
430
|
+
// its box at turn start: the lease acquire + resume-by-id + hooks + downloads +
|
|
431
|
+
// heartbeat + recording are deferred to an in-process single-flight provisioner
|
|
432
|
+
// that runs the FIRST time a sandbox op is dispatched (via the routing proxy's
|
|
433
|
+
// resolveActiveBackend). A turn whose model never calls a sandbox-backed tool ends
|
|
434
|
+
// with NO lease row and ZERO warm-seconds. When FALSE (or ownership off) the turn
|
|
435
|
+
// provisions eagerly exactly as today — byte-for-byte. EnvBoolean (NOT
|
|
436
|
+
// z.coerce.boolean(), which coerces "false" -> true and would turn the flag ON the
|
|
437
|
+
// moment anyone set the env var to disable it).
|
|
438
|
+
sandboxLazyProvisionEnabled: EnvBoolean.default(false),
|
|
438
439
|
// --- bring-your-own-compute (selfhosted 11th backend) rollout flag, default OFF ---
|
|
439
440
|
// The keystone flag for the whole selfhosted feature (the enrollment device-flow,
|
|
440
441
|
// the NATS control plane, the relay stream tier). When FALSE the enrollment routes
|
|
@@ -443,6 +444,11 @@ var SettingsSchema = z.object({
|
|
|
443
444
|
// z.coerce.boolean(), which coerces "false" -> true). Flipped per-environment via
|
|
444
445
|
// the deploy-staging IaC secret/configmap pattern (dossier §17/§25.1).
|
|
445
446
|
sandboxSelfhostedEnabled: EnvBoolean.default(false),
|
|
447
|
+
// Gates the op-stream (streaming exec) transport to Connected Machines. The
|
|
448
|
+
// runner must ALSO advertise Capabilities.op_stream; default off, and legacy
|
|
449
|
+
// request/reply exec is the permanent fallback. EnvBoolean (NOT
|
|
450
|
+
// z.coerce.boolean(), which coerces "false" -> true).
|
|
451
|
+
agentOpStreamEnabled: EnvBoolean.default(false),
|
|
446
452
|
// The HMAC secret the control plane signs the enrollment bearer credential with
|
|
447
453
|
// (the `oge_` envelope the agent presents back to the control plane). Optional:
|
|
448
454
|
// when ABSENT and sandboxSelfhostedEnabled is on, the poll route reports the
|
|
@@ -499,6 +505,25 @@ var SettingsSchema = z.object({
|
|
|
499
505
|
// bus connects anonymously (local dev / a NATS with no auth_callout).
|
|
500
506
|
selfhostedNatsControlUser: z.string().optional(),
|
|
501
507
|
selfhostedNatsControlPassword: z.string().optional(),
|
|
508
|
+
// --- selfhosted (Connected Machine) control/exec op deadlines ---------------
|
|
509
|
+
// The control plane splits its op deadline in two. CONTROL ops (ping / fs / git /
|
|
510
|
+
// desktop / pty) must stay responsive so a machine's liveness is never masked by a
|
|
511
|
+
// slow op, so they use the short control timeout. EXEC gets its OWN, larger budget:
|
|
512
|
+
// a real command (compile, test run, dependency install) routinely outlives the
|
|
513
|
+
// control timeout, and before the split a long command was killed at the ~30s
|
|
514
|
+
// control wall. The agent kills the exec child at this deadline; the wire waits
|
|
515
|
+
// slightly longer (SELFHOSTED_EXEC_REPLY_GRACE_MS) for the typed timed-out reply.
|
|
516
|
+
//
|
|
517
|
+
// The exec default is a DELIBERATELY MODEST 2min (not 5): the agent-side admission
|
|
518
|
+
// pool is (until a later agent release) a FLAT 8 permits with no per-class split,
|
|
519
|
+
// so 8 slow execs holding a permit for 5 minutes would blanket-DRAIN every fs/git
|
|
520
|
+
// op — shipping the amplifier before the class-aware-admission fix. 2min still
|
|
521
|
+
// clears the large majority of the observed >30s exec tail; genuinely long jobs run
|
|
522
|
+
// in the background (see the exec-deadline hint) or raise the knob per deployment.
|
|
523
|
+
// Knobs: OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS (default 2min) and
|
|
524
|
+
// OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS (default 30s).
|
|
525
|
+
sandboxSelfhostedExecTimeoutMs: z.coerce.number().int().positive().default(12e4),
|
|
526
|
+
sandboxSelfhostedControlTimeoutMs: z.coerce.number().int().positive().default(3e4),
|
|
502
527
|
// --- sandbox lease cadences (cadence invariant validated at boot below) ---
|
|
503
528
|
// reaperPeriod < viewerHolderTTL, and reaperPeriod + idleGrace < the EFFECTIVE
|
|
504
529
|
// box idle timeout (effectiveModalIdleTimeoutSeconds, which defaults to the hard
|
|
@@ -517,6 +542,22 @@ var SettingsSchema = z.object({
|
|
|
517
542
|
// fresh EMPTY box; lower it to trade warm cost for a snappier reclaim. Knob:
|
|
518
543
|
// OPENGENI_SANDBOX_IDLE_GRACE_MS.
|
|
519
544
|
sandboxIdleGraceMs: z.coerce.number().int().positive().default(9e5),
|
|
545
|
+
// MID-SESSION /workspace snapshot cadence (sandbox-file-persistence). The
|
|
546
|
+
// reaper's drain-persist only protects boxes the reaper itself kills; a box
|
|
547
|
+
// that dies any other way (Modal's hard creation-time timeout on a session
|
|
548
|
+
// busy past it, provider OOM/infra death) loses everything since the last
|
|
549
|
+
// clean drain. While a turn holds the box, the turn heartbeat and turn-end
|
|
550
|
+
// both take a snapshot when at least this interval has passed since the last
|
|
551
|
+
// one (same epoch-fenced fold-onto-lease seam as the drain), bounding the
|
|
552
|
+
// worst-case loss of ANY unclean box death to this window. 0 disables.
|
|
553
|
+
// Knob: OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS. Default 15min.
|
|
554
|
+
sandboxSnapshotIntervalMs: z.coerce.number().int().min(0).default(9e5),
|
|
555
|
+
// Maximum time a best-effort /workspace snapshot capture may hold turn/reaper
|
|
556
|
+
// cleanup. A hung provider snapshot must never pin a lease holder, block
|
|
557
|
+
// graceful shutdown, or become permission to GC an older archive. Timeout is
|
|
558
|
+
// treated exactly like a failed best-effort snapshot. Knob:
|
|
559
|
+
// OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS. Default 60s.
|
|
560
|
+
sandboxSnapshotTimeoutMs: z.coerce.number().int().positive().default(6e4),
|
|
520
561
|
// expires_at refresh window for a held lease (>> the turn 10s heartbeat so a
|
|
521
562
|
// single missed heartbeat never TTL-reaps a live turn). The warming TTL is the
|
|
522
563
|
// window a cold->warming spawner has to commit warm before a reaper resets it.
|
|
@@ -526,6 +567,11 @@ var SettingsSchema = z.object({
|
|
|
526
567
|
// (a liveness/reaper cadence), this bounds how long one turn waits for capacity
|
|
527
568
|
// or provider creation before surfacing a clear turn.failed error.
|
|
528
569
|
sandboxWarmingTimeoutMs: z.coerce.number().int().positive().default(6e5),
|
|
570
|
+
// Rig setup-script budget (M3): the wall-clock timeout the rig-setup lifecycle
|
|
571
|
+
// hook runs its script under, distinct from the 120s per-command lifecycle
|
|
572
|
+
// default (a rig may compile/install heavy tooling on first cold create).
|
|
573
|
+
// Env: OPENGENI_RIG_SETUP_TIMEOUT_MS. Default 10min.
|
|
574
|
+
rigSetupTimeoutMs: z.coerce.number().int().positive().default(6e5),
|
|
529
575
|
// --- sandbox warm-time billing (P2.1) ---
|
|
530
576
|
// Per-backend warm rate (usd_micros/sec), like modelPricingJson: an empty {}
|
|
531
577
|
// means warm-cost is not debited (warm-seconds are still metered for audit).
|
|
@@ -586,30 +632,32 @@ var SettingsSchema = z.object({
|
|
|
586
632
|
stripePublishableKey: z.string().optional(),
|
|
587
633
|
stripeWebhookSecret: z.string().optional(),
|
|
588
634
|
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
|
-
|
|
635
|
+
mcpServers: z.array(
|
|
636
|
+
z.object({
|
|
637
|
+
id: z.string().min(1).regex(registryId),
|
|
638
|
+
name: z.string().min(1).optional(),
|
|
639
|
+
url: z.string().url(),
|
|
640
|
+
allowedTools: z.array(z.string().min(1)).optional(),
|
|
641
|
+
timeoutMs: z.number().int().positive().optional(),
|
|
642
|
+
cacheToolsList: z.boolean().default(false),
|
|
643
|
+
/**
|
|
644
|
+
* Human-approval policy for this server's tools, overlaid per-run from a
|
|
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(),
|
|
651
|
+
/**
|
|
652
|
+
* Extra request headers sent to this MCP server (credential injection
|
|
653
|
+
* for workspace-enabled capability MCPs). Populated at runtime from
|
|
654
|
+
* encrypted capability-installation credentials; do not put secrets in
|
|
655
|
+
* OPENGENI_MCP_SERVERS.
|
|
656
|
+
*/
|
|
657
|
+
headers: z.record(z.string(), z.string()).optional(),
|
|
658
|
+
connectionRef: McpServerConnectionRefSchema.optional()
|
|
659
|
+
})
|
|
660
|
+
).default([])
|
|
613
661
|
});
|
|
614
662
|
var ModelPricingSchema = z.object({
|
|
615
663
|
inputMicrosPerMillionTokens: z.number().int().nonnegative(),
|
|
@@ -625,6 +673,8 @@ var RegistryModelSchema = z.object({
|
|
|
625
673
|
label: z.string().min(1).optional(),
|
|
626
674
|
// display name; defaults to id
|
|
627
675
|
contextWindowTokens: z.number().int().positive().optional(),
|
|
676
|
+
effectiveContextWindowTokens: z.number().int().positive().optional(),
|
|
677
|
+
autoCompactTokenLimit: z.number().int().positive().optional(),
|
|
628
678
|
reasoningEffort: z.boolean().optional(),
|
|
629
679
|
// model accepts a reasoning-effort control
|
|
630
680
|
hostedWebSearch: z.boolean().optional(),
|
|
@@ -653,12 +703,24 @@ var IntegrationOAuthClientConfigSchema = z.object({
|
|
|
653
703
|
tokenEndpointAuthMethod: z.enum(["none", "client_secret_post", "client_secret_basic"]).default("none")
|
|
654
704
|
});
|
|
655
705
|
var defaultModelPricing = {
|
|
656
|
-
"gpt-5.
|
|
706
|
+
"gpt-5.6-sol": {
|
|
657
707
|
inputMicrosPerMillionTokens: 5e6,
|
|
658
708
|
cachedInputMicrosPerMillionTokens: 5e5,
|
|
659
709
|
outputMicrosPerMillionTokens: 3e7,
|
|
660
710
|
marginBps: 2500
|
|
661
711
|
},
|
|
712
|
+
"gpt-5.6-terra": {
|
|
713
|
+
inputMicrosPerMillionTokens: 25e5,
|
|
714
|
+
cachedInputMicrosPerMillionTokens: 25e4,
|
|
715
|
+
outputMicrosPerMillionTokens: 15e6,
|
|
716
|
+
marginBps: 2500
|
|
717
|
+
},
|
|
718
|
+
"gpt-5.6-luna": {
|
|
719
|
+
inputMicrosPerMillionTokens: 1e6,
|
|
720
|
+
cachedInputMicrosPerMillionTokens: 1e5,
|
|
721
|
+
outputMicrosPerMillionTokens: 6e6,
|
|
722
|
+
marginBps: 2500
|
|
723
|
+
},
|
|
662
724
|
"gpt-5.4": {
|
|
663
725
|
inputMicrosPerMillionTokens: 25e5,
|
|
664
726
|
cachedInputMicrosPerMillionTokens: 25e4,
|
|
@@ -734,21 +796,11 @@ var SANDBOX_REQUIRED_ENV = {
|
|
|
734
796
|
{ field: "modalTokenId", env: "OPENGENI_MODAL_TOKEN_ID" },
|
|
735
797
|
{ field: "modalTokenSecret", env: "OPENGENI_MODAL_TOKEN_SECRET" }
|
|
736
798
|
],
|
|
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
|
-
],
|
|
799
|
+
daytona: [{ field: "daytonaApiKey", env: "OPENGENI_DAYTONA_API_KEY" }],
|
|
800
|
+
runloop: [{ field: "runloopApiKey", env: "OPENGENI_RUNLOOP_API_KEY" }],
|
|
801
|
+
e2b: [{ field: "e2bApiKey", env: "OPENGENI_E2B_API_KEY" }],
|
|
802
|
+
blaxel: [{ field: "blaxelApiKey", env: "OPENGENI_BLAXEL_API_KEY" }],
|
|
803
|
+
cloudflare: [{ field: "cloudflareWorkerUrl", env: "OPENGENI_CLOUDFLARE_WORKER_URL" }],
|
|
752
804
|
vercel: [
|
|
753
805
|
{ field: "vercelToken", env: "OPENGENI_VERCEL_TOKEN" },
|
|
754
806
|
{ field: "vercelProjectId", env: "OPENGENI_VERCEL_PROJECT_ID" }
|
|
@@ -780,7 +832,9 @@ function getSettings() {
|
|
|
780
832
|
temporalNamespace: optional("OPENGENI_TEMPORAL_NAMESPACE"),
|
|
781
833
|
temporalTaskQueue: optional("OPENGENI_TEMPORAL_TASK_QUEUE"),
|
|
782
834
|
startupDependencyRetryAttempts: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_ATTEMPTS"),
|
|
783
|
-
startupDependencyRetryInitialDelayMs: optional(
|
|
835
|
+
startupDependencyRetryInitialDelayMs: optional(
|
|
836
|
+
"OPENGENI_STARTUP_DEPENDENCY_RETRY_INITIAL_DELAY_MS"
|
|
837
|
+
),
|
|
784
838
|
startupDependencyRetryMaxDelayMs: optional("OPENGENI_STARTUP_DEPENDENCY_RETRY_MAX_DELAY_MS"),
|
|
785
839
|
observabilityStructuredLogs: optional("OPENGENI_OBSERVABILITY_STRUCTURED_LOGS"),
|
|
786
840
|
observabilityMetricsEnabled: optional("OPENGENI_OBSERVABILITY_METRICS_ENABLED"),
|
|
@@ -802,21 +856,18 @@ function getSettings() {
|
|
|
802
856
|
environmentsEncryptionKey: optional("OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY"),
|
|
803
857
|
integrationsEnabled: optional("OPENGENI_INTEGRATIONS_ENABLED"),
|
|
804
858
|
integrationsStateSecret: optional("OPENGENI_INTEGRATIONS_STATE_SECRET"),
|
|
805
|
-
integrationsAllowPrivateNetworkTargets: optional(
|
|
859
|
+
integrationsAllowPrivateNetworkTargets: optional(
|
|
860
|
+
"OPENGENI_INTEGRATIONS_ALLOW_PRIVATE_NETWORK_TARGETS"
|
|
861
|
+
),
|
|
806
862
|
integrationsOauthClientsJson: optional("OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON"),
|
|
807
863
|
goalMaxAutoContinuations: optional("OPENGENI_GOAL_MAX_AUTO_CONTINUATIONS"),
|
|
808
864
|
goalNoProgressLimit: optional("OPENGENI_GOAL_NO_PROGRESS_LIMIT"),
|
|
809
865
|
agentMaxModelCallsPerTurn: optional("OPENGENI_AGENT_MAX_MODEL_CALLS_PER_TURN"),
|
|
810
|
-
sessionHistorySource: optional("OPENGENI_SESSION_HISTORY_SOURCE"),
|
|
811
|
-
contextCompactionMode: optional("OPENGENI_CONTEXT_COMPACTION_MODE"),
|
|
812
866
|
contextWindowTokens: optional("OPENGENI_CONTEXT_WINDOW_TOKENS"),
|
|
867
|
+
contextEffectiveWindowTokens: optional("OPENGENI_CONTEXT_EFFECTIVE_WINDOW_TOKENS"),
|
|
813
868
|
contextCompactionThresholdRatio: optional("OPENGENI_COMPACTION_THRESHOLD_RATIO"),
|
|
814
869
|
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"),
|
|
870
|
+
contextAutoCompactThresholdTokens: optional("OPENGENI_CONTEXT_AUTO_COMPACT_THRESHOLD_TOKENS"),
|
|
820
871
|
authRequired: optional("OPENGENI_AUTH_REQUIRED"),
|
|
821
872
|
accessKey: optional("OPENGENI_ACCESS_KEY"),
|
|
822
873
|
authAllowHealth: optional("OPENGENI_AUTH_ALLOW_HEALTH"),
|
|
@@ -835,6 +886,7 @@ function getSettings() {
|
|
|
835
886
|
modelProvidersJson: optional("OPENGENI_MODEL_PROVIDERS_JSON"),
|
|
836
887
|
codexSubscriptionEnabled: optional("OPENGENI_CODEX_SUBSCRIPTION_ENABLED"),
|
|
837
888
|
codexToolSearchEnabled: optional("OPENGENI_CODEX_TOOL_SEARCH_ENABLED"),
|
|
889
|
+
codexCredentialLeasingEnabled: optional("OPENGENI_CODEX_CREDENTIAL_LEASING_ENABLED"),
|
|
838
890
|
codexProductSku: optional("OPENGENI_CODEX_PRODUCT_SKU"),
|
|
839
891
|
codexRotationNearExhaustionPct: optional("OPENGENI_CODEX_ROTATION_NEAR_EXHAUSTION_PCT"),
|
|
840
892
|
openaiReasoningEffort: optional("OPENGENI_OPENAI_REASONING_EFFORT"),
|
|
@@ -874,6 +926,7 @@ function getSettings() {
|
|
|
874
926
|
computerUseEnabled: optional("OPENGENI_COMPUTER_USE_ENABLED"),
|
|
875
927
|
computerUseReadOnly: optional("OPENGENI_COMPUTER_USE_READONLY"),
|
|
876
928
|
recordingEnabled: optional("OPENGENI_RECORDING_ENABLED"),
|
|
929
|
+
workspaceCaptureEnabled: optional("OPENGENI_WORKSPACE_CAPTURE"),
|
|
877
930
|
recordingDefaultCodec: optional("OPENGENI_RECORDING_DEFAULT_CODEC"),
|
|
878
931
|
recordingFramerate: optional("OPENGENI_RECORDING_FRAMERATE"),
|
|
879
932
|
recordingMaxSeconds: optional("OPENGENI_RECORDING_MAX_SECONDS"),
|
|
@@ -913,7 +966,9 @@ function getSettings() {
|
|
|
913
966
|
vercelTeamId: optional("OPENGENI_VERCEL_TEAM_ID"),
|
|
914
967
|
vercelRuntime: optional("OPENGENI_VERCEL_RUNTIME"),
|
|
915
968
|
sandboxOwnershipEnabled: optional("OPENGENI_SANDBOX_OWNERSHIP_ENABLED"),
|
|
969
|
+
sandboxLazyProvisionEnabled: optional("OPENGENI_SANDBOX_LAZY_PROVISION"),
|
|
916
970
|
sandboxSelfhostedEnabled: optional("OPENGENI_SANDBOX_SELFHOSTED_ENABLED"),
|
|
971
|
+
agentOpStreamEnabled: optional("OPENGENI_AGENT_OP_STREAM_ENABLED"),
|
|
917
972
|
enrollmentSigningSecret: optional("OPENGENI_ENROLLMENT_SIGNING_SECRET"),
|
|
918
973
|
selfhostedNatsUrl: optional("OPENGENI_SELFHOSTED_NATS_URL"),
|
|
919
974
|
selfhostedRelayUrl: optional("OPENGENI_SELFHOSTED_RELAY_URL"),
|
|
@@ -925,13 +980,20 @@ function getSettings() {
|
|
|
925
980
|
selfhostedNatsCalloutPassword: optional("OPENGENI_SELFHOSTED_NATS_CALLOUT_PASSWORD"),
|
|
926
981
|
selfhostedNatsControlUser: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_USER"),
|
|
927
982
|
selfhostedNatsControlPassword: optional("OPENGENI_SELFHOSTED_NATS_CONTROL_PASSWORD"),
|
|
983
|
+
sandboxSelfhostedExecTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_EXEC_TIMEOUT_MS"),
|
|
984
|
+
sandboxSelfhostedControlTimeoutMs: optional("OPENGENI_SANDBOX_SELFHOSTED_CONTROL_TIMEOUT_MS"),
|
|
928
985
|
sandboxLeaseReaperPeriodMs: optional("OPENGENI_SANDBOX_LEASE_REAPER_PERIOD_MS"),
|
|
929
986
|
sandboxViewerHolderTtlMs: optional("OPENGENI_SANDBOX_VIEWER_HOLDER_TTL_MS"),
|
|
930
987
|
sandboxIdleGraceMs: optional("OPENGENI_SANDBOX_IDLE_GRACE_MS"),
|
|
988
|
+
sandboxSnapshotIntervalMs: optional("OPENGENI_SANDBOX_SNAPSHOT_INTERVAL_MS"),
|
|
989
|
+
sandboxSnapshotTimeoutMs: optional("OPENGENI_SANDBOX_SNAPSHOT_TIMEOUT_MS"),
|
|
931
990
|
sandboxLeaseTtlMs: optional("OPENGENI_SANDBOX_LEASE_TTL_MS"),
|
|
932
991
|
sandboxLeaseWarmingTtlMs: optional("OPENGENI_SANDBOX_LEASE_WARMING_TTL_MS"),
|
|
933
992
|
sandboxWarmingTimeoutMs: optional("OPENGENI_SANDBOX_WARMING_TIMEOUT_MS"),
|
|
934
|
-
|
|
993
|
+
rigSetupTimeoutMs: optional("OPENGENI_RIG_SETUP_TIMEOUT_MS"),
|
|
994
|
+
sandboxWarmRateMicrosPerSecondJson: optional(
|
|
995
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON"
|
|
996
|
+
),
|
|
935
997
|
sandboxMaxWarmSecondsPerWorkspace: optional("OPENGENI_SANDBOX_MAX_WARM_SECONDS_PER_WORKSPACE"),
|
|
936
998
|
sandboxPreparationProfiles: optional("OPENGENI_SANDBOX_PREPARATION_PROFILES"),
|
|
937
999
|
sandboxEnvAllowlist: optional("OPENGENI_SANDBOX_ENV_ALLOWLIST"),
|
|
@@ -1027,8 +1089,7 @@ function configuredProviders(settings) {
|
|
|
1027
1089
|
label: builtinProviderLabel(settings),
|
|
1028
1090
|
kind: "api-key",
|
|
1029
1091
|
api: "responses",
|
|
1030
|
-
builtin: true
|
|
1031
|
-
compactionMode: resolveContextCompactionMode(settings)
|
|
1092
|
+
builtin: true
|
|
1032
1093
|
};
|
|
1033
1094
|
if (settings.openaiProvider === "azure") {
|
|
1034
1095
|
builtin.baseUrl = settings.azureOpenaiBaseUrl ?? settings.azureOpenaiEndpoint;
|
|
@@ -1037,28 +1098,41 @@ function configuredProviders(settings) {
|
|
|
1037
1098
|
builtin.baseUrl = settings.openaiBaseUrl;
|
|
1038
1099
|
builtin.apiKey = settings.openaiApiKey;
|
|
1039
1100
|
}
|
|
1040
|
-
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1101
|
+
const registry = parseModelProvidersJson(settings.modelProvidersJson).map(
|
|
1102
|
+
(provider) => ({
|
|
1103
|
+
id: provider.id,
|
|
1104
|
+
label: provider.label ?? provider.id,
|
|
1105
|
+
kind: provider.kind,
|
|
1106
|
+
api: provider.api,
|
|
1107
|
+
builtin: false,
|
|
1108
|
+
baseUrl: provider.baseUrl,
|
|
1109
|
+
apiKey: resolveProviderApiKey(provider),
|
|
1110
|
+
defaultQuery: provider.defaultQuery,
|
|
1111
|
+
defaultHeaders: provider.defaultHeaders
|
|
1112
|
+
})
|
|
1113
|
+
);
|
|
1052
1114
|
return [builtin, ...registry];
|
|
1053
1115
|
}
|
|
1116
|
+
function policyProviderIdForModel(settings, modelId) {
|
|
1117
|
+
if (modelId.startsWith(CODEX_MODEL_ID_PREFIX)) {
|
|
1118
|
+
return CODEX_PROVIDER_ID;
|
|
1119
|
+
}
|
|
1120
|
+
const configured = configuredModels(settings).find((model) => model.id === modelId);
|
|
1121
|
+
return configured?.providerId ?? builtinProviderId(settings);
|
|
1122
|
+
}
|
|
1054
1123
|
function configuredModels(settings) {
|
|
1055
1124
|
const builtinId = builtinProviderId(settings);
|
|
1056
1125
|
const builtinLabel = builtinProviderLabel(settings);
|
|
1057
1126
|
const registryOwnedIds = new Set(
|
|
1058
|
-
parseModelProvidersJson(settings.modelProvidersJson).flatMap(
|
|
1127
|
+
parseModelProvidersJson(settings.modelProvidersJson).flatMap(
|
|
1128
|
+
(provider) => provider.models.map((model) => model.id)
|
|
1129
|
+
)
|
|
1059
1130
|
);
|
|
1060
1131
|
const isRegistryNamespaced = (id) => id.startsWith(CODEX_MODEL_ID_PREFIX) || id.includes("/") && registryOwnedIds.has(id);
|
|
1061
|
-
const out = uniqueValues([
|
|
1132
|
+
const out = uniqueValues([
|
|
1133
|
+
settings.openaiModel,
|
|
1134
|
+
...splitCsv(settings.openaiAllowedModels)
|
|
1135
|
+
]).filter((id) => !isRegistryNamespaced(id)).map((id) => ({
|
|
1062
1136
|
id,
|
|
1063
1137
|
label: id,
|
|
1064
1138
|
providerId: builtinId,
|
|
@@ -1078,6 +1152,8 @@ function configuredModels(settings) {
|
|
|
1078
1152
|
providerLabel,
|
|
1079
1153
|
api: provider.api,
|
|
1080
1154
|
...model.contextWindowTokens === void 0 ? {} : { contextWindowTokens: model.contextWindowTokens },
|
|
1155
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : { effectiveContextWindowTokens: model.effectiveContextWindowTokens },
|
|
1156
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { autoCompactTokenLimit: model.autoCompactTokenLimit },
|
|
1081
1157
|
reasoningEffort: model.reasoningEffort ?? false,
|
|
1082
1158
|
hostedWebSearch: model.hostedWebSearch ?? false
|
|
1083
1159
|
});
|
|
@@ -1100,7 +1176,9 @@ function resolveModelProvider(settings, modelId) {
|
|
|
1100
1176
|
if (!model) {
|
|
1101
1177
|
return void 0;
|
|
1102
1178
|
}
|
|
1103
|
-
const provider = configuredProviders(settings).find(
|
|
1179
|
+
const provider = configuredProviders(settings).find(
|
|
1180
|
+
(candidate) => candidate.id === model.providerId
|
|
1181
|
+
);
|
|
1104
1182
|
if (!provider) {
|
|
1105
1183
|
return void 0;
|
|
1106
1184
|
}
|
|
@@ -1122,27 +1200,25 @@ function configuredModelPricing(settings) {
|
|
|
1122
1200
|
...configured
|
|
1123
1201
|
};
|
|
1124
1202
|
}
|
|
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
1203
|
function contextInputBudgetTokens(settings) {
|
|
1204
|
+
if (settings.contextEffectiveWindowTokens !== void 0) {
|
|
1205
|
+
return Math.min(settings.contextWindowTokens, settings.contextEffectiveWindowTokens);
|
|
1206
|
+
}
|
|
1139
1207
|
return Math.max(0, settings.contextWindowTokens - settings.contextReservedOutputTokens);
|
|
1140
1208
|
}
|
|
1141
|
-
function
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1209
|
+
function settingsWithResolvedModelContext(settings, model) {
|
|
1210
|
+
const contextWindowTokens = model.contextWindowTokens ?? settings.contextWindowTokens;
|
|
1211
|
+
return {
|
|
1212
|
+
...settings,
|
|
1213
|
+
contextWindowTokens,
|
|
1214
|
+
...model.effectiveContextWindowTokens === void 0 ? {} : {
|
|
1215
|
+
contextEffectiveWindowTokens: Math.min(
|
|
1216
|
+
contextWindowTokens,
|
|
1217
|
+
model.effectiveContextWindowTokens
|
|
1218
|
+
)
|
|
1219
|
+
},
|
|
1220
|
+
...model.autoCompactTokenLimit === void 0 ? {} : { contextAutoCompactThresholdTokens: model.autoCompactTokenLimit }
|
|
1221
|
+
};
|
|
1146
1222
|
}
|
|
1147
1223
|
function configuredStaticUsageLimits(settings) {
|
|
1148
1224
|
return parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
|
|
@@ -1175,7 +1251,10 @@ function calculateModelUsageCostMicros(settings, model, usage) {
|
|
|
1175
1251
|
return Math.ceil(rawCost * (1e4 + marginBps) / 1e4);
|
|
1176
1252
|
}
|
|
1177
1253
|
function configuredAllowedReasoningEfforts(settings) {
|
|
1178
|
-
return uniqueValues([
|
|
1254
|
+
return uniqueValues([
|
|
1255
|
+
settings.openaiReasoningEffort,
|
|
1256
|
+
...splitCsv(settings.openaiAllowedReasoningEfforts)
|
|
1257
|
+
]).map((value) => ReasoningEffort.parse(value));
|
|
1179
1258
|
}
|
|
1180
1259
|
function environmentsEncryptionKeyBytes(settings) {
|
|
1181
1260
|
if (!settings.environmentsEncryptionKey) {
|
|
@@ -1183,7 +1262,9 @@ function environmentsEncryptionKeyBytes(settings) {
|
|
|
1183
1262
|
}
|
|
1184
1263
|
const decoded = Buffer.from(settings.environmentsEncryptionKey, "base64");
|
|
1185
1264
|
if (decoded.length !== 32) {
|
|
1186
|
-
throw new Error(
|
|
1265
|
+
throw new Error(
|
|
1266
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY must be base64 for exactly 32 bytes (generate with: openssl rand -base64 32)"
|
|
1267
|
+
);
|
|
1187
1268
|
}
|
|
1188
1269
|
return new Uint8Array(decoded);
|
|
1189
1270
|
}
|
|
@@ -1198,12 +1279,21 @@ function dbSearchPath(settings) {
|
|
|
1198
1279
|
return `${schema},opengeni_private,public`;
|
|
1199
1280
|
}
|
|
1200
1281
|
function collectGitIdentityEnvironment(settings) {
|
|
1201
|
-
return Object.fromEntries(
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1282
|
+
return Object.fromEntries(
|
|
1283
|
+
Object.entries({
|
|
1284
|
+
GIT_AUTHOR_NAME: settings.gitAuthorName,
|
|
1285
|
+
GIT_AUTHOR_EMAIL: settings.gitAuthorEmail,
|
|
1286
|
+
GIT_COMMITTER_NAME: settings.gitCommitterName ?? settings.gitAuthorName,
|
|
1287
|
+
GIT_COMMITTER_EMAIL: settings.gitCommitterEmail ?? settings.gitAuthorEmail
|
|
1288
|
+
}).filter(
|
|
1289
|
+
(entry) => typeof entry[1] === "string" && entry[1].trim().length > 0
|
|
1290
|
+
)
|
|
1291
|
+
);
|
|
1292
|
+
}
|
|
1293
|
+
var DEFAULT_SANDBOX_PATH = "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin";
|
|
1294
|
+
function prependPathEntry(pathValue, entry) {
|
|
1295
|
+
const parts = (pathValue ?? DEFAULT_SANDBOX_PATH).split(":").filter(Boolean);
|
|
1296
|
+
return [entry, ...parts.filter((part) => part !== entry)].join(":");
|
|
1207
1297
|
}
|
|
1208
1298
|
function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, options = {}) {
|
|
1209
1299
|
const environment = {
|
|
@@ -1215,18 +1305,35 @@ function stableSandboxEnvironmentForRun(settings, workspaceEnvironment = {}, opt
|
|
|
1215
1305
|
if (settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local") {
|
|
1216
1306
|
environment.HOME ??= descriptor.workspaceRoot;
|
|
1217
1307
|
}
|
|
1218
|
-
|
|
1308
|
+
const provisionedGitHelperBackend = settings.sandboxBackend !== "none" && settings.sandboxBackend !== "local" && settings.sandboxBackend !== "selfhosted";
|
|
1309
|
+
if (provisionedGitHelperBackend) {
|
|
1310
|
+
const home = environment.HOME ?? descriptor.workspaceRoot;
|
|
1311
|
+
environment.OPENGENI_GIT_CREDENTIALS_DIR ??= `${home}/.opengeni/git-credentials`;
|
|
1312
|
+
environment.OPENGENI_GIT_TOKEN_FILE ??= `${home}/.opengeni/git-token`;
|
|
1313
|
+
environment.OPENGENI_GIT_CLI_WRAPPER_DIR ??= `${home}/.opengeni/bin`;
|
|
1314
|
+
environment.PATH = prependPathEntry(environment.PATH, environment.OPENGENI_GIT_CLI_WRAPPER_DIR);
|
|
1315
|
+
}
|
|
1219
1316
|
if (settings.toolspaceEnabled) {
|
|
1220
1317
|
environment.OPENGENI_TOOLSPACE_TOKEN_FILE ??= `${environment.HOME ?? descriptor.workspaceRoot}/.opengeni/toolspace-token`;
|
|
1221
1318
|
if (options.workspaceId) {
|
|
1222
|
-
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1319
|
+
environment.OPENGENI_TOOLSPACE_URL ??= firstPartyMcpWorkspaceUrl(
|
|
1320
|
+
settings,
|
|
1321
|
+
options.workspaceId
|
|
1322
|
+
);
|
|
1223
1323
|
}
|
|
1224
1324
|
}
|
|
1225
1325
|
return environment;
|
|
1226
1326
|
}
|
|
1227
1327
|
function hasGitHubRepositorySelection(resources) {
|
|
1228
1328
|
const positive = (value) => typeof value === "number" && Number.isInteger(value) && value > 0 || typeof value === "string" && /^\d+$/.test(value) && Number(value) > 0;
|
|
1229
|
-
return resources.some(
|
|
1329
|
+
return resources.some(
|
|
1330
|
+
(resource) => resource.kind === "repository" && (positive(resource.githubInstallationId) && positive(resource.githubRepositoryId) || resource.provider === "github" && positive(resource.installationId) && positive(resource.repositoryId))
|
|
1331
|
+
);
|
|
1332
|
+
}
|
|
1333
|
+
function hasGitCredentialRepositorySelection(resources) {
|
|
1334
|
+
return resources.some(
|
|
1335
|
+
(resource) => resource.kind === "repository" && (resource.provider === "github" || resource.provider === "gitlab" || resource.provider === "azure_devops" || hasGitHubRepositorySelection([resource]))
|
|
1336
|
+
);
|
|
1230
1337
|
}
|
|
1231
1338
|
function applyGitAuthPointerEnvironment(environment, identity) {
|
|
1232
1339
|
environment.GIT_ASKPASS = `${environment.HOME ?? "/workspace"}/.opengeni/askpass`;
|
|
@@ -1281,10 +1388,14 @@ function sandboxLifecycleHookIds(settings) {
|
|
|
1281
1388
|
return uniqueValues(ids);
|
|
1282
1389
|
}
|
|
1283
1390
|
function sandboxPreparationProfileNames(settings) {
|
|
1284
|
-
const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
|
|
1391
|
+
const profiles = splitCsv(settings.sandboxPreparationProfiles).map(
|
|
1392
|
+
(value) => value.toLowerCase()
|
|
1393
|
+
);
|
|
1285
1394
|
if (profiles.includes("none")) {
|
|
1286
1395
|
if (profiles.length > 1) {
|
|
1287
|
-
throw new Error(
|
|
1396
|
+
throw new Error(
|
|
1397
|
+
"OPENGENI_SANDBOX_PREPARATION_PROFILES cannot combine none with other profiles"
|
|
1398
|
+
);
|
|
1288
1399
|
}
|
|
1289
1400
|
return ["none"];
|
|
1290
1401
|
}
|
|
@@ -1316,7 +1427,7 @@ function parseMcpServers(raw) {
|
|
|
1316
1427
|
return parsed;
|
|
1317
1428
|
} catch (error) {
|
|
1318
1429
|
const message = error instanceof Error ? error.message : String(error);
|
|
1319
|
-
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}
|
|
1430
|
+
throw new Error(`OPENGENI_MCP_SERVERS must be a JSON array: ${message}`, { cause: error });
|
|
1320
1431
|
}
|
|
1321
1432
|
}
|
|
1322
1433
|
function parseModelPricingJson(raw) {
|
|
@@ -1328,7 +1439,7 @@ function parseModelPricingJson(raw) {
|
|
|
1328
1439
|
parsed = JSON.parse(raw);
|
|
1329
1440
|
} catch (error) {
|
|
1330
1441
|
const message = error instanceof Error ? error.message : String(error);
|
|
1331
|
-
throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}
|
|
1442
|
+
throw new Error(`OPENGENI_MODEL_PRICING_JSON must be valid JSON: ${message}`, { cause: error });
|
|
1332
1443
|
}
|
|
1333
1444
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1334
1445
|
throw new Error("OPENGENI_MODEL_PRICING_JSON must be a JSON object keyed by model name");
|
|
@@ -1351,19 +1462,28 @@ function parseSandboxWarmRateJson(raw) {
|
|
|
1351
1462
|
parsed = JSON.parse(raw);
|
|
1352
1463
|
} catch (error) {
|
|
1353
1464
|
const message = error instanceof Error ? error.message : String(error);
|
|
1354
|
-
throw new Error(
|
|
1465
|
+
throw new Error(
|
|
1466
|
+
`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be valid JSON: ${message}`,
|
|
1467
|
+
{ cause: error }
|
|
1468
|
+
);
|
|
1355
1469
|
}
|
|
1356
1470
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1357
|
-
throw new Error(
|
|
1471
|
+
throw new Error(
|
|
1472
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON must be a JSON object keyed by backend name"
|
|
1473
|
+
);
|
|
1358
1474
|
}
|
|
1359
1475
|
const out = {};
|
|
1360
1476
|
for (const [backend, value] of Object.entries(parsed)) {
|
|
1361
1477
|
if (!backend.trim()) {
|
|
1362
|
-
throw new Error(
|
|
1478
|
+
throw new Error(
|
|
1479
|
+
"OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON contains an empty backend name"
|
|
1480
|
+
);
|
|
1363
1481
|
}
|
|
1364
1482
|
const rate = typeof value === "number" ? value : Number(value);
|
|
1365
1483
|
if (!Number.isFinite(rate) || rate < 0) {
|
|
1366
|
-
throw new Error(
|
|
1484
|
+
throw new Error(
|
|
1485
|
+
`OPENGENI_SANDBOX_WARM_RATE_MICROS_PER_SECOND_JSON rate for ${backend} must be a non-negative number`
|
|
1486
|
+
);
|
|
1367
1487
|
}
|
|
1368
1488
|
out[backend] = rate;
|
|
1369
1489
|
}
|
|
@@ -1382,7 +1502,9 @@ function parseModelProvidersJson(raw) {
|
|
|
1382
1502
|
parsed = JSON.parse(raw);
|
|
1383
1503
|
} catch (error) {
|
|
1384
1504
|
const message = error instanceof Error ? error.message : String(error);
|
|
1385
|
-
throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}
|
|
1505
|
+
throw new Error(`OPENGENI_MODEL_PROVIDERS_JSON must be valid JSON: ${message}`, {
|
|
1506
|
+
cause: error
|
|
1507
|
+
});
|
|
1386
1508
|
}
|
|
1387
1509
|
if (!Array.isArray(parsed)) {
|
|
1388
1510
|
throw new Error("OPENGENI_MODEL_PROVIDERS_JSON must be a JSON array of providers");
|
|
@@ -1390,7 +1512,9 @@ function parseModelProvidersJson(raw) {
|
|
|
1390
1512
|
return parsed.map((entry, index) => {
|
|
1391
1513
|
const result = RegistryProviderSchema.safeParse(entry);
|
|
1392
1514
|
if (!result.success) {
|
|
1393
|
-
throw new Error(
|
|
1515
|
+
throw new Error(
|
|
1516
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider[${index}] is invalid: ${result.error.message}`
|
|
1517
|
+
);
|
|
1394
1518
|
}
|
|
1395
1519
|
return result.data;
|
|
1396
1520
|
});
|
|
@@ -1404,10 +1528,14 @@ function parseIntegrationsOauthClientsJson(raw) {
|
|
|
1404
1528
|
parsed = JSON.parse(raw);
|
|
1405
1529
|
} catch (error) {
|
|
1406
1530
|
const message = error instanceof Error ? error.message : String(error);
|
|
1407
|
-
throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}
|
|
1531
|
+
throw new Error(`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be valid JSON: ${message}`, {
|
|
1532
|
+
cause: error
|
|
1533
|
+
});
|
|
1408
1534
|
}
|
|
1409
1535
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
1410
|
-
throw new Error(
|
|
1536
|
+
throw new Error(
|
|
1537
|
+
"OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON must be a JSON object keyed by authorization-server issuer or URL"
|
|
1538
|
+
);
|
|
1411
1539
|
}
|
|
1412
1540
|
const out = {};
|
|
1413
1541
|
for (const [key, value] of Object.entries(parsed)) {
|
|
@@ -1416,7 +1544,9 @@ function parseIntegrationsOauthClientsJson(raw) {
|
|
|
1416
1544
|
}
|
|
1417
1545
|
const result = IntegrationOAuthClientConfigSchema.safeParse(value);
|
|
1418
1546
|
if (!result.success) {
|
|
1419
|
-
throw new Error(
|
|
1547
|
+
throw new Error(
|
|
1548
|
+
`OPENGENI_INTEGRATIONS_OAUTH_CLIENTS_JSON client for ${key} is invalid: ${result.error.message}`
|
|
1549
|
+
);
|
|
1420
1550
|
}
|
|
1421
1551
|
out[key] = result.data;
|
|
1422
1552
|
}
|
|
@@ -1431,7 +1561,9 @@ function parseStaticUsageLimitsJson(raw) {
|
|
|
1431
1561
|
parsed = JSON.parse(raw);
|
|
1432
1562
|
} catch (error) {
|
|
1433
1563
|
const message = error instanceof Error ? error.message : String(error);
|
|
1434
|
-
throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}
|
|
1564
|
+
throw new Error(`OPENGENI_STATIC_USAGE_LIMITS_JSON must be valid JSON: ${message}`, {
|
|
1565
|
+
cause: error
|
|
1566
|
+
});
|
|
1435
1567
|
}
|
|
1436
1568
|
return StaticUsageLimits.parse(parsed);
|
|
1437
1569
|
}
|
|
@@ -1444,7 +1576,9 @@ function parseStaticEntitlementsJson(raw) {
|
|
|
1444
1576
|
parsed = JSON.parse(raw);
|
|
1445
1577
|
} catch (error) {
|
|
1446
1578
|
const message = error instanceof Error ? error.message : String(error);
|
|
1447
|
-
throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}
|
|
1579
|
+
throw new Error(`OPENGENI_STATIC_ENTITLEMENTS_JSON must be valid JSON: ${message}`, {
|
|
1580
|
+
cause: error
|
|
1581
|
+
});
|
|
1448
1582
|
}
|
|
1449
1583
|
return Entitlements.parse(parsed);
|
|
1450
1584
|
}
|
|
@@ -1491,20 +1625,32 @@ function ensureBuiltInMcpServers(settings) {
|
|
|
1491
1625
|
// safe to cache / leave as-is.)
|
|
1492
1626
|
cacheToolsList: false
|
|
1493
1627
|
},
|
|
1494
|
-
...hasFiles ? [] : [
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1628
|
+
...hasFiles ? [] : [
|
|
1629
|
+
{
|
|
1630
|
+
id: "files",
|
|
1631
|
+
name: "Files",
|
|
1632
|
+
url: firstPartyMcpUrl,
|
|
1633
|
+
allowedTools: ["files_get_download_url"],
|
|
1634
|
+
cacheToolsList: true
|
|
1635
|
+
}
|
|
1636
|
+
],
|
|
1637
|
+
...hasDocs ? [] : [
|
|
1638
|
+
{
|
|
1639
|
+
id: "docs",
|
|
1640
|
+
name: "Document Search",
|
|
1641
|
+
url: firstPartyDocsMcpUrl,
|
|
1642
|
+
allowedTools: [
|
|
1643
|
+
"search_documents",
|
|
1644
|
+
"fetch_document_chunk",
|
|
1645
|
+
"list_document_bases",
|
|
1646
|
+
"knowledge_search",
|
|
1647
|
+
"knowledge_fetch",
|
|
1648
|
+
"memory_search",
|
|
1649
|
+
"memory_propose"
|
|
1650
|
+
],
|
|
1651
|
+
cacheToolsList: false
|
|
1652
|
+
}
|
|
1653
|
+
],
|
|
1508
1654
|
...existing
|
|
1509
1655
|
];
|
|
1510
1656
|
}
|
|
@@ -1534,40 +1680,58 @@ function validateSettings(settings) {
|
|
|
1534
1680
|
}
|
|
1535
1681
|
if (settings.productAccessMode === "managed") {
|
|
1536
1682
|
if (!settings.publicBaseUrl) {
|
|
1537
|
-
throw new Error(
|
|
1683
|
+
throw new Error(
|
|
1684
|
+
"OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1685
|
+
);
|
|
1538
1686
|
}
|
|
1539
1687
|
if (!settings.betterAuthSecret) {
|
|
1540
|
-
throw new Error(
|
|
1688
|
+
throw new Error(
|
|
1689
|
+
"OPENGENI_BETTER_AUTH_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1690
|
+
);
|
|
1541
1691
|
}
|
|
1542
1692
|
if (!settings.delegationSecret) {
|
|
1543
|
-
throw new Error(
|
|
1693
|
+
throw new Error(
|
|
1694
|
+
"OPENGENI_DELEGATION_SECRET is required when OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1695
|
+
);
|
|
1544
1696
|
}
|
|
1545
1697
|
if (!["local", "test"].includes(settings.environment) && !settings.resendApiKey) {
|
|
1546
1698
|
throw new Error("OPENGENI_RESEND_API_KEY is required for managed mode outside local/test");
|
|
1547
1699
|
}
|
|
1548
1700
|
if (!["local", "test"].includes(settings.environment) && !settings.environmentsEncryptionKey) {
|
|
1549
|
-
throw new Error(
|
|
1701
|
+
throw new Error(
|
|
1702
|
+
"OPENGENI_ENVIRONMENTS_ENCRYPTION_KEY is required for managed mode outside local/test"
|
|
1703
|
+
);
|
|
1550
1704
|
}
|
|
1551
1705
|
}
|
|
1552
1706
|
environmentsEncryptionKeyBytes(settings);
|
|
1553
1707
|
if (settings.integrationsEnabled) {
|
|
1554
1708
|
if (settings.productAccessMode === "managed" && !settings.publicBaseUrl) {
|
|
1555
|
-
throw new Error(
|
|
1709
|
+
throw new Error(
|
|
1710
|
+
"OPENGENI_PUBLIC_BASE_URL is required when OPENGENI_INTEGRATIONS_ENABLED=true and OPENGENI_PRODUCT_ACCESS_MODE=managed"
|
|
1711
|
+
);
|
|
1556
1712
|
}
|
|
1557
1713
|
if (settings.publicBaseUrl && !settings.publicBaseUrl.startsWith("https://") && !["local", "test"].includes(settings.environment)) {
|
|
1558
|
-
throw new Error(
|
|
1714
|
+
throw new Error(
|
|
1715
|
+
"OPENGENI_PUBLIC_BASE_URL must use https when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
|
|
1716
|
+
);
|
|
1559
1717
|
}
|
|
1560
1718
|
if (!settings.integrationsStateSecret && !["local", "test"].includes(settings.environment)) {
|
|
1561
|
-
throw new Error(
|
|
1719
|
+
throw new Error(
|
|
1720
|
+
"OPENGENI_INTEGRATIONS_STATE_SECRET is required when OPENGENI_INTEGRATIONS_ENABLED=true outside local/test"
|
|
1721
|
+
);
|
|
1562
1722
|
}
|
|
1563
1723
|
}
|
|
1564
1724
|
parseIntegrationsOauthClientsJson(settings.integrationsOauthClientsJson);
|
|
1565
1725
|
if (settings.productAccessMode === "configured" && !["local", "test"].includes(settings.environment) && !settings.delegationSecret && !settings.authRequired) {
|
|
1566
|
-
throw new Error(
|
|
1726
|
+
throw new Error(
|
|
1727
|
+
"OPENGENI_PRODUCT_ACCESS_MODE=configured requires OPENGENI_DELEGATION_SECRET or OPENGENI_AUTH_REQUIRED=true outside local/test"
|
|
1728
|
+
);
|
|
1567
1729
|
}
|
|
1568
1730
|
if (settings.billingMode === "stripe") {
|
|
1569
1731
|
if (!settings.stripeSecretKey || !settings.stripeWebhookSecret) {
|
|
1570
|
-
throw new Error(
|
|
1732
|
+
throw new Error(
|
|
1733
|
+
"OPENGENI_STRIPE_SECRET_KEY and OPENGENI_STRIPE_WEBHOOK_SECRET are required when OPENGENI_BILLING_MODE=stripe"
|
|
1734
|
+
);
|
|
1571
1735
|
}
|
|
1572
1736
|
}
|
|
1573
1737
|
if (settings.productAccessMode !== "managed" && settings.billingMode === "stripe") {
|
|
@@ -1577,13 +1741,17 @@ function validateSettings(settings) {
|
|
|
1577
1741
|
const pricing = configuredModelPricing(settings);
|
|
1578
1742
|
const missing = configuredAllowedModels(settings).filter((model) => !pricing[model]);
|
|
1579
1743
|
if (missing.length > 0) {
|
|
1580
|
-
throw new Error(
|
|
1744
|
+
throw new Error(
|
|
1745
|
+
`Missing model pricing for managed billing model(s): ${missing.join(", ")}. Set OPENGENI_MODEL_PRICING_JSON.`
|
|
1746
|
+
);
|
|
1581
1747
|
}
|
|
1582
1748
|
}
|
|
1583
1749
|
if (settings.usageLimitsMode === "static") {
|
|
1584
1750
|
const limits = configuredStaticUsageLimits(settings);
|
|
1585
1751
|
if (Object.keys(limits).length === 0) {
|
|
1586
|
-
throw new Error(
|
|
1752
|
+
throw new Error(
|
|
1753
|
+
"OPENGENI_STATIC_USAGE_LIMITS_JSON must define at least one cap when OPENGENI_USAGE_LIMITS_MODE=static"
|
|
1754
|
+
);
|
|
1587
1755
|
}
|
|
1588
1756
|
} else {
|
|
1589
1757
|
parseStaticUsageLimitsJson(settings.staticUsageLimitsJson);
|
|
@@ -1591,7 +1759,9 @@ function validateSettings(settings) {
|
|
|
1591
1759
|
if (settings.entitlementsMode === "static") {
|
|
1592
1760
|
const entitlements = parseStaticEntitlementsJson(settings.staticEntitlementsJson);
|
|
1593
1761
|
if (Object.keys(entitlements).length === 0) {
|
|
1594
|
-
throw new Error(
|
|
1762
|
+
throw new Error(
|
|
1763
|
+
"OPENGENI_STATIC_ENTITLEMENTS_JSON must define at least one feature when OPENGENI_ENTITLEMENTS_MODE=static"
|
|
1764
|
+
);
|
|
1595
1765
|
}
|
|
1596
1766
|
} else {
|
|
1597
1767
|
parseStaticEntitlementsJson(settings.staticEntitlementsJson);
|
|
@@ -1601,7 +1771,9 @@ function validateSettings(settings) {
|
|
|
1601
1771
|
}
|
|
1602
1772
|
if (settings.openaiProvider === "azure") {
|
|
1603
1773
|
if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiEndpoint) {
|
|
1604
|
-
throw new Error(
|
|
1774
|
+
throw new Error(
|
|
1775
|
+
"Azure OpenAI requires OPENGENI_AZURE_OPENAI_BASE_URL or OPENGENI_AZURE_OPENAI_ENDPOINT"
|
|
1776
|
+
);
|
|
1605
1777
|
}
|
|
1606
1778
|
if (!settings.azureOpenaiBaseUrl && !settings.azureOpenaiDeployment) {
|
|
1607
1779
|
throw new Error("Azure OpenAI endpoint mode requires OPENGENI_AZURE_OPENAI_DEPLOYMENT");
|
|
@@ -1614,52 +1786,76 @@ function validateSettings(settings) {
|
|
|
1614
1786
|
}
|
|
1615
1787
|
}
|
|
1616
1788
|
if (Boolean(settings.modalTokenId) !== Boolean(settings.modalTokenSecret)) {
|
|
1617
|
-
throw new Error(
|
|
1789
|
+
throw new Error(
|
|
1790
|
+
"OPENGENI_MODAL_TOKEN_ID and OPENGENI_MODAL_TOKEN_SECRET must both be set or both omitted"
|
|
1791
|
+
);
|
|
1618
1792
|
}
|
|
1619
1793
|
for (const required of SANDBOX_REQUIRED_ENV[settings.sandboxBackend] ?? []) {
|
|
1620
1794
|
const value = settings[required.field];
|
|
1621
1795
|
if (value === void 0 || value === null || typeof value === "string" && value.trim().length === 0) {
|
|
1622
|
-
throw new Error(
|
|
1796
|
+
throw new Error(
|
|
1797
|
+
`${required.env} is required when OPENGENI_SANDBOX_BACKEND=${settings.sandboxBackend}`
|
|
1798
|
+
);
|
|
1623
1799
|
}
|
|
1624
1800
|
}
|
|
1625
1801
|
if (settings.objectStorageBackend === "s3-compatible" || settings.objectStorageBackend === "aws-s3") {
|
|
1626
1802
|
if (Boolean(settings.objectStorageAccessKeyId) !== Boolean(settings.objectStorageSecretAccessKey)) {
|
|
1627
|
-
throw new Error(
|
|
1803
|
+
throw new Error(
|
|
1804
|
+
"OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY must both be set or both omitted"
|
|
1805
|
+
);
|
|
1628
1806
|
}
|
|
1629
1807
|
if (settings.objectStorageBackend === "s3-compatible" && (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint) && (!settings.objectStorageAccessKeyId || !settings.objectStorageSecretAccessKey)) {
|
|
1630
|
-
throw new Error(
|
|
1808
|
+
throw new Error(
|
|
1809
|
+
"S3-compatible object storage endpoints require OPENGENI_OBJECT_STORAGE_ACCESS_KEY_ID and OPENGENI_OBJECT_STORAGE_SECRET_ACCESS_KEY"
|
|
1810
|
+
);
|
|
1631
1811
|
}
|
|
1632
1812
|
if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
|
|
1633
|
-
throw new Error(
|
|
1813
|
+
throw new Error(
|
|
1814
|
+
"S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
|
|
1815
|
+
);
|
|
1634
1816
|
}
|
|
1635
1817
|
if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
|
|
1636
|
-
throw new Error(
|
|
1818
|
+
throw new Error(
|
|
1819
|
+
"S3 object storage uses OPENGENI_OBJECT_STORAGE_* S3 settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
|
|
1820
|
+
);
|
|
1637
1821
|
}
|
|
1638
1822
|
} else if (settings.objectStorageBackend === "azure-blob") {
|
|
1639
1823
|
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
1640
|
-
throw new Error(
|
|
1824
|
+
throw new Error(
|
|
1825
|
+
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not S3-compatible object storage settings"
|
|
1826
|
+
);
|
|
1641
1827
|
}
|
|
1642
1828
|
if (settings.objectStorageGcsProjectId || settings.objectStorageGcsCredentialsJson || settings.objectStorageGcsKeyFilename || settings.objectStorageGcsApiEndpoint) {
|
|
1643
|
-
throw new Error(
|
|
1829
|
+
throw new Error(
|
|
1830
|
+
"Azure Blob storage uses OPENGENI_OBJECT_STORAGE_AZURE_* settings, not OPENGENI_OBJECT_STORAGE_GCS_* settings"
|
|
1831
|
+
);
|
|
1644
1832
|
}
|
|
1645
1833
|
const hasConnectionString = Boolean(settings.objectStorageAzureConnectionString);
|
|
1646
1834
|
const hasSharedKey = Boolean(settings.objectStorageAzureAccountName) && Boolean(settings.objectStorageAzureAccountKey);
|
|
1647
1835
|
if (!hasConnectionString && !hasSharedKey) {
|
|
1648
|
-
throw new Error(
|
|
1836
|
+
throw new Error(
|
|
1837
|
+
"Azure Blob storage requires OPENGENI_OBJECT_STORAGE_AZURE_CONNECTION_STRING or OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_NAME plus OPENGENI_OBJECT_STORAGE_AZURE_ACCOUNT_KEY"
|
|
1838
|
+
);
|
|
1649
1839
|
}
|
|
1650
1840
|
} else {
|
|
1651
1841
|
if (settings.objectStorageEndpoint || settings.objectStorageSandboxEndpoint || settings.objectStorageAccessKeyId || settings.objectStorageSecretAccessKey) {
|
|
1652
|
-
throw new Error(
|
|
1842
|
+
throw new Error(
|
|
1843
|
+
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not S3-compatible object storage settings"
|
|
1844
|
+
);
|
|
1653
1845
|
}
|
|
1654
1846
|
if (settings.objectStorageAzureConnectionString || settings.objectStorageAzureAccountName || settings.objectStorageAzureAccountKey || settings.objectStorageAzureEndpoint) {
|
|
1655
|
-
throw new Error(
|
|
1847
|
+
throw new Error(
|
|
1848
|
+
"GCS object storage uses OPENGENI_OBJECT_STORAGE_GCS_* settings, not OPENGENI_OBJECT_STORAGE_AZURE_* settings"
|
|
1849
|
+
);
|
|
1656
1850
|
}
|
|
1657
1851
|
if (settings.objectStorageGcsCredentialsJson) {
|
|
1658
1852
|
parseGcsCredentialsJson(settings.objectStorageGcsCredentialsJson);
|
|
1659
1853
|
}
|
|
1660
1854
|
}
|
|
1661
1855
|
if (settings.documentChunkOverlap >= settings.documentChunkSize) {
|
|
1662
|
-
throw new Error(
|
|
1856
|
+
throw new Error(
|
|
1857
|
+
"OPENGENI_DOCUMENT_CHUNK_OVERLAP must be smaller than OPENGENI_DOCUMENT_CHUNK_SIZE"
|
|
1858
|
+
);
|
|
1663
1859
|
}
|
|
1664
1860
|
parseExposedPorts(settings.dockerExposedPorts);
|
|
1665
1861
|
sandboxEnvironmentVariableNames(settings);
|
|
@@ -1709,14 +1905,20 @@ function validateSettings(settings) {
|
|
|
1709
1905
|
const providerIds = /* @__PURE__ */ new Set();
|
|
1710
1906
|
for (const provider of registryProviders) {
|
|
1711
1907
|
if (provider.id === builtinId) {
|
|
1712
|
-
throw new Error(
|
|
1908
|
+
throw new Error(
|
|
1909
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider id ${provider.id} collides with the built-in provider id`
|
|
1910
|
+
);
|
|
1713
1911
|
}
|
|
1714
1912
|
if (providerIds.has(provider.id)) {
|
|
1715
|
-
throw new Error(
|
|
1913
|
+
throw new Error(
|
|
1914
|
+
`OPENGENI_MODEL_PROVIDERS_JSON contains duplicate provider id ${provider.id}`
|
|
1915
|
+
);
|
|
1716
1916
|
}
|
|
1717
1917
|
providerIds.add(provider.id);
|
|
1718
1918
|
if (!resolveProviderApiKey(provider)) {
|
|
1719
|
-
throw new Error(
|
|
1919
|
+
throw new Error(
|
|
1920
|
+
`OPENGENI_MODEL_PROVIDERS_JSON provider ${provider.id} requires a resolvable API key (set apiKey or apiKeyEnv)`
|
|
1921
|
+
);
|
|
1720
1922
|
}
|
|
1721
1923
|
}
|
|
1722
1924
|
}
|
|
@@ -1794,7 +1996,9 @@ function parseGcsCredentialsJson(raw) {
|
|
|
1794
1996
|
return JSON.parse(raw);
|
|
1795
1997
|
} catch (error) {
|
|
1796
1998
|
const message = error instanceof Error ? error.message : String(error);
|
|
1797
|
-
throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}
|
|
1999
|
+
throw new Error(`OPENGENI_OBJECT_STORAGE_GCS_CREDENTIALS_JSON must be valid JSON: ${message}`, {
|
|
2000
|
+
cause: error
|
|
2001
|
+
});
|
|
1798
2002
|
}
|
|
1799
2003
|
}
|
|
1800
2004
|
function delay(ms) {
|
|
@@ -1809,6 +2013,7 @@ export {
|
|
|
1809
2013
|
RegistryProviderKind,
|
|
1810
2014
|
SANDBOX_REQUIRED_ENV,
|
|
1811
2015
|
applyGitAuthPointerEnvironment,
|
|
2016
|
+
builtinProviderId,
|
|
1812
2017
|
calculateModelUsageCostMicros,
|
|
1813
2018
|
collectGitIdentityEnvironment,
|
|
1814
2019
|
collectSandboxEnvironment,
|
|
@@ -1820,7 +2025,6 @@ export {
|
|
|
1820
2025
|
configuredProviders,
|
|
1821
2026
|
configuredStaticUsageLimits,
|
|
1822
2027
|
contextInputBudgetTokens,
|
|
1823
|
-
contextServerCompactThreshold,
|
|
1824
2028
|
dbSearchPath,
|
|
1825
2029
|
defaultModelPricing,
|
|
1826
2030
|
effectiveModalIdleTimeoutSeconds,
|
|
@@ -1828,6 +2032,7 @@ export {
|
|
|
1828
2032
|
firstPartyMcpBaseUrl,
|
|
1829
2033
|
firstPartyMcpWorkspaceUrl,
|
|
1830
2034
|
getSettings,
|
|
2035
|
+
hasGitCredentialRepositorySelection,
|
|
1831
2036
|
hasGitHubRepositorySelection,
|
|
1832
2037
|
parseExposedPorts,
|
|
1833
2038
|
parseIntegrationsOauthClientsJson,
|
|
@@ -1837,8 +2042,8 @@ export {
|
|
|
1837
2042
|
parseSandboxWarmRateJson,
|
|
1838
2043
|
parseStaticEntitlementsJson,
|
|
1839
2044
|
parseStaticUsageLimitsJson,
|
|
2045
|
+
policyProviderIdForModel,
|
|
1840
2046
|
requiredSandboxEnvForBackend,
|
|
1841
|
-
resolveContextCompactionMode,
|
|
1842
2047
|
resolveEnrollmentSigningSecret,
|
|
1843
2048
|
resolveModelProvider,
|
|
1844
2049
|
resolveNatsCalloutConfig,
|
|
@@ -1851,6 +2056,7 @@ export {
|
|
|
1851
2056
|
sandboxLifecycleHookIds,
|
|
1852
2057
|
sandboxPreparationProfiles,
|
|
1853
2058
|
sandboxWarmRateMicrosPerSecond,
|
|
2059
|
+
settingsWithResolvedModelContext,
|
|
1854
2060
|
stableSandboxEnvironmentForRun,
|
|
1855
2061
|
startupRetryOptions,
|
|
1856
2062
|
streamTokenDegraded
|